- Use OpenZeppelin's `TransparentUpgradeableProxy` or `UUPSUpgradeable` for upgradeable contracts — never use `selfdestruct` as a migration strategy.
- Separate storage from logic using the diamond storage pattern (`DiamondStorage`) or dedicated storage contracts to avoid storage collisions across upgrades.
- Use the factory pattern (`ContractFactory.deploy()`) to create and register new contract instances rather than hardcoding addresses.
- Apply the Facade pattern: expose a single entry-point contract that delegates to specialized sub-contracts to keep the public API stable across refactors.
- Use `initialize()` functions with `initializer` modifier (OpenZeppelin `Initializable`) instead of constructors for upgradeable contracts.
- Separate token logic (`ERC20`), access control (`AccessControl`), and business logic into distinct contracts composed via inheritance or delegation.
- Use the Registry pattern to manage contract addresses on-chain so dependent contracts can discover each other without redeployment.