heygrc
Answer

How can engineering teams catch compliance violations in code review instead of the audit?

the heygrc team

An audit is a lagging indicator: it samples what shipped months ago, when the violation is already in production and expensive. Code review is the leading one, the last moment a violation is one click from not existing. To catch compliance there: know which of your controls actually live in code, make the compliance question part of reading every diff, ground each flag in the specific clause, and keep the trail so the review itself becomes audit evidence.

  1. Map your controls to code surfaces

    Most framework controls are organisational, but a consistent minority live in code: audit logging (ISO 27001 A.8.15, SOC 2 CC7.2), access control (CC6.1), cryptography (A.8.24), data minimisation (GDPR Art. 5(1)(c)) and storage limitation (Art. 5(1)(e)), backups, and dependency changes. Those are the controls that drift one commit at a time, so list them once and keep the list where reviewers see it.

  2. Ask the compliance question on every diff

    Correctness and style already have a home in review; compliance needs one too. That can be a review checklist to start, or an automated reviewer that reads every pull request against your frameworks. Automation matters here because the question has to be asked every time to work: the one unread diff is where the violation ships. A neutral status check keeps this from slowing merges.

  3. Ground every flag in the clause

    A flag that says ISO 27001 A.8.15 is at risk is arguable, fixable, and later sampleable; a flag that says be careful with logs is none of those. Grounding is also what keeps false positives tolerable, because the engineer can check the claim against the control instead of guessing.

  4. Keep the trail

    A compliance reading attached to each pull request is evidence that the review ran on every change, exactly the kind of per-change trail an auditor samples. It also means the audit stops being the moment of discovery, because discovery already happened in review.

analytics/events.ts+1 -1
track("report.exported", {-  email: user.email,+  userId: user.id,  reportId: report.id,})
heygrcGDPR Art. 5(1)(c)

The event keeps its analytical value with an internal id instead of an email address. Caught in review, this is a one-line fix; found at the audit, it is months of exported events to remediate.