Four Eyes Without Pull Requests
How to design a review control that survives an audit and produces real safety, not just artefacts
Abstract
A reader of this newsletter, who runs a team that practises trunk-based development with TDD and pair programming, recently received feedback from an ISO auditor. Nothing in the team’s system of record explicitly recorded that a second pair of eyes had verified each change, nor whose eyes those were. He saw two options: log the second pair of eyes in commit messages, or fall back to short-lived pull requests with a single approval.
Both framings miss the point. The auditor’s complaint is correct on a narrow procedural matter, and wrong on the underlying mechanism. The answer is concrete, and made of five pieces: a written policy, structured commit trailers, automated enforcement with a tight exception path, supplementary telemetry, and a sampling audit with a defined protocol. Together they form a control that is testable, deliberate, and honest about its limits.
A reader’s question
The reader wrote a careful letter. His team had moved to trunk-based development with pair programming. They were happier and produced better software. Then the auditor noticed that the team’s working practice was not visible in any system of record. He suggested co-authored commits or a return to pull requests. He wanted advice.
The answer matters beyond one team. Many engineering leaders who would otherwise drop pull requests hesitate at the audit stage, and many keep pull requests precisely because they assume audit compliance demands them. That assumption is wrong, and it is worth dismantling carefully so the alternative becomes something a serious auditor will sign off, not a hopeful sketch.
The category error
ISO does not require pull requests. Neither does SOC 2. Neither does the great majority of compliance regimes that ordinary software teams find themselves under.
ISO/IEC 27001:2022 has Annex A controls covering change management and secure development. Their text, in plain language, requires that changes are authorised, that they are tested, that environments are separated, and that the process is documented. SOC 2 has CC8.1, which requires that the organisation manages changes through a process that includes authorisation and testing. The phrase “pull request” appears in neither. The shape of the review is left to the team.
A pull request is one possible implementation of a change-review control. A pairing session followed by a co-authored commit is another. A signed-off patch series sent through email, in the style the Linux kernel has used for two decades, is a third. The standard cares about the control objective. It is the auditor’s job to assess whether the team’s chosen mechanism meets that objective, regardless of its shape.
What a control actually needs
A review control that an auditor will accept has four properties. It is defined in writing, so the auditor knows what to test against. It is implemented in practice, so what the writing says is what actually happens. It produces evidence, so the implementation can be inspected after the fact. And it is enforced, so bypassing it is hard, or at least visible.
Mature controls have all four. Weak controls have three of the four, and it is usually the implementation that is missing. This is the awkward fact at the heart of the typical pull request workflow.
The implementation gap in pull request review
A pull request creates an artefact. It does not, on its own, create a review. The artefact and the review are independent variables. A diff is opened. Someone clicks approve. The change merges. Did the approver read the diff? Did they understand it? Did they push back on anything? The artefact does not tell us. The auditor sees a green tick and is satisfied. The control objective, in substance, is unmet.
I am not arguing that all pull request review is theatre. Some teams review carefully and reject changes that need rework. The argument is narrower. The artefact does not distinguish between deep review and rubber stamping, so the artefact alone is not evidence that review happened. When the artefact is the evidence, the control is weak even where the practice is strong.
Pair programming is the opposite trade. Two engineers engage with the code in real time, arguing about names, test design, edge cases, and whether the change should exist at all. The review is continuous, and it shapes the code as it is written rather than inspecting it afterwards. The findings in Accelerate and the annual State of DevOps reports indicate that high-performing teams favour smaller batches, more frequent integration, and trunk-based development. The kind of continuous review that pairing makes possible fits the pattern those findings describe better than long-lived branches do.
The catch is that pairing leaves no native artefact. The substance is present, the artefact is missing. The auditor’s complaint is correct on this narrow point. The fix is not to throw away the substance. The fix is to add the artefact.
The composite that closes the gap
Five pieces, working together, close the implementation and evidence gap that pure pairing leaves open. Compared with a typical pull request workflow, the composite is stronger on the question that matters most for safety (whether real review happened) and weaker on the question of independent-action provability that pull requests answer through platform authentication. That trade is worth making for most teams under ISO 27001 and SOC 2, but the article will be honest about both sides.
The policy
The team’s change management policy must say, in plain language, that pair programming or mob programming is the team’s primary control for the four-eyes requirement on code changes, that every change reaching the trunk is produced by at least two engineers working together, and that the second engineer is recorded on the commit. Without this document, an auditor has no benchmark against which to test the team’s behaviour. With it, every other artefact has meaning. Half a page, signed by the engineering lead and reviewed annually, is sufficient.
The commit trailers
Git supports structured trailers natively, and the conventions are not new. The Linux kernel has used Signed-off-by, Reviewed-by, Tested-by, and Acked-by for two decades to attribute who did what to a patch. The same machinery is available to any team. A pairing team adopts Co-authored-by for the active partner and, where appropriate, Reviewed-by for an additional asynchronous reviewer. A typical commit looks like this.
feat(billing): apply VAT for cross-border invoices
Co-authored-by: Jane Doe <jane@example.com>
Reviewed-by: Marco Rossi <marco@example.com> (commit b3f7a2c)
These trailers are machine-readable. A script can count them, sample them, and verify their format. They are also human-readable: an auditor opening the commit sees, at a glance, who participated.
The enforcement
A pre-commit hook on the developer’s machine refuses commits that do not meet the rules. A CI check on the integration branch is the backstop, in case the local hook is bypassed. The rules need to be specific, otherwise enforcement is theatre too.
A defensible minimum has five rules. Every commit on the integration branch must contain at least one Co-authored-by or Reviewed-by trailer. The trailer’s email must resolve against the company directory, so anonymous attestation is impossible. The author and the named co-author or reviewer must be different identities. A commit without a co-author must contain an explicit Solo-work: line naming an asynchronous reviewer by directory identity and the commit hash that reviewer inspected. A commit lacking any of the above is rejected by CI, and the rejection is logged.
The exception path is deliberately narrow. There is no zero-review path. Either the work was paired (Co-authored-by), or it was reviewed asynchronously (Reviewed-by), or it was solo with an explicit asynchronous reviewer attached to a specific commit hash (Solo-work:). All three produce structured evidence; none allows a change to reach the trunk unattested.
The telemetry
Where pairing tools such as Tuple, Pop, or mob.sh are in use, their session records corroborate the trailers. Calendar invites for recurring mob sessions add a second source. None of this is the primary evidence; it is corroboration, and it makes systematic falsification harder. The composite does not lean on telemetry, because individual sources are easy to dismiss in isolation. The trailers and the audit do the work.
The sampling audit
This is the linchpin of the composite, and it deserves a defined protocol rather than a vague intent.
A defensible minimum specification: each quarter, draw a random sample of commits from the integration branch using a seeded selection script. The sample size is the greater of ten percent of commits in the period or twenty commits, capped at fifty. The audit is run by a person who is not the engineering lead of the audited team, such as a peer team lead, a quality engineer, or an internal audit function, in order to avoid self-review. For each sampled commit, both named engineers are interviewed separately, with three standard questions: what was the purpose of this change, what alternatives were considered, and what would you do differently now. The interviewer records answers in a shared log alongside the commit hash and the date.
Failure criteria are explicit. If two or more sampled engineers cannot recall a change they are recorded on, the result is a process finding that triggers a documented remediation: a refresh of the policy, a refresh of the team’s pairing protocol, or, in serious cases, a return to short-lived pull requests until the team rebuilds the practice. The criterion does not catch deliberate fraud reliably; it does catch process drift, which is the failure mode most relevant under ISO 27001 and SOC 2.
This is the step that converts a culture of trust into a control an external auditor can test, because it gives the auditor a mature internal control to inspect rather than a claim to take on faith. Auditors recognise this structure. It is the same shape they apply to financial controls.
Three honest objections
A serious reader will already have noticed three problems. Let me address them rather than pretend they do not exist.
Self-attestation
A Co-authored-by trailer is, in the end, a string the committer types. A pull request approval is stronger on this narrow point: it is tied to an authenticated user action with its own audit trail. The composite does not pretend to match that property.
What the composite does instead is shift the weight of the control from a preventative mechanism (a click that allegedly happens before merge) to a detective mechanism (a sampling audit that verifies, after the fact, that review happened in substance). This is a recognised pattern in audit design: detective controls are weaker on the moment of action and stronger on the cumulative behaviour of the system. Two engineers conspiring to falsify a trailer must also conspire to give consistent, detailed answers to separate interviews about a commit they did not work on, and they must do so every time a sampling audit lands on a falsified commit. Casual misuse becomes uneconomic; deliberate fraud becomes detectable through pattern analysis.
The composite does not eliminate the self-attestation problem. It raises the cost of circumvention to the point where the dominant residual risk is process drift, not collusion. For ISO 27001 and SOC 2, that is sufficient. For regimes that demand non-repudiation as a primary property of the control, it is not, and the regulatory carveouts below apply.
Distributed teams
Pairing across timezones is genuinely hard. A team that cannot pair synchronously cannot rely on pairing as its primary control without modification. The honest answer is a hybrid: synchronous pairing within a timezone, asynchronous review with Reviewed-by trailers across timezones, and a policy that distinguishes the two cases. The asynchronous case shares structure with a pull request workflow, but the review is recorded in the commit history rather than in a separate workflow, the integration is not delayed by it, and the same enforcement and sampling apply. The article does not claim distributed teams can drop the second pair of eyes. It claims they can record it without inheriting the bottleneck of long-lived branches.
Solo work
Some changes are genuinely solo: a quick fix, a documentation typo, a configuration tweak that does not warrant a pairing session. The exception path in the hook handles this, but only with a name and a commit hash attached, and only with the same sampling treatment as paired commits. The team’s policy should set a threshold above which pairing is required regardless. This is no different from the way mature pull request workflows distinguish trivial changes from substantive ones; the difference is that the distinction is recorded in the commit, not in the branch lifecycle.
The regulatory carveouts
There are regimes where the literal separation of author and approver is required, and where genuine pairing cannot satisfy the requirement on its own. SOX controls over financially material systems, FDA rules for software in medical devices, parts of the GxP family in pharmaceutical manufacturing, and certain defence-sector standards come to mind. In those contexts, the team can still pair as its way of working, but a separate, documented approval step by a person who was not part of the pairing is also required. The composite described above is not a universal substitute. It is a substitute for the great majority of teams under ISO 27001 and SOC 2, which is the relevant audience here. A team operating under one of the stricter regimes should read the actual control text and design accordingly, and should be wary of any blog post, including this one, that promises a single mechanism for all cases.
Talking to the auditor
The conversation with the auditor is a conversation about controls, not artefacts. Open by asking which control objective the auditor is testing, in their own words. Almost always, the answer reduces to “evidence that a person other than the author has reviewed the change before it reached production”. Once that objective is on the table, the team presents the policy, the trailers, the enforcement rules, the telemetry, and the sampling audit log as the answer. Bring the policy. Bring an example of a sampled commit with the corresponding interview record. Treat the auditor as a serious reader who needs to be shown a serious control.
Most auditors respond well to a team that has thought clearly about what the standard is asking for and has produced a deliberate answer. Some do not. If the auditor insists on pull requests as a fixed shape, regardless of the control objective they implement, the team has three options. First, escalate within the audit firm: some firms have specialists who understand modern engineering practice, and some auditors can be replaced. Second, accept the audit firm’s preference and run a pull request workflow alongside the team’s real practice, knowing that the pull request is a record-keeping ritual rather than the actual review. Third, move the certification to a different firm.
None of these is comfortable, and the article will not pretend they are. Escalation is not always available, especially for smaller customers of larger audit firms; switching firms is disruptive and rarely fast; running a parallel ritual erodes the cultural commitment to pairing over time. The honest position is that a team should attempt the first option first, prepare for the second as a fallback during the certification cycle in question, and consider the third only if the audit relationship cannot be repaired.
The choice
The reader’s question looked technical. It is, in the end, a choice between two kinds of compliance.
One kind is built from the appearance of review. It is cheap, recognisable to a tired auditor, and satisfies the letter of the standard at the cost of producing very little of the substance the standard intends. The other kind is built from real review, made auditable. It costs discipline, a small amount of tooling, and a defined sampling protocol. It produces both real safety and a paper trail, and it makes the team better at building software along the way.
I know which one I think is worth buying. So does the reader, which is why he wrote in the first place. The good news is that he does not have to abandon what is working in order to satisfy an auditor. He has to write the policy, install the hooks, define the enforcement rules, set up the sampling protocol, run the audits, and have the conversation with care. The pairing stays where it belongs, in front of the code, with two pairs of eyes that actually see it.
References
A. Laforgia, Stop Using Pull Requests, https://a4al6a.substack.com/p/stop-using-pull-requests
A. Laforgia, The Illusion of Compliance, https://a4al6a.substack.com/p/the-illusion-of-compliance
ISO/IEC 27001:2022, Information security, cybersecurity and privacy protection. Information security management systems. Requirements, Annex A controls on change management and secure development.
AICPA, Trust Services Criteria for Security, Availability, Processing Integrity, Confidentiality, and Privacy, criterion CC8.1 on change management.
Linux kernel project, Submitting patches: the essential guide to getting your code into the kernel,
Documentation/process/submitting-patches.rst, on the use ofSigned-off-by,Reviewed-by,Tested-by, andAcked-bytrailers.Git documentation on commit trailers and the
Co-authored-byconvention as supported by GitHub and GitLab.P. Hammant and contributors, Trunk Based Development, https://trunkbaseddevelopment.com
N. Forsgren, J. Humble, G. Kim, Accelerate: The Science of Lean Software and DevOps, IT Revolution Press, 2018.
K. Beck, Test-Driven Development: By Example, Addison-Wesley, 2002.
M. Feathers, Working Effectively with Legacy Code, Prentice Hall, 2004.

