Most upgradeability patterns solve one problem by moving it somewhere else. You split logic from storage, gain the ability to redeploy implementations, and tell yourself the system is now flexible. But once a protocol grows beyond its first clean version, that flexibility can start to feel thin. The contract grows, features accumulate, and the architecture that once felt elegant starts to feel like duct tape with nicer branding.

That is why EIP-2535, the Diamond standard introduced by Nick Mudge, feels meaningfully different. It does not just make a monolith upgradeable. It changes the unit of change. Instead of routing calls to one implementation contract, the Diamond routes function selectors to many implementation contracts called facets. That shift sounds small until you realize what it unlocks.

Why simpler proxy patterns eventually feel cramped

The EVM still imposes a contract size limit, and that limit stops feeling theoretical once a protocol grows past the scope of its original design. Transparent proxies and UUPS help, but they do not actually eliminate the monolith problem. They separate proxy from implementation, yet the implementation itself can still become too large, too tangled, or too risky to keep redeploying as one unit.

At the call level, the Diamond fallback resolves a selector to the right facet and delegates execution there.
address facet = selectorToFacet[msg.sig];
require(facet != address(0));
assembly {
  calldatacopy(0, 0, calldatasize())
  let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
}

Diamonds address that by treating the protocol as a set of modular execution surfaces. The fallback function looks up the incoming selector, finds the facet responsible for that function, and delegatecalls into it. Now upgrades can be surgical. If one function or one module needs to change, you swap that slice instead of replacing the entire implementation.

Why the architecture is powerful and dangerous at the same time

The power comes with risk because all facets share the same underlying storage. That means a sloppy facet is not isolated by default. If two modules write to overlapping slots, you do not necessarily get a clean revert. You get state corruption, quietly. This is where many first impressions of Diamonds go wrong: they look infinitely flexible, so people forget that shared storage makes discipline mandatory, not optional.

Diamond Storage gives each module a deterministic place to anchor its struct and avoid accidental slot collisions.
bytes32 constant POSITION = keccak256("myapp.storage.token");
function ds() internal pure returns (DiamondStorage storage s) {
  bytes32 pos = POSITION;
  assembly { s.slot := pos }
}

Diamond Storage is what makes this practical. Instead of relying on fragile inheritance layouts, each module can anchor its own struct to a deterministic storage position derived from a unique hash. That gives teams a pattern for organizing state explicitly across facets in a way that is easier to reason about and harder to corrupt by accident.

The real advantage is operational, not just academic

diamondCut lets you add, replace, or remove selectors without redeploying the rest of the protocol.
function diamondCut(
  FacetCut[] calldata _diamondCut,
  address _init,
  bytes calldata _calldata
) external;

Upgrades happen through diamondCut, where the protocol can add, replace, or remove specific function selectors and map them to the appropriate facet addresses. That changes the operational feel of upgrades completely. A bug in one function no longer implies redeploying an entire implementation. A new feature does not have to drag every untouched module through the release pipeline. A protocol can evolve in smaller, more legible increments.

That is the moment where Diamonds stop looking like an academic pattern and start looking like infrastructure. If you are building something expected to outlive its first architecture, the ability to patch exactly what broke and leave the rest untouched is not just convenient. It changes how maintainable the system feels over time.

Where Diamonds actually make sense

This is why Diamonds show up in places like Aavegotchi, modular governance systems, onchain games, and protocols that expect long lifetimes with evolving requirements. The pattern is much less compelling for a small product with a narrow scope and short shelf life. But for systems that need to keep growing without repeatedly rebuilding the whole plane mid-flight, the tradeoff starts to make sense.

The tradeoffs are real, though, and pretending otherwise is how teams misuse the pattern. Auditing becomes harder because logic is distributed across facets. Tooling support is improving but still not as frictionless as simpler patterns. Debugging across modules requires discipline. And the biggest operational warning is obvious: diamondCut is a god key. If you do not protect it with a multisig, timelock, and strong upgrade process, the flexibility of the pattern becomes its own attack surface.

If you are building something small, short-lived, or unlikely to change much, UUPS is often enough. But if you are building infrastructure meant to evolve, scale, and survive architectural drift, Diamonds deserve serious attention. They do not eliminate complexity. They make complexity more modular, which is sometimes the only kind of control large systems ever really get.