A common way is to include a unique ID which never changes (for users and posts, in your case), and then 301-redirect to the current/canonical variant.
Example
This is what, for example, Stack Overflow is doing.
If you change your question’s title, the ID (36463097
) will stay and the slug will change:
https://stackoverflow.com/questions/36463097/handling-urls-that-change
https://stackoverflow.com/questions/36463097/handling-urls-that-might-change
If you change your user name, the ID (2961662
) will stay and the slug will change:
https://stackoverflow.com/users/2961662/psidhu
https://stackoverflow.com/users/2961662/john-doe
This also allows to have users with the same user name (in case you want to allow that):
http://example.com/23/john-doe
http://example.com/42/john-doe
Drawbacks
It’s not the best URL design:
/users/2961662/psidhu
is not as beautiful as /users/psidhu
- remembering a URL with such a cryptic ID is hard(er)
- such an ID is not meaningful, so ideally it wouldn’t be part of the URL
Alternative
Track all changes and block user names that were once registered. So If I start as john
and then change my name to john-doe
, no one else may register john
.
This is good practice for email addresses, and in my opinion it should also be good practice for every service that allows direct communication with users. If users get messages intended for the previous owner of that user name, that’s a serious problem; if users don’t get their favorite user name because someone else registered it before, it’s, at most, annoying.
So for each user you could track previous user names, and for each post previous titles, and then 301-redirect to the current/canonical one.