A deployed Vyper contract's logic can never change in place. To 'upgrade', teams deploy a lightweight proxy contract that `delegatecall`s into a separate, replaceable logic contract — though Vyper itself intentionally has no `delegatecall` builtin at the language level for regular use, so proxies are typically minimal, heavily audited pieces (e.g. EIP-1167 minimal proxies) rather than something you hand-roll casually.
# create_minimal_proxy_to deploys an EIP-1167 clone pointing at `target`
@external
def clone(target: address) -> address:
return create_minimal_proxy_to(target)Because upgrade proxies concentrate enormous power (the ability to change all logic), any upgrade path should have a timelock and, ideally, a way for the community/users to see pending changes before they take effect.