Why this matters
SOC 2 change management asks one question: can you show that every production change was authorized, reviewed, tested and approved before it shipped? The auditor pulls a sample and follows the path from ticket to pull request to build to deployment. A missing link becomes an exception to explain.
The controls are not just settings in GitHub or GitLab. They are rules your team follows and records your tools keep. A required review that an admin can bypass is weak. A deployment approval that happens after release is theater. Good controls block the action and leave a timestamp.
This matters beyond the audit. The same controls catch bad code, accidental production changes and shared credentials. Make them boring. The best evidence is a normal change with a clear reviewer, passing checks and a deployment record that matches the artifact you tested.
Before you start
List every path to production. Include app repos, infrastructure as code, database migrations, serverless functions, mobile releases, feature flag changes and admin scripts. For each, note the source repo, build system, deploy tool and cloud account. SOC 2 scope usually follows the systems that run the product, not every internal tool.
Decide which branches and environments need protection. Most teams protect the default branch, release branches, staging and production. Name the roles: author, reviewer, approver and deployer. A reviewer should not be the author. A deployer should not approve their own release.
Check what your tools can enforce. GitHub has branch protection rules, required pull request reviews, CODEOWNERS, required status checks and environment protection rules. GitLab has protected branches, merge request approvals and protected environments. Azure DevOps has branch policies, required reviewers and deployment approvals. Confirm audit log retention and export before you promise evidence.
Write the emergency path now. A hotfix still needs a ticket, a review and a deployment record. You can allow one approval or a review after deploy, but say who can use the path and where it is logged. If the path is vague, people will invent one during an incident.
How to do it
Start with the repository. Turn on branch protection for the default branch. Require a pull request, one approval from someone other than the author and a green build. Dismiss stale approvals when new commits arrive. Block force pushes and branch deletion. For auth, billing or infrastructure paths, require review from a named team with CODEOWNERS.
Make the change link mandatory. Put the ticket ID in the pull request title or a required field. Add a CI check that fails when the link is missing. That link lets an auditor move from deployment back to the approved request without asking you for a spreadsheet.
Treat deployment as its own approval. Build the artifact once in CI, store it in a registry and promote that same artifact. Do not rebuild from a branch at deploy time. Put an approval gate on the production environment so a release waits for a named approver. Restrict who can deploy. Log the artifact digest, commit SHA, approver, deployer and time.
Keep the emergency path narrow. Use a break-glass role with extra logging. Open an incident ticket, get one approval, deploy the fix, then review it after the fact and record the result. Export pull request, approval, CI and deployment logs to storage you control, because tool retention can be shorter than your audit period.
- Map every production deploy path. Write down each repo, pipeline, cloud account and manual step that can change production. Include database migrations, infrastructure code and feature flag tools.
- Protect the main branches. Require pull requests, one approval from a non-author and passing status checks before merge. Block force pushes, branch deletion and direct commits for everyone, including admins.
- Require a ticket link on every change. Add a pull request template field or a CI check for the ticket ID. Make the check required so a change cannot merge without it.
- Gate production with named approvers. Use environment protection rules in GitHub, protected environments in GitLab or deployment approvals in Azure DevOps. Limit the approver group to people who understand the release.
- Build once and promote the artifact. Have CI produce a versioned artifact and record its digest. Deploy that exact artifact to staging and production, not a fresh build from the branch.
- Record emergency changes. Create a short form for the incident ticket: who approved, what shipped, why the normal path was skipped and when the review happened. Review it before the next release cycle.
- Export and retain the evidence. Send audit logs, pull request records and deployment events to a log store with a retention period that covers your audit window. Test an export before the auditor asks.
How to check it worked
Pick a handful of production changes from the last quarter, including a normal feature, a config change and a hotfix. Follow the chain: ticket, pull request, review approval, CI result, artifact digest and deployment record. If you cannot complete the chain without asking a person, the control is not auditable yet.
Try to break the controls. Push directly to the protected branch. Merge a pull request with no approval. Deploy production without the gate. Use the break-glass role and check that the alert and log entry appear. A control that only works when everyone cooperates is not a control.
Review the logs for the fields an auditor will ask about: who approved, who deployed, what commit or artifact, which environment and when. Confirm the reviewer is not the author and the approver is not the deployer for high-risk releases. Check that retention covers the audit period and that exports are readable outside the tool.
Run this check on a schedule, not once before the audit. Keep the result as a control test record. If a change fails, log it as an exception with the reason and the fix. A known gap with a plan beats a perfect story with no evidence.
Mistakes to avoid
Do not treat a passing CI job as approval. Tests show the code built; they do not show that a person with authority reviewed the change. Keep review approval and deployment approval as separate records when risk is high.
Do not let admins bypass branch protection without a trace. If admins can push to main, the control has a hole. Use a break-glass role with alerts and a required review after the fact. Export settings and events, not screenshots.
Do not review after merge or approve your own deploy. A pull request that is merged first and reviewed later gives the auditor a sequence problem. A deployer who approves their own release removes the second set of eyes. Fix the workflow, not the description.
Do not write a policy that your tools do not enforce. If the policy says two approvals but GitHub requires one, the sample will show the gap. Start with controls you can enforce today and add the rest with owners and dates. If you want help scoping the work, Cloudverg offers a SOC 2 readiness assessment at $7,500/month, which covers a gap assessment and a remediation roadmap. You can also use a compliance tool or your auditor's checklist.
Frequently asked questions
What code review evidence does a SOC 2 auditor need?
The auditor needs a record that shows the change, the author, the reviewer, the approval time and the checks that ran before merge. A pull request with a non-author approval and a linked ticket is usually enough. Keep the deployment record too, so the change can be tied to the release.
Can a small team meet SOC 2 code review requirements?
Yes, if the reviewer is not the author and the approval happens before merge. Use CODEOWNERS or a small reviewer group for sensitive code. If one person must handle a release alone, document the exception, get approval and add a review after the fact.
Do code review and deployment need separate approvals?
Not always, but the auditor wants to see that the change was approved and that production deployment was authorized. For high-risk systems, use a separate deployment approval. For low-risk internal tools, one review plus a logged deploy can work if the policy says so.