Instead of inheriting shared logic, Vyper contracts compose by calling out to other deployed contracts through interfaces, or by copy-adapting a well-reviewed pattern into each contract explicitly. This trades some code duplication for the guarantee that every contract's logic is fully visible in its own file.
interface Registry:
def is_allowed(user: address) -> bool: view
registry: public(address)
@external
def restricted_action():
assert Registry(self.registry).is_allowed(msg.sender), "not allowed"This pattern also naturally supports upgradeable 'policy' contracts: swap the registry address and behaviour changes without touching the calling contract's code.