Waiver as Code
also called Machine-Readable Exception, Gate-Readable Waiver
Storing each approved deviation where the enforcement point reads it - rule id, scope, owner, justification, compensating control, expiry - so an exception stops a block automatically and its expiry becomes a technical event rather than a calendar reminder.
A waiver is approved in a governance meeting, minuted, and recorded in a spreadsheet. On Monday the pipeline still fails at the policy step, because the gate cannot read a spreadsheet. Within a day someone adds a bypass flag to unblock the release, and the bypass outlives the waiver, the standard and the people who agreed both.
That sequence is how organisations end up with a control that blocks nothing. The exception process and the enforcement point were built by different groups, in different media, and the gap between them is filled by whatever is fastest at 18:00 on a Thursday.
Why it matters
An exception is part of the control, not an escape from it. A control with no exception path gets bypassed; a control whose exceptions live only in prose is unenforceable. The only version that works is one where the same record both permits the deviation and expires it, because expiry is the single property that stops a temporary deviation becoming the architecture.
Two registers always drift. Governance says 41 live waivers, the repositories say something else, and nobody can reconcile them without reading both. Prose registers reliably end as a few hundred entries where most dates have passed, because nothing in the system notices a date.
Implementation patterns
- One record, two views. It lives beside the code it excuses, or behind an API the gate calls. Fields: rule id, scope selector, risk owner, justification, compensating control, expiry, approval reference. The governance report is a query over the same records.
- Gate logic explicit about scope: rule fails AND an unexpired waiver covers this scope, then allow and emit a use event. Never accept a wildcard scope, which disables the rule estate-wide while the register still shows one waiver.
- Approval as a signed merge, with the waiver path protected by code owners from the risk function.
- Graduated enforcement: warn in the build output from 30 days out with a countdown, then block. The warning has to appear where the engineer already looks, not in mail to a group alias.
- Jittered expiry and a weekly cap, refusing to grant a waiver expiring in a week already at the reassessment capacity of the approval path.
- Halving extensions, so the second term is half the first and the path converges.
- Report by rule, not by waiver. Twenty waivers against one rule is a statement about the rule.
Industry example
A retailer's platform team of around 400 engineers runs policy checks in production across several hundred repositories. The pattern is consistent at that shape: during a six-week release freeze, dozens of waivers are granted against one standard with the same twelve-month term, and they expire in the same week a year later. Every pipeline holding one fails that morning, the failures are reported as a gate outage because a denial message that does not name the waiver is indistinguishable from a broken engine, and the week ends with a bulk extension nobody reassessed.
Failure scenarios
- Wildcard scope, disabling a rule globally under the appearance of one exception.
- The waiver file editable by the team it excuses, making the exception self-service.
- Correlated expiry, producing a stampede and a bulk extension that launders a permanent deviation.
- Gate unavailable and failing open, so the exception path becomes the outage path and nothing is recorded.
- Expiry extended in the same pull request as the feature, which is how a reviewer approves it unaware.
Trade-offs
| Choose | Gains | Pays |
|---|---|---|
| Prose register | No tooling | Unenforceable expiry and two drifting truths |
| Waiver as code | Expiry enforced, use measurable | Schema, review path and a grant tool to maintain |
| No exception path | Nothing to maintain | Bypass flags you cannot see |
The real cost is governance work rather than engineering: somebody owns scopes, and a selector written too broadly is worse than no waiver.
When not to use it
Where the control is enforced by a human reviewer, the register is the right home and a file adds nothing. Below roughly ten open exceptions, a spreadsheet and a reminder are adequate. And when one rule accumulates waivers from most teams, stop writing waivers: the standard is wrong, and codifying the exceptions avoids that conclusion.
Interview question
Q: Your policy gate blocks on a rule a team legitimately cannot meet for another quarter. Design the exception so that in twelve months you are not holding fifty permanent waivers.
What a strong answer covers: the record's fields and where it lives; a scope narrow enough that the waiver excuses nothing else; who merges it; graduated warning then block; jittered expiry capped at reassessment capacity; halving extensions; use events so bypass frequency is measurable; and the aggregate signal that should change the rule instead.
Quick check
Quiz: Why does an approved waiver recorded in a spreadsheet still fail a build? — The enforcement point only evaluates records it can read, so the exception must sit in the repository or behind an API the gate calls, with scope and expiry as fields.
Flashcard: What makes a waiver's expiry a technical event rather than a diary entry? — The gate reads the expiry at evaluation time and blocks after it, with a countdown in the build output from 30 days out.