code-freeze
Code Freeze Best Practices: A Checklist for Release Managers
Code freeze best practices: scope, duration, automated enforcement, a real override path, and thaw planning. A practical checklist for release managers.
Code freeze best practices come down to six things: narrow scope, defined duration, automated enforcement, a structured override path, advance communication, and a planned thaw. Freezes that fail usually miss at least two of these. Freezes that generate lasting team resentment miss most of them.
This checklist is organized by phase: setup decisions before you announce the freeze, operational checks while it is running, and thaw steps before you lift it.
What makes a freeze succeed or fail?
A well-run code freeze is invisible to most of your team. Engineers knew it was coming, they know when it ends, they know exactly how to get an urgent fix through, and the enforcement runs in the background without anyone managing it. The freeze protects the high-risk window it was designed for without becoming an ongoing management project.
A poorly-run freeze is a constant source of friction: ambiguous scope, no defined end date, enforced by Slack message, and resolved by finding an admin at midnight when something breaks. The frustration engineers feel is not with the concept of code freezes. It is with that specific operational pattern.
Before the freeze: setup checklist
Scope: freeze the right systems, not all of them
The most common mistake is freezing every repository in your GitHub org "to be safe." Teams working on internal tools, docs, and low-risk services stop shipping for no reason. That produces resentment and teaches engineers to treat future freezes as bureaucracy.
Start by asking three questions:
- Which services caused incidents in the last 90 days?
- Which services have the thinnest on-call coverage during this window?
- Which services would cause meaningful customer impact if they failed?
Freeze those. Leave everything else open, or move it to a softer protocol (merges allowed, no production deployments).
| Scope dimension | Best practice | Common mistake |
|---|---|---|
| Repositories | High-risk and customer-facing only | Every repo in the org |
| Environments | Production (and staging if it auto-promotes) | Every environment including dev |
| Branches | Main and release branches | All branches including feature branches |
Duration: define the window before you announce it
Set explicit start and end timestamps before you send the freeze announcement. "Until further notice" is not a freeze end date; it is a guarantee the freeze will drift. Engineers stop trusting it will end, held-back work accumulates, and the post-freeze surge becomes a reliability problem in its own right.
If the freeze is tied to a recurring event (December holidays, peak traffic periods, end-of-quarter), configure it as a recurring scheduled freeze so the activation and deactivation happen automatically without anyone needing to remember on the day.
Enforcement: automated beats announced
A freeze declared in Slack is a soft freeze. It depends on every engineer reading the message, remembering it at 5pm, and deciding not to ship "just this one small fix." That does not hold at scale.
An enforced freeze fails the merge automatically via a required status check. The merge button grays out. The engineer cannot proceed without an explicit override. This is a categorical difference from an announcement, not a variation.
The how to block merges to main on GitHub post covers the mechanics in detail. The core requirement: a service must post failure to the GitHub Commit Status API for every open pull request when the freeze is active, and success when it ends. New PRs opened during the freeze need the check posted at creation time, which requires a webhook listener, not a one-time script.
Automated enforcement also gives the override path its meaning. If any engineer can skip the Slack warning and merge, the override workflow is theater.
Override path: define it before the freeze, not during it
You will have a production incident during some freezes. Define the override path before the freeze starts, not at midnight when someone is paging you about a P0.
A workable override process:
- The engineer requesting the override submits a brief reason
- A second person (on-call lead, engineering manager, or release manager) must approve before the merge proceeds
- The action, the requester, and the approver are logged automatically
- The rest of the freeze stays active for everyone else
That last point matters. An override should not disable the freeze for the whole org. An admin who turns off the branch protection rule to let one fix through has now opened every repository to all merges, with no record of what shipped during that window. Targeted, logged, dual-approval overrides are the right design.
Communication: two weeks' notice, not two days'
Announce the freeze at least two weeks before it starts. That lead time lets engineers front-load high-risk changes, avoid scheduling risky migrations inside the freeze window, and set up on-call rotations accordingly. Last-minute announcements produce last-minute deploys.
The announcement should cover:
- Exact start and end, with time zone (not just dates)
- Which repos and environments are frozen
- What is not frozen
- How to request an override and who approves it
- Where the audit log lives
Say explicitly in the announcement that enforcement is automatic, not just declared in Slack. Engineers who have lived through soft freezes will notice and trust the process more.
During the freeze
Override requests need a response SLA
Someone needs to be reachable to approve override requests during the freeze, with a reasonable response window. If approval requires tracking down someone on vacation, engineers will either abandon the fix or route around the process.
A 30-minute response target during business hours, with the on-call rotation handling after-hours requests, is a workable setup. Write it down so override requesters know what to expect before they are in the middle of an incident.
Track what is being held back
A pull request that sits for two weeks can accumulate merge conflicts, stale dependencies, and untested assumptions. If 40 PRs are queued to ship the moment the freeze lifts, an undifferentiated surge is a predictable incident.
Keep a running list of held-back changes during the freeze, with a rough risk rating. You do not need elaborate tooling; a shared doc or a dedicated Slack channel works. The goal is to have something to sequence from when the freeze ends.
Planning the thaw
The end of a code freeze is its own risk. Every held-back change wants to go out at once. A few steps prevent the post-freeze surge from becoming a reliability event.
Sequence by risk. Ship the smaller, lower-risk changes first. Let the deployment pipeline warm up and let engineers watch dashboards before shipping anything that touches core infrastructure. Reserve large migrations and schema changes for day two or three.
Avoid ending the freeze right before a weekend. A freeze that lifts on Thursday gives two working days to catch issues from the surge. A freeze ending on Friday compresses that window to an afternoon.
Check for conflicts in held-back PRs before the lift. Long-running PRs diverge from main. Review the queue before lifting the freeze, not after, and flag anything risky before the rush starts.
When not to call a code freeze
A code freeze is the right tool for a specific situation: a defined window where the cost of an incident is higher than usual and the capacity to respond is lower. It is not the right tool for:
Compensating for a high baseline change failure rate. If a significant share of your deploys cause incidents, a freeze delays those incidents until it lifts. It does not fix what is causing them. What the data says about whether code freezes work covers this in more depth.
Long-duration coverage of normal engineering risk. A six-week freeze without a specific high-risk trigger is not a control; it is an engineering halt. The backlog compounds, the risk of the thaw grows, and the team learns that "frozen" can mean indefinitely paused.
Situations where a soft freeze would actually work. If your team is small and communication is tight, a shared norm may be enough for a one-time, short event. Just be honest about whether it is actually being followed.
The full checklist
| Phase | Checklist item |
|---|---|
| Setup | Scope limited to high-risk repos and environments only |
| Setup | Start and end timestamps defined and written down |
| Setup | Enforcement is automated (required status check, not Slack) |
| Setup | Override path defined: dual approval, auto-logged, targeted |
| Setup | Announcement sent two or more weeks in advance |
| During | Override approver reachable with a defined response SLA |
| During | Held-back changes tracked and risk-rated for sequencing |
| Thaw | Deploy backlog sequenced by risk (low-risk changes first) |
| Thaw | Freeze end not immediately before a weekend |
| Thaw | Long-running PRs checked for conflicts before lift |
Where tooling fits in
The checklist above is the design. Getting it to run without a human managing every step is a separate question.
For a single repository and a one-time freeze, GitHub's native branch protection and required status checks cover the basics. The parts that need extra work: the webhook listener for new PRs opened during the freeze, the scheduler for recurring windows, and the override workflow with a real audit trail.
NoShip is a GitHub App that handles these at the org level: required status checks across all connected repositories, RRULE-based scheduling for recurring freeze windows, rule-scoped policies by repo and environment, dual-approval overrides, and a signed audit trail. It requests no access to your source code. The concepts guide covers how freeze windows, rules, and override flows work in practice.
A freeze that activates on schedule, scopes to the right repos, and logs overrides without anyone manually doing it is the goal. The checklist describes what to build; the tooling question is how much of it you want to build yourself versus use off the shelf.
For teams running a seasonal freeze, the holiday code freeze guide covers the December-specific timing, scope, and communication decisions in more detail.
Keep reading
How to Manage the Deploy Backlog After a Freeze
The deploy backlog after a code freeze is the most dangerous part of the freeze. Here is how to release the queue in a controlled order instead of all at once.
How to Set Up Recurring Code Freeze Windows
A recurring code freeze runs on a schedule automatically. Here is how to configure Friday windows, quarterly freezes, and biweekly release patterns in RRULE.
How to Handle Emergency Deploys During a Code Freeze
An emergency deploy during a freeze needs a structured approval path, not a workaround. Here is how to design one that is fast enough to use and controlled enough to trust.