OpenZeppelin upgradeable contracts: the initializer pattern and the storage rule
Upgradeable variant of OpenZeppelin Contracts, meant for use in upgradeable contracts.
At a glance
- What is it?
- The upgradeable variant of OpenZeppelin Contracts ships the same community-audited components with initializer functions and no constructors, and it carries one warning that matters more than any feature: because storage layout is part of the contract's identity, a major version upgrade is assumed to be unsafe unless you migrate the layout yourself.
- Who is it for?
- Adopt the upgradeable variant when your contract genuinely has to be replaced after deployment, and pin the exact version you deployed, because the storage layout rule means a major version bump is a migration rather than an upgrade. Do not adopt it for a contract that will never change, since the initializer pattern exists only to serve upgrades and adds a step every deployer has to remember.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The same components, with constructors replaced
This repository is the upgradeable variant of OpenZeppelin Contracts, meant for use in upgradeable contracts, and the README flags that immediately so that nobody installs it by mistake.
The library itself is described as a library for secure smart contract development, a solid foundation of community-vetted code. You get implementations of standards such as ERC20 and ERC721, a flexible role-based permissioning scheme, and reusable Solidity components for custom contracts and more complex decentralised systems. The documentation covers access control, tokens including ERC-20, ERC-721, ERC-1155 and ERC-6909, and utilities such as non-overflowing math, signature verification and trustless paying systems.
What makes this variant different is the shape of the deployment. The usage example shows it plainly:
pragma solidity ^0.8.20;
import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
contract MyCollectible is ERC721Upgradeable {
function initialize() initializer public {
__ERC721_init("MyCollectible", "MCO");
}
}The constructor arguments of the non-upgradeable ERC721 become the arguments of an initializer function, and the initializer modifier replaces the constructor's one-time semantics. The base contract is deployed once and the storage is filled in afterwards, which is what makes the whole contract replaceable later.
The README also gives the advice that matters for a library people tend to fork: always use the installed code as-is, and neither copy-paste it from online sources nor modify it yourself. The library is designed so that only the contracts and functions you actually use are deployed, so there is no gas cost to worry about for the parts you leave alone.
The storage layout rule, which is the real constraint
The important note in the README is about versioning, and it is stated as an important warning rather than a footnote.
OpenZeppelin Contracts uses semantic versioning to communicate backwards compatibility of its API and storage layout. And then the key sentence: for upgradeable contracts, the storage layout of different major versions should be assumed incompatible, for example it is unsafe to upgrade from 4.9.3 to 5.0.0.
That changes what an upgrade means. In an ordinary contract, a major version bump is a breaking API change that you fix in your own code and redeploy. In an upgradeable contract, the deployed instance has a storage layout, and the new implementation reads that storage by slot. If the layout changed, the new code reads the old slots as if they were the new ones, and the failure is silent: values appear in the wrong variables, and nothing throws.
So a major version upgrade is a migration, not an upgrade. You cannot just point the proxy at a new implementation and expect the existing state to line up. Either the layout is preserved, which is what the version guarantee buys you within a major line, or you write a migration that moves the data into the new slots, which is a piece of engineering on a contract holding real value.
The practical advice that follows is to pin the version you deployed, treat a major bump as a project, and read the backwards compatibility documentation before assuming an upgrade is free.
Three npm tags, and what audited actually means
OpenZeppelin uses npm tags to distinguish audited releases from unaudited ones, and the distinction is not the usual alpha, beta, release one.
The latest tag is for audited releases. These are the stable, audited versions of the package, and it is the default version installed when users run npm install @openzeppelin/contracts. That default is the important detail, because a package manager will not ask you which you meant.
The dev tag is for versions that are final but not audited. They are finalized and feature-complete but have not yet been audited, and the README says this version is fully tested, can be used in production and is covered by the bug bounty.
The next tag is for release candidates, which are not final. Those exist for testing and validation before a version becomes a final dev or latest release.
So there are three states of a release rather than two, and the middle one is unusual: a dev release is finished work that has not been through the audit process, and it is a legitimate production choice if you accept the residual risk. The presence of a release candidate in the tags of this repository, v5.7.0-rc.0 alongside v5.7.0, shows the sequence in practice.
Installing with npm, or with git for Foundry
There are two documented routes, and the Foundry one carries a warning worth reading twice.
For Hardhat and npm, the audited release is one command:
npm install @openzeppelin/contracts-upgradeableand the unaudited dev release is the same with the tag appended, npm install @openzeppelin/contracts-upgradeable@dev.
For Foundry, the package is installed from git:
forge install OpenZeppelin/openzeppelin-contracts-upgradeableand then the remapping goes in remappings.txt:
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/The warnings are about the branch, not the command. When installing via git, it is a common error to use the master branch, which is a development branch that should be avoided in favour of tagged releases, because the release process involves security measures that the master branch does not guarantee.
The second warning is subtler and will bite anyone who upgrades dependencies later: Foundry installs the latest version initially, but subsequent forge update commands will use the master branch. So a project that starts from a tag and then runs forge update ends up on master, in a state the README says you should not run in production.
That is the sharpest operational edge in this README, and it is the kind of thing that deserves a line in your own project's deployment notes.
The generated, checked codebase behind the library
The package metadata shows how much of this library is produced and verified rather than written by hand.
The package is named openzeppelin-solidity, at version 5.7.0, and it is marked private, with a files list that ships the contracts and explicitly excludes the mocks. The scripts describe the pipeline. Compiling is hardhat build, with a separate harnesses compile for the formal verification targets. There is a generate script that runs scripts/generate/run.js, and a pragma script that rebuilds and then minimises the pragma across the build info files.
And then the checks, which are the interesting part. test:generation runs scripts/checks/generation.sh, so the generated contracts are verified against their sources. test:inheritance rebuilds and runs scripts/checks/inheritance-ordering.js over the build output, which matters because inheritance order determines storage layout and therefore the upgradeability guarantee. test:pragma rebuilds and runs scripts/checks/pragma-validity.js.
The inheritance-ordering check is the one that explains the rest. The whole reason the storage layout can be trusted within a major version is that a check enforces the order in which base contracts are laid out, so a refactor cannot silently reshuffle the slots.
Around that there is ordinary but thorough tooling: linting with prettier and eslint for the JavaScript and prettier and solhint for the Solidity, a docs script using oz-docs, a clean target, husky for the commit hooks, and a version script under scripts/release. The repository also carries a foundry.toml, hardhat.config.ts, slither.config.json, a .changeset directory, renovate.json for dependency updates, an fv/ directory with its own requirements file for formal verification, and an audits/ directory.
Where the upgradeable variant is the wrong choice
The first wrong-tool case is a contract that will never be replaced. The initializer pattern, the storage layout concerns and the extra deployment step all exist to serve upgrades, and a contract deployed once and never touched gains nothing from them but inherits the risk of a wrong initializer call, since an initializer that is callable twice is a known failure mode in this pattern.
The second is a project that expects upgrades to be routine. The README's own rule is that a major version change is unsafe, and the whole value of a pinned, audited release is that it does not change under you. A team whose plan is to ship a new implementation every month will spend its time on storage migrations rather than on features, and the tooling for those migrations is not in this repository.
The third is a team that wants to fork the code. The README is explicit that you should always use the installed code as-is and not modify it yourself, and for a good reason. The moment you change the layout, the version guarantee no longer applies to your contract, and the storage-layout compatibility you were relying on disappears without any compiler or test telling you.
There is also a licensing and support dimension for the unusual case. The code is MIT, which is permissive, but the audits, the security policy and the bug bounty coverage attach to the upstream releases, and a modified copy is outside them.
MIT, version 5.7.0, and a project that ships a whole governance apparatus
The library is MIT licensed, with the LICENSE file at the repository root, and the documentation site is at openzeppelin.com/contracts with the guides at docs.openzeppelin.com/contracts.
The release history is the familiar one of a widely used dependency. The versions are v5.7.0 on 2026-07-29, v5.7.0-rc.0 from two weeks earlier, and v5.6.1 from February 2026, so a release candidate is followed by a final within a fortnight, and patch releases come out of the same line. The last push was on 2026-10-15, so the repository is worked on between releases, and the CHANGELOG.md at the root is the history.
What the repository contains beyond the contracts is the more interesting story. There is a GUIDELINES.md, a RELEASING.md, a SECURITY.md, a CODE_OF_CONDUCT.md, a CONTRIBUTING.md, an audits/ directory, a FUNDING.json, and a docs/ directory. A library that other people put money into and put on-chain needs the process around it to be as careful as the code, and the same goes for a project whose releases carry the word audited in their npm tag.
The practical advice for anyone using it is short. Install the audited tag, pin the version, do not modify the code, and treat a major version change as a migration with a written plan rather than a dependency bump.
Editorial conclusion
Adopt the upgradeable variant when your contract genuinely has to be replaced after deployment, and pin the exact version you deployed, because the storage layout rule means a major version bump is a migration rather than an upgrade. Do not adopt it for a contract that will never change, since the initializer pattern exists only to serve upgrades and adds a step every deployer has to remember. Before you deploy, read the documentation on using OpenZeppelin Contracts with upgrades, install the audited tag rather than dev or next, and make sure the initializer cannot be called a second time.
Frequently asked questions
How do I install the OpenZeppelin upgradeable contracts?
With npm and Hardhat, run npm install @openzeppelin/contracts-upgradeable for the latest audited release, or append @dev for the latest unaudited one. With Foundry, run forge install OpenZeppelin/openzeppelin-contracts-upgradeable and add the remapping @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ to remappings.txt.
Is it safe to upgrade an upgradeable contract to a new major version?
No. The README states that for upgradeable contracts the storage layout of different major versions should be assumed incompatible, and gives upgrading from 4.9.3 to 5.0.0 as an example of something unsafe. A major version change is a storage migration rather than a drop-in upgrade.
What do the latest, dev and next npm tags mean?
latest is the audited release and is the default installed by npm install. dev is final and feature-complete but not yet audited, is fully tested, can be used in production and is covered by the bug bounty. next holds pre-release release candidates that are not final.
What is the initializer function in the OpenZeppelin upgradeable contracts?
It replaces the constructor. The example contract defines function initialize() initializer public and calls __ERC721_init("MyCollectible", "MCO") in the body, so the base contract state is set after deployment rather than at it.
What licence is the OpenZeppelin upgradeable contracts released under?
MIT, with the LICENSE file at the repository root. The most recent release is v5.7.0, dated 2026-07-29, and the last push was on 2026-10-15.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/openzeppelin-openzeppelin-contracts-upgradeable)Was this useful?