code-freeze
Code Freeze Compliance: Building an Audit Trail for SOC 2
A code freeze backed by an automated audit trail satisfies SOC 2 CC8.1 change management requirements. Here is what auditors look for, what your logs must contain, and how to enforce it on GitHub.
A code freeze paired with a proper compliance audit trail is one of the most direct ways to satisfy SOC 2 change management requirements. Auditors reviewing CC8.1 want evidence that production changes were authorized, tracked, and bounded in time. A freeze that is enforced at the GitHub level and logs every action gives them exactly that, without a separate change management platform.
What do SOC 2 auditors actually look for in change management?
SOC 2 Type II auditors evaluate whether your change management controls operated consistently across the audit period. The control they focus on most is CC8.1: "The entity authorizes, designs, develops or acquires, configures, documents, tests, approves, and implements changes to infrastructure, data, software, and procedures to meet its change management objectives."
That language is intentionally broad, but in practice auditors want answers to three questions:
- Who authorized the change, and when?
- Was there a defined window during which production changes were allowed or restricted?
- Were exceptions documented and approved before they were used?
A code freeze answers all three, provided it is enforced automatically (not just announced in Slack) and produces a durable log of what happened.
The specific controls that freezes support vary by auditor. Some map them to CC8.1 directly; others use them as supporting evidence for CC7.2 (monitoring) or CC6.8 (preventing unauthorized software). What stays consistent is this: auditors want a paper trail, and a human-maintained spreadsheet does not hold up the same way an automated system log does.
Why do manual freeze logs fail in audits?
Most engineering teams that run code freezes do it with some combination of a Slack announcement, a calendar event, and a shared doc listing who got exceptions. That works well enough day-to-day. For a SOC 2 audit, it is a problem.
Completeness gaps. A manual log is only as complete as whoever maintained it. If someone merged a hotfix at 11pm during a freeze and forgot to update the spreadsheet, there is no record. That gap looks worse in an audit than the merge itself would have.
No proof of enforcement. A policy document stating "we don't deploy during freezes" is different from a technical control that actually prevents deploys during freezes. Auditors know the difference. The first is a stated intention; the second is evidence the control operated.
Attribution problems. Who approved the override? When exactly? Manual logs often say "approved by: engineering manager" with no timestamp, no specific scope, no record of what was permitted. That is not sufficient for a Type II audit covering 12 months.
Retention gaps. If your freeze records live in Slack or a shared doc, they get lost. Teams switch tools, channels get archived, spreadsheets get deleted. A dedicated system with a defined retention policy avoids this entirely.
What should a code freeze audit trail contain?
For a freeze log to hold up in a SOC 2 audit, each event needs to record specific fields:
| Event | Required log fields |
|---|---|
| Freeze created | Actor, name, start/end time, rules (block type, repos, branches, environments) |
| Freeze activated | Timestamp, triggering actor or schedule name |
| Override requested | Requester, reason, scope (repo, environment, or PR), timestamp |
| Override approved or denied | Approver, decision, timestamp |
| Freeze ended | Timestamp, actor or "expired automatically" |
Two fields matter most: the actor (who did this) and the timestamp (when). Without both, you cannot answer the auditor's authorization question.
Scope precision matters too. A log entry that says "freeze ended" is less useful than one that says "Q3 Release Freeze ended at 14:32 UTC on September 1, triggered by [admin username], covering repos matching myorg/api-* and myorg/web-app, environment=production." Auditors often sample specific events and ask you to walk through exactly what happened.
How do you scope a compliance freeze without blocking all engineering?
One objection to freeze-based compliance controls is that they are too blunt. A broad freeze covering every repository for 30 days will get bypassed, which means your audit log fills with override requests and the control looks weak.
The fix is narrowing the freeze scope to match the actual risk. A compliance freeze does not need to block every repository in your org. For SOC 2 purposes, the relevant scope is production-facing services.
A well-scoped compliance freeze looks like this:
- Block type:
both(merges and deployments) - Repos: a pattern matching your production services, not the entire org
- Branch:
mainorrelease/* - Environment:
prod*orproduction - Duration: the actual window, not a conservative overestimate
When the scope is right, engineers on non-production services are not affected at all. The freeze is invisible to most of the team, override requests are few, and the ones that exist represent genuine exceptions. That is exactly what an auditor wants to see.
The freeze scoping post covers repo, branch, and environment patterns in detail.
What does an emergency override look like to an auditor?
Override requests are where audits get uncomfortable. Auditors sample them specifically because they represent exceptions to your stated control. What they want to see is not zero overrides; that is not realistic. What they want is evidence that each exception was authorized before it was used and that its scope was limited.
A defensible override record looks like:
- Requested by: [GitHub username] at 2026-08-14 22:17 UTC
- Reason: "Critical fix for payment processing bug affecting checkout, PR #1142"
- Scope: repo myorg/billing-service, PR #1142 only
- Approved by: [admin username] at 2026-08-14 22:23 UTC
- TTL: 60 minutes, single-use
That is a clean record. A second person reviewed and approved the request before the merge happened. The scope was limited to one PR in one repo. The 6-minute response time shows the process ran without circumventing the freeze.
What auditors flag: overrides with no documented reason, overrides approved retroactively, overrides scoped to "all repos," and overrides approved by the same person who requested them. Any of these suggest the override process is not functioning as a real control.
See how to handle emergency deploys during a freeze for more on structuring the override workflow.
How long do you need to keep audit records?
For SOC 2 Type II, the audit covers a defined period, typically 6 or 12 months. Your logs need to cover that entire period plus some buffer for the audit process itself. Plan for at least 12 months of retention if you are pursuing certification, and 18 months to be safe during your first audit.
Check your retention policy before the auditor asks. If your freeze log tool has a 30-day or 90-day retention window, you will fail the evidence request at the end of the period. This is a common and easily avoidable problem.
Where does NoShip fit in the compliance picture?
NoShip logs every action to an immutable audit trail: freeze windows created and ended, schedules toggled, overrides requested and approved, each entry including actor, timestamp, affected resource, and full context. Audit records are retained for 7 days on the free tier, 90 days on Team, and 365 days on Business, which covers a full SOC 2 audit period.
Override requests require approval from a second person (an admin or owner in your GitHub org). They are single-use, expire after a configurable TTL, and can be scoped to a specific PR, repository, or environment. In the audit log, each override appears as a bounded exception with the requester, approver, and timestamp all recorded.
NoShip enforces freezes through GitHub's own mechanisms: required status checks and deployment protection rules. It requests no access to your source code. The concepts guide shows the full audit event schema.
NoShip is on the GitHub App Marketplace. Team is $99/mo with a 14-day free trial. Business ($299/mo, 365-day audit retention) includes the retention policy most SOC 2 auditors need. Public-repo orgs get the Open Source plan free.
The practical path forward
The goal is not to find a tool that generates compliance paperwork. It is to run a change control that actually works and also produces the evidence your auditor needs. A freeze that is automated, scoped to the right services, enforced at the GitHub level, and logged to an immutable trail does both at once.
If you are starting with change management controls, the code freeze best practices checklist covers the structural decisions that determine whether a freeze is a real control or just a policy on paper. The do code freezes actually work post covers the operational evidence behind them.
A compliance freeze done right is boring: engineers know the window, exceptions go through a two-person approval flow, and auditors see a complete, searchable log. That is the version that holds up.
Keep reading
Change Management for Engineering Teams Without the Bureaucracy
Change management for software engineering teams is about controlling which changes reach production, and when, without ITIL-style approval overhead. Here is what lightweight looks like.
Why Friday Deploys Are Risky (and What to Do Instead)
Friday deploys are risky because weekend response capacity drops, not because the code is worse. Here is when the risk is real and what to do about it.
How to Scope a Code Freeze: Repos, Branches, Environments
Scoping a code freeze means freezing only the repositories, branches, and environments that carry real risk, not the whole org. Here is how to draw the line.