code-freeze
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.
The deploy backlog after a code freeze is the most dangerous part of the whole freeze, and most teams plan for it least. When a freeze lifts, the queue of merges and deploys that piled up behind it does not release itself safely just because the calendar says the freeze is over. If everyone hits merge at 9am on the first day back, you have converted a controlled window of no change into an uncontrolled window of maximum change, which is exactly the condition freezes exist to avoid. The thaw needs its own plan.
Why is the backlog after a freeze so risky?
The backlog is risky because it concentrates change. A freeze does not stop work, it stops shipping. So a two-week holiday freeze does not produce two weeks of nothing, it produces two weeks of merged-but-unreleased branches, held PRs, and deploys someone was ready to run on day one. All of that lands in a short window when the freeze lifts.
Three things make that window worse than a normal release day:
- Volume. You are shipping the accumulated change of the entire freeze, often in a day or two.
- Staleness. Code that was written and reviewed three weeks ago has drifted from the current state of
main. Assumptions the author made may no longer hold. - Interaction. When ten changes ship in an hour, and something breaks, you cannot easily tell which one did it. The debugging surface is every change at once.
This is why the incident rate after a freeze can be higher than during a normal week, not lower. The freeze bought you a quiet window and then handed you the bill for it all at once. Research on whether code freezes actually work keeps running into this: the freeze itself is often fine, and the thaw is where the incidents cluster.
When should you start planning the thaw?
Before the freeze starts, not when it ends. The thaw plan is part of the freeze plan. If you are reading this on the last day of a freeze with 40 queued PRs and no order to release them in, you are already behind, but you can still triage (see the ordering section below).
The reason to plan the thaw up front is that the information you need is freshest before the freeze. Which changes are highest risk? Which touch shared infrastructure, migrations, or the payment path? Which are simple and isolated? You know that best when the work is being held, not two weeks later when you have forgotten the context. Tag the risk level as PRs get held, not as they get released.
How should you order the deploy backlog?
Release the backlog in risk order, lowest risk first, one meaningful batch at a time, with room to watch each batch before the next. Do not release by merge order, alphabetical order, or whoever asks first.
A practical ordering, from first to last:
- Low-risk, isolated changes. Copy fixes, config-flag-guarded changes, small bug fixes with no shared surface. These build confidence that the pipeline and production are healthy before anything scary ships.
- Standard feature changes with good test coverage. The bulk of the queue. Ship in small batches, not all at once.
- Database migrations and schema changes. Ship these alone, with nothing else in the same batch, so if something breaks you know exactly what caused it.
- Changes to shared infrastructure or high-traffic paths. Auth, billing, checkout, anything a lot of other things depend on. Ship last, when the pipeline is warm and the team is watching.
- Anything stale enough to warrant re-review. If a PR was reviewed three weeks ago against a very different
main, treat it as new work, not as pre-approved.
The point of the ordering is observability. You want each batch to be small enough that if something breaks, the cause is obvious. Shipping a low-risk batch first also verifies the release pipeline itself works before you trust it with the risky changes.
Should you lift the whole freeze at once, or in stages?
Stage it. A freeze that lifts globally at one instant maximizes the concentration problem. A staged thaw spreads the change over hours or a day and keeps the observability you need.
A few ways to stage the lift:
| Staging approach | How it works | Best for |
|---|---|---|
| By risk tier | Release low-risk changes first, high-risk last | Almost every thaw |
| By service or repo | Lift the freeze on one service, confirm healthy, then the next | Multi-repo orgs with clear service boundaries |
| By environment | Ship to staging in bulk, soak, then production in ordered batches | Teams with a real staging tier and time to soak |
| By time window | Reopen deploys for a set window each day rather than all at once | Very large backlogs that need multiple days |
The common thread is that at no point are you shipping the entire accumulated backlog in one undifferentiated rush. You always have a small, recent set of changes in flight that you can reason about.
How do you keep the pipeline observable during the thaw?
Watch each batch land before you release the next one. The single most valuable thing during a thaw is a person (or a rotation) whose job is to watch production health as the backlog drains, not to be shipping their own change at the same time.
Concretely:
- Deploy in small batches with a gap between them. Enough time to see error rates, latency, and key business metrics settle before the next batch.
- Keep a named owner for the thaw. One person coordinates the release order and calls a stop if something looks wrong. This is a coordination role, not a merge-everything role.
- Have a fast rollback ready. The thaw is exactly when you will need it. If your rollback story is slow or manual, fix that before you start draining the queue.
- Do not schedule the thaw for a Friday afternoon. You want the change window to have full staffing and full daylight after it. Shipping a large backlog right before a weekend is the worst version of the Friday-deploy problem.
What about stale branches and merge conflicts?
Rebase and re-check every held branch against current main before it ships. A branch that was green when it was held is not necessarily green now. Other changes have landed, the base has moved, and a clean merge three weeks ago can be a silent logical conflict today.
For each held PR before release:
- Rebase or merge in the latest
mainand re-run the full test suite. A passing CI run from before the freeze does not count. - Re-review anything that changed materially during the rebase. Non-trivial conflict resolution is new code and deserves new eyes.
- Confirm the change still makes sense. Sometimes the reason for a change evaporated during the freeze, or another held PR now covers it. Ship less, not more.
The staler the branch, the closer you should treat it to net-new work. The worst backlog incidents come from code that everyone assumed was already vetted because it was "just waiting."
How does a freeze tool help with the thaw?
A freeze tool that scopes by repo and environment lets you stage the lift instead of flipping one global switch. That is the difference between a controlled thaw and a rush. If your freeze is a single branch protection rule you toggle off, the lift is all-or-nothing: the instant you disable it, every queued change across every repo can merge. There is no natural staging.
NoShip enforces freezes through per-repo and per-environment rules, so you can lift the freeze on one service, confirm it is healthy, and then lift the next, all without touching the rules for services you are not ready to thaw. Because it blocks merges through required status checks and deployments through GitHub deployment protection rules, you can also lift merges and deploys independently: let the queue merge and settle on a staging environment first, then open production deploys in ordered batches.
The audit trail matters here too. When you release the backlog in stages, having a record of what shipped when, and in what order, is what lets you correlate a metric change to a specific batch during the thaw. Setting up the freeze windows themselves is covered in recurring freeze windows, and the broader checklist lives in code freeze best practices.
The short version
The freeze is the easy part. Holding change is a switch. Releasing it safely is a plan. Order the backlog by risk, stage the lift instead of flipping one global switch, rebase and re-check every stale branch, and watch each batch land before you release the next. A freeze that ends in a 9am merge stampede did not make your release safer, it just moved all the risk to one morning and put it on the calendar.
Keep reading
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.
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.