There is such a thing as a "proxy" contract. The contract can delegate to another "delegate" contract that actually performs the functions you want. The proxy contract also has a function that allows the owner to change the address of the delegate contract. If you tell you users to interact with the proxy contract then they will always interact with the latest version of the delegate contract.
Some ERC20 tokens implement this but I believe many do not.
https://docs.openzeppelin.com/contracts/3.x/api/proxy