SOC 2, ISO 27001 and GRC for engineers
Compliance appears in senior backend job descriptions because a company selling to enterprises cannot close deals without it. You are not asked to be an auditor. You are asked to build systems that produce evidence without anyone doing manual work.
The three terms
GRC - Governance, Risk, Compliance. Governance is who decides, risk is what could go wrong and what you accept, compliance is proving you did what you said. The engineering surface is almost entirely in the last one.
SOC 2 - a report by an external auditor against the AICPA Trust Services Criteria. Not a certification and not pass/fail: you get a report with any exceptions listed.
ISO 27001 - an international standard you certify against. It requires an ISMS: a documented management system with a risk register, defined scope, and a recertification cycle.
| SOC 2 | ISO 27001 | |
|---|---|---|
| Output | Auditor’s report | Certificate |
| Geography | US-centric | International |
| Focus | Operating effectiveness | Management system |
Most startups do SOC 2 first because US enterprise buyers ask for it by name.
Type I and Type II
Type I says the controls were designed properly on one date. Type II says they operated effectively across a window, usually 3 to 12 months.
Only Type II carries real weight, and the distinction matters to engineers: a Type II audit samples evidence across the whole period. A control you turned on the week before the audit fails.
What actually lands on the backlog
The controls that touch code are narrower than the standards suggest.
Access control. Least privilege, enforced and reviewable. In practice: SSO with no shared logins, IAM roles rather than long-lived keys, and quarterly access reviews someone signs off. Your RBAC model is a control - see Group, Role, and Claim Mapping.
Change management. Every production change is reviewed, tested and traceable. A protected branch requiring one approval and a green CI run is the control, and the pull request is the evidence.
Audit logging
Audit logging. Who did what to which record, when. This is the one most often retrofitted badly.
# The evidence trail has to be non-optional.
async def update_role(
actor: User, target: User, role: str
):
await repo.set_role(target.id, role)
await audit.record(
actor=actor.id,
action="user.role.changed",
target=target.id,
meta={"to": role},
)If the audit write can be skipped by a code path, it is not a control. Emit it in the repository or via an event, not in whichever handler remembered.
Encryption. TLS in transit, encryption at rest, managed keys. On AWS this is mostly a matter of turning it on and proving it stayed on.
Backups and recovery. Backups exist, are encrypted, and - the part people fail - restoration is tested on a schedule with the result written down.
Vulnerability management. Dependency scanning in CI, a defined window to patch by severity, and a record that you met it.
Evidence is the deliverable
The engineering insight is that auditors sample evidence, so the control has to emit evidence automatically. Anything requiring a human to remember will have gaps, and a gap in a Type II window is an exception in the report.
This reframes the work as observability:
- Access reviews come from an identity provider export, not a spreadsheet.
- Change management evidence is the git history plus branch protection settings.
- Deploy provenance comes from CI logs.
- Vulnerability SLAs come from scanner output over time.
# Branch protection IS the change-management control.
required_pull_request_reviews:
required_approving_review_count: 1
required_status_checks:
strict: true
contexts: ["tests", "security-scan"]
enforce_admins: trueenforce_admins is the line auditors look for. A control an admin can bypass
is a control with an exception.
The scoping lever
Scope is what makes an audit cheap or expensive. Everything in scope needs controls, evidence and review, so the practical move is to shrink the boundary: keep production data in a small number of systems, keep the audited environment separate from experimentation, and do not let customer data spread into analytics tools nobody listed.
The related engineering decision is data residency and isolation - a multi-tenant system with weak isolation is hard to scope. See Architecture & design.
What to say, and what not to
Claiming to have “done SOC 2” as an engineer overstates it and is easy to unpick. The credible version is that you built and operated controls: you added audit logging with an immutable trail, moved deploys behind branch protection and CI gates, replaced static keys with short-lived IAM roles, and produced evidence for the auditor without manual collection.
If you have not worked under an audit, say so and show you understand why the controls exist: they are ordinary engineering hygiene with an evidence requirement attached.
Related
Interview angle 5
- “What do SOC 2 and ISO 27001 mean to a backend engineer?” - a set of controls you implement and, more importantly, must produce automatic evidence for. SOC 2 is an auditor’s report against Trust Services Criteria; ISO 27001 certifies a management system. The engineering work is nearly identical.
- “Type I versus Type II?” - Type I checks the design of controls on a single date; Type II checks they operated across a window of months. Type II is the one buyers care about, and it samples evidence over the whole period, so a control switched on just before the audit fails.
- “Which controls touch your code?” - access control and least privilege, change management via reviewed and tested deploys, audit logging of who changed what, encryption in transit and at rest, tested backup restoration, and dependency patching within a defined SLA.
- “How do you keep compliance from becoming busywork?” - make each control emit its own evidence. Branch protection and CI logs are the change-management evidence, an identity provider export is the access review, scanner output is the vulnerability record. Anything that depends on a human remembering will show gaps in a Type II window.
- “Where does audit logging usually go wrong?” - it is written in handlers, so a code path that skips it creates a hole with no signal. Emit it in the repository layer or as an event so it cannot be bypassed, and keep it append-only and separate from debug logs.