Smart contract upgrade patterns without losing trust

Smart contract upgrade patterns without losing trust
By Marco Lopes · September 17, 2025 · Blockchain & Web3
Smart contracts promise predictable execution. Software development promises change. When we put those ideas together, we get a difficult question: how do we fix bugs, add capabilities and respond to new requirements without asking users to trust an unlimited administrator?
At Kordspace, we approach upgradeability as an architecture and governance problem, not just a Solidity pattern. A technically correct proxy can still expose users to arbitrary changes. A carefully governed system can still fail because an upgrade corrupts storage.
The goal is not to make every contract upgradeable. It is to make the change surface explicit, constrain who controls it and give affected users a credible way to understand and respond. Here is how we think about that trade-off when designing a Web3 product.
Start by deciding what should never change
Before choosing a proxy, we identify the commitments the product makes. Can balances be frozen? Can fees change? Can an asset’s transfer rules be replaced? Can governance alter withdrawal conditions?
Those questions matter more to users than the name of the upgrade standard. If an administrator can replace the code controlling deposited assets, users are trusting that administrator’s future decisions, even when today’s implementation is audited.
We usually separate a system into three categories:
- Fixed commitments: small components whose behavior should remain immutable.
- Bounded configuration: parameters that can change within explicit limits.
- Evolving functionality: modules that genuinely need implementation upgrades.
This separation reduces the amount of authority an upgrade mechanism needs. However, a limit enforced only inside replaceable code is not a durable limit: the next implementation could remove it. Strong guarantees must live outside the administrator’s replacement authority.
Choose the simplest pattern that fits
For Ethereum and other EVM-compatible networks, several upgrade patterns are established. We choose based on operational needs, rather than assuming that the most flexible design is the best one.
Transparent proxies
A transparent proxy separates administrative calls from ordinary application calls. Users interact with the proxy address, while the proxy delegates execution to an implementation. Upgrade authority commonly sits behind a dedicated ProxyAdmin contract.
This makes the administrative boundary relatively easy to explain and inspect. The exact deployment and ownership workflow depends on the library version, so we pin dependencies and review that version’s behavior rather than copying an old tutorial.
We consider this pattern when straightforward administration and widely supported tooling are priorities.
UUPS proxies
With UUPS, the upgrade mechanism lives in the implementation rather than in a larger administrative proxy. This can reduce proxy deployment overhead, but it makes implementation correctness especially important.
We verify the upgrade authorization function, implementation compatibility checks and the ability to perform future upgrades. A replacement that accidentally removes or breaks upgrade functionality can strand the proxy. We also avoid casually mixing transparent proxies and UUPS implementations: their combined authorization paths can produce unexpected upgrade access.
UUPS is a good fit when we want a lean proxy and have disciplined implementation review.
Beacon proxies
A beacon supplies an implementation address to multiple proxies. Updating it can change the behavior of an entire fleet, which is useful for standardized instances.
The convenience comes with a large blast radius. We treat beacon upgrades as fleet-wide releases, testing representative storage states and avoiding the assumption that every instance has identical history.
If we need staged adoption, we design separate beacon groups or another explicit rollout mechanism. A single shared beacon does not provide a canary deployment by itself.
Diamonds and modular routing
The diamond pattern, described in EIP-2535, routes function selectors to separate implementation modules called facets. It can support large, modular systems, but adds routing, storage and governance complexity.
We use it only when modularity solves a real requirement. Facet replacement and selector changes deserve the same scrutiny as a whole-contract upgrade; splitting code into modules does not automatically split administrative risk.
Consider migration instead of in-place upgrades
Sometimes the cleanest upgrade is a new contract deployment.
We can publish a new version, explain its differences and let users migrate deliberately. The old contract remains available under its original rules, subject to its existing dependencies and permissions.
This can preserve meaningful choice, but migration has costs: fragmented liquidity, duplicated integrations, additional transactions and users who never move. Positions may not be transferable without unwinding them.
We therefore compare migration against proxy upgrades early. For a small, durable primitive, immutability plus voluntary migration may be preferable. For a product with complex shared state, a constrained upgrade process may be more workable.
Neither approach deserves the label “trustless” merely because it uses a particular deployment technique.
Treat storage as a permanent interface
Proxy implementations execute against the proxy’s storage. That makes storage layout a compatibility boundary, not an internal refactoring detail.
Reordering variables, changing their types or altering inheritance can cause new code to read old data incorrectly. The contract may still compile and deploy successfully while balances, roles or accounting values become meaningless.
We keep a machine-checkable record of storage layouts and validate each proposed upgrade against the deployed version. With conventional layouts, we avoid moving or removing existing fields and add new fields only where compatibility rules permit.
Namespaced storage, including the approach standardized in ERC-7201, can make modular layouts easier to manage. It does not make arbitrary changes inside a namespace safe. Reserved storage gaps likewise require careful accounting; they are not permission to ignore layout validation.
We also review what is not proxy storage. Constructors do not initialize the proxy’s state, and immutable values belong to the implementation’s deployed code. Confusing those models can produce behavior that tests against the implementation alone will miss.
Make initialization part of the security boundary
An uninitialized proxy can become an ownership takeover opportunity. We prefer deployment flows that initialize the proxy atomically, leaving no public window between creation and setup.
Implementation contracts should also be protected against unintended initialization, using the safeguards appropriate to the selected library. Locking an implementation does not initialize its proxies; these are separate responsibilities.
For later releases, we use explicitly versioned initialization steps when new state needs setup. We verify who can call them, whether they can run twice and what happens if a prerequisite is missing.
Where supported, an upgrade-and-call operation can combine implementation replacement with initialization. We test the whole transaction, including failure paths, rather than treating initialization as a manual task to remember after deployment.
Constrain upgrade authority visibly
A proxy is only as trustworthy as its control path. We map that path from the implementation-changing function through every administrator, role, multisignature wallet and governance contract that can reach it.
A multisignature wallet reduces dependence on one key, but it does not automatically create independent decision-making. We look at signer separation, key custody, replacement procedures and whether the same people control every approval layer.
For planned upgrades, a timelock can provide a public review window. Its value depends on what users can do during that window. If withdrawals take longer than the delay, or an administrator can pause exits immediately, “time to exit” may be an empty promise.
We also inspect bypasses. Can another role upgrade directly? Can governance shorten the delay without a comparable delay? Can an emergency mechanism replace arbitrary code?
We describe these powers plainly in product documentation. “Upgradeable by a multisignature after a public delay” is more useful than a broad claim of decentralization.
Design emergency controls without hiding a back door
Emergency response creates a genuine tension: waiting can increase losses, while instant upgrades increase administrator power.
We prefer narrowly scoped emergency actions where practical. A guardian might pause new deposits or disable a vulnerable operation without gaining the ability to rewrite every rule. Whether withdrawals remain available depends on the exploit and accounting model; keeping an unsafe withdrawal path open is not inherently protective.
We document activation conditions, authorized actors, recovery steps and any expiration mechanism. If immediate arbitrary upgrades are necessary, we say so. We do not market that architecture as having guarantees it cannot enforce.
Crucially, pause authority, upgrade authority and treasury authority need not belong to the same role.
Rehearse the upgrade against realistic state
Unit tests for the new implementation are necessary, but insufficient. We need evidence that the transition preserves the system’s promises.
Our release process includes storage compatibility checks, authorization tests and upgrade rehearsals on a fork or another representative environment. We exercise existing positions, accrued fees, permission assignments, paused states and edge cases from earlier releases.
We define invariants before testing: assets remain accounted for, unauthorized callers cannot upgrade, existing claims retain their intended meaning and configuration stays within its enforceable bounds.
We then simulate the exact governance or administrator transaction sequence. That includes proposal scheduling, execution, initialization and post-upgrade checks. External reviews should cover the upgrade mechanism and migration logic, not only the new business feature.
A fork is useful evidence, not a prediction of the future. Live state can change between rehearsal and execution, so we examine those assumptions explicitly.
Publish the release and monitor the result
Before execution, we publish the proposed implementation address, verified source, intended transaction data, behavioral changes and known risks. We make those artifacts easy to compare with what governance actually approves.
After execution, we check implementation pointers, relevant events, roles, initialization state and critical accounting properties. We monitor more than transaction success: a successful receipt does not prove a safe upgrade.
For supporting infrastructure, we generally prefer Google Cloud for release automation, logging and alerting, while accommodating AWS or Azure where appropriate. Cloud access controls protect those supporting systems; they do not constrain an on-chain administrator by themselves.
We plan recovery without assuming rollback is safe. Reinstalling old code may not reverse transformed storage or external effects. The recovery plan might instead require a forward fix, a restricted pause or an explicit migration.
A short pre-upgrade checklist
- Have we explained what can change and who can authorize it?
- Have we checked every control path, including emergency bypasses?
- Are storage compatibility and initialization validated?
- Have we rehearsed the exact transaction sequence against representative state?
- Can users review the change and exercise any promised exit rights?
- Are deployment artifacts, monitoring and recovery procedures ready?
Trust survives upgrades when authority is understandable, changes are reviewable and execution is disciplined. At Kordspace, we bring software engineering, Web3 architecture, hardware, AI and design perspectives into remote collaboration, while keeping each project’s trust boundaries explicit. If we can help shape your next protocol or product, start a project, or explore our related services to see how we work.
Conversation (0)
No comments yet. Start the conversation.
Join the conversation
Create a free member account to comment.