code-freeze
Code Freeze vs Change Freeze vs Deployment Freeze
Code freeze, change freeze, and deployment freeze each stop something different. Here is how to tell them apart and choose the right one for your situation.
Code freeze, change freeze, and deployment freeze are used interchangeably in most engineering conversations, but they describe different scopes of restriction. Using the wrong term creates real ambiguity about what is and is not allowed during a controlled window. This guide pins down each definition and maps it to what actually gets blocked.
What does each term actually mean?
Code freeze stops merges into protected branches. Typically it also stops deployments from those branches, but the primary lever is the merge gate. Work can continue in feature branches; it just cannot land on main (or whatever branch feeds production). Most holiday freezes and launch-window freezes are code freezes.
Change freeze is broader. It stops any production change, including infrastructure changes, configuration updates, feature flag flips, and database schema changes. A code freeze is a subset of a change freeze. A fully compliant change freeze means no one pushes a Terraform plan, no one tweaks a feature flag, no one rotates a secret, no one restarts a service.
Deployment freeze is narrower. It stops code from shipping to an environment, but merging continues. Work lands on main and queues up behind the freeze. When the freeze lifts, everything that accumulated can ship. This is the right model for a release train: development keeps moving, but releases happen on a fixed cadence.
Release freeze stops the act of cutting or distributing a new version. In practice this applies mainly to projects with explicit versioned releases (open-source libraries, mobile apps). Continuous-deployment web services rarely call it a "release freeze" because there is no discrete release artifact.
Comparison table
| Term | What it stops | What can continue | Typical use |
|---|---|---|---|
| Code freeze | Merges into protected branches | Work in feature branches, non-production deploys | Holiday windows, pre-launch stability |
| Change freeze | All production changes (code, config, infra, flags) | Local development, staging | Compliance audits, SOC 2 evidence windows |
| Deployment freeze | Deploys to one or more environments | Merges to main, all development | Release trains, staged rollouts |
| Release freeze | Cutting or shipping new versions | All development, internal deploys | Stabilizing a release candidate |
Which one do you actually need?
The right answer depends on two questions: what is the risk you are protecting against, and what would count as a violation?
Protecting against a bad deploy during a high-traffic window: a deployment freeze is the minimal correct choice. If you are also worried about a bad merge accumulating silently during the window, add a code freeze on top.
Protecting against any production change for an audit or compliance window: a change freeze covers the full surface. A code freeze alone would miss the SRE who rotates a secret or the product manager who flips a flag in LaunchDarkly.
Stabilizing a release branch while development keeps flowing: a code freeze on the release branch only (not on main) or a release freeze preserves momentum without stopping the team. Development continues; the candidate does not change.
Incident response: a code freeze and deployment freeze together keep new variables out while responders work. A full change freeze is often overkill unless the incident is infrastructure-related, where a config change or Terraform drift is the suspected cause.
Where teams get into trouble
The most common mistake is calling a "code freeze" when you actually mean a change freeze. You announce no merges, the engineers comply, and then someone deploys an updated nginx config from a Terraform run. That is a production change that the code freeze did not capture, and if it triggers an incident, the post-incident conversation turns into an argument about what the freeze was supposed to cover.
The other common mistake is treating these terms as self-explanatory. A code freeze at one company means "no merges to main." At another it means "nothing touches production, period." Neither is wrong as a policy, but they are different policies. When you declare a freeze, write down explicitly what is stopped, what is allowed through, and who has override authority.
How these map to GitHub enforcement
GitHub does not have a native concept of any of these freeze types. It has branch protection rules (which block merges) and environment protection rules (which block deployments). Those primitives map roughly as follows:
| Freeze type | GitHub primitive | Where it's configured |
|---|---|---|
| Code freeze | Required status check on a branch protection rule | Branch settings, per-repository |
| Deployment freeze | Deployment protection rule or required reviewers | Environment settings, per-repository |
| Change freeze | No single native primitive | Infra pipeline controls, flag system, branch + deploy |
| Release freeze | Tag protection rules + release branch protection | Repository settings |
A change freeze is the hardest to automate because it spans multiple systems. GitHub controls code and deployments; it does not control Terraform, LaunchDarkly, or direct database access. Enforcing a real change freeze requires process controls in each of those systems separately.
For code freezes and deployment freezes, the native GitHub tools work, but they require manual toggling per repository. If you have more than a handful of repos or run freezes on a schedule, the manual overhead adds up quickly. The GitHub deployment freeze guide covers the two-control model (required status checks + deployment protection rules) in detail, and the core concepts guide covers how freeze windows and scoping work at the platform level.
What NoShip handles
NoShip is a GitHub App that automates code freezes and deployment freezes within GitHub's enforcement model: required status checks for merges and GitHub deployment protection rules for releases. It handles scheduling, multi-repo scoping, recurring windows (RRULE), emergency overrides behind dual approval, and an audit trail for every action. There is no access to your source code.
What NoShip does not do is enforce the broader change freeze surface across Terraform, feature flag systems, or direct infrastructure access. That part still requires process controls outside GitHub. Knowing that distinction matters when you are writing your change control policy: NoShip covers the code and deploy surfaces within GitHub well; the rest of the estate needs its own controls in the systems that own it.
Naming the freeze precisely is part of the policy
A freeze policy that uses "code freeze" to mean everything creates ambiguity the first time something goes wrong. The cleaner approach: be specific. State exactly what is stopped, what is allowed through, what requires approval, and who can override. That specificity is the difference between a freeze that actually controls production and one that just creates the appearance of control.
The what is a code freeze guide covers the foundations of freeze design in more depth if you are starting from scratch.
Keep reading
How to Freeze Deployments in GitHub (Step-by-Step)
Freezing deployments in GitHub requires two controls: required status checks to block merges and deployment protection rules to block releases. Here is how to set up both.
Merge Freeze Alternatives for GitHub Teams
The best Merge Freeze alternatives for GitHub, compared by enforcement depth, scheduling, override workflows, and migration path from Merge Freeze.
Best Code Freeze Tools for GitHub (2026)
Compare the four main approaches to code freezes on GitHub: branch protection, rulesets, Merge Freeze, and NoShip. Find the right fit for your team.