Start here

Compliance is a specification problem

The distance between a published regulation and a running system is one translation step, and that step usually has no owner. Somebody reads the rule, somebody writes a policy, and nobody converts the requirement into something the software enforces. That gap is where most real non-compliance is found.

Published August 22, 2026. Editorial.

Key takeaways

  • Split obligations into those satisfied by a person doing something and those satisfied only by the system behaving a certain way.
  • Convert each system obligation into an assertion with a subject, an action, and an observable result.
  • The words any, all, and the passive voice hide most gaps in regulatory text.
  • A check written after the implementation encodes what the code does, which in compliance is worse than no check.

A regulation is written to be legally precise, not to be implemented. Somewhere between the published text and the running system, somebody has to translate, and in most organisations that step belongs to nobody. Compliance owns the policy. Engineering owns the code. The conversion between them happens informally, in a meeting, and produces no artefact.

This page is about doing it deliberately. The method is the same across every sector, which is why it is on this hub rather than on one of the industry guides.

Step one: split what reaches the code

Regulation covers a great deal that has nothing to do with your application: governance, training, designated officers, board reporting, vendor diligence. Giving engineers all of it guarantees they miss the parts that matter.

The useful split is between obligations satisfied by a person doing something and obligations satisfied only by the system behaving a certain way. "Designate a qualified individual responsible for overseeing your information security program" [1] is process. Nothing in the code satisfies it. "Implement policies, procedures, and controls designed to monitor and log the activity of authorized users" [1] is a system obligation, and no amount of process satisfies it if the application does not write the log line.

Do this split in a document, with the sentence quoted next to each item. It takes an afternoon and it is the highest-value artefact in the exercise, because everything that follows traces back to it.

Quote rather than summarise. The summary is where a requirement loses its force: "any individual accessing any information system" becomes "users need MFA", and the two mean different things.

Step two: convert each obligation into an assertion

A system obligation is not yet something an engineer can build against. It has to become an assertion with three parts: a subject, an action, and an observable result. If you cannot name the thing you would look at to tell whether it held, the requirement has not been specified, and an engineer will fill the gap with a guess.

Take one sentence from the SEC's recordkeeping rule. The system must maintain a complete time-stamped audit trail including all modifications and deletions, the date and time of actions that create, modify, or delete the record, the identity of the individual doing so, and enough information to permit re-creation of the original record if it is modified or deleted [2].

That is four assertions, not one. Every create, modify, and delete produces an entry. Each entry carries an unambiguous timestamp. Each entry names the actual individual. And the original is reconstructable from the trail alone.

The fourth is the interesting one, because it is the one most systems fail and the only one that cannot be verified by reading code. You have to run the sequence and check that the reconstruction works. That distinction, between assertions you can check by inspection and assertions that require an experiment, is worth marking on your list, because the experiments are where the findings are.

A worked example makes the difference concrete. A team building a lending platform reads a requirement that adverse action notices must state the specific reasons for a denial. Read as prose, that sounds satisfied by a template that says "your application did not meet our criteria." Converted into an assertion, it becomes: for every denial, the system produces a record naming the specific factors that drove the outcome, in language a recipient could act on, generated at the moment of the decision rather than reconstructed afterward from a general description of the model. Written that way, the template fails the assertion immediately, because a template names no specific factor for any specific applicant. That is the value of the conversion: the prose requirement and the template both sound reasonable on their own, and only the assertion shows they do not match.

Step three: watch the three words

Three patterns in regulatory text account for most gaps.

"Any." A requirement for multi-factor authentication for any individual accessing any information system [1] gets implemented on the customer login and omitted from the admin console, the reporting tool, the log viewer, and the database bastion. Every one of those is an information system, and several expose more than the customer product does.

"All." A requirement to encrypt all customer information held or transmitted [1] gets verified on the primary database, which is encrypted because the cloud provider does it by default, and never on the backups, the replica, the warehouse, the export bucket, or the logs. "All" includes the copies, and the copies are where encryption actually fails.

The passive voice. "Records must be preserved" names neither an actor nor a location, which lets an obligation fall between the application and the infrastructure with each side assuming the other owns it. When you convert a passive obligation, assign the owner explicitly, because the regulation will not.

There is a fourth pattern worth watching that is not a single word: a qualifier that sounds like it narrows scope but actually widens it. "Reasonable security measures" reads as a lower bar than a specific numbered control, and in practice it is the opposite, because a numbered control has an edge you can point to and "reasonable" has to be defended after the fact against whatever standard an examiner or a court applies later. When a requirement uses an open-ended qualifier instead of a specific mechanism, the safer conversion is to the strictest plausible reading, documented with the reasoning, rather than to the reading that happens to match what the system already does. Choosing the second is how a team ends up defending its own convenience as if it were the regulation's intent.

Step four: trace every check to a sentence

Each compliance check should carry a reference to the provision it exists to satisfy, in the code, next to the assertion.

This makes coverage a query rather than an exercise. Put the obligation list beside the check suite and the obligations with nothing attached are visible immediately. Without the trace, assessing coverage means inferring intent from test names, which is unreliable enough that most teams do not do it and assume they are fine.

It also makes amendments manageable. When a provision changes, the affected checks can be found with one search. That is not hypothetical: the proposed HIPAA Security Rule update would move encryption from an addressable specification to a required one, changing what an assertion must say without changing what it is about. Traced, that is a targeted edit. Untraced, it means searching through a suite nobody has read in a year.

How this shows up in practice: an engineer joins a team eighteen months into a regulated build and is asked to modify the record deletion flow. Without traces, the honest answer to "which checks does this touch" is to run the whole suite and see what breaks, which finds regressions but not omissions, since a check that was never written will not fail. With traces, the same engineer searches the obligation reference for the deletion provision, finds the four checks tagged against it, and can also see that a fifth assertion implied by the same sentence, the one about reconstructing the original record, was never written at all. That gap is invisible to a test run and visible immediately to a search, which is the entire argument for tracing in one sentence.

Step five: write the check before the implementation

This ordering rule matters more in compliance than anywhere else.

A check written after the code encodes what the code does. Someone reads the implementation, sees that deletes are logged, and writes a test asserting that deletes are logged. It passes, and it proves nothing, because the standard was copied from the thing being measured. In a compliance context that is actively harmful, because it creates a passing result that a reviewer will reasonably read as evidence.

Written from the obligation first, the same check starts from "the trail must permit re-creation of the original" and frequently fails on its first run. That failure is the whole value. It is the moment you learn that your soft delete blanks the prior field values rather than preserving them, which is a thing nobody discovers by reading their own code.

The edge case worth planning for is the obligation with no clean assertion at all, because the behaviour it describes only exists at the boundary between two systems neither team fully owns. A requirement that a customer's data be portable to another provider in a standard format sounds like a single check until you notice that "standard format" depends on what the receiving system accepts, which your team does not control and cannot test against directly. The honest handling is to write the assertion for the part you do control, that an export exists, is complete, and matches a documented schema, and to record explicitly that conformance with a receiving system is outside what an automated check can verify. Marking a gap as untestable, with the reason stated, is more useful than a check that claims to cover something it cannot.

What you end up with

Three artefacts that fit together: an obligation list with quoted text, a set of assertions derived from it, and a check suite where every check names its source provision.

The deliverable is really the visibility. Most teams cannot answer "which of our obligations are enforced by something that runs," and the honest answer is usually that nobody knows. Getting to a state where the answer is a list, with gaps visible, is most of the work and nearly all of the value.

If you want help building that mapping for your product, get in touch.

Best for

  • Teams with a policy set and no mapping from obligations to enforcement mechanisms
  • Rebuilds, where the assertions can be written before the new implementation exists

Avoid if

  • Which obligations apply has not been scoped by someone qualified, since everything that follows has the same error

Check before you decide

  • Ask to see the obligation list with regulatory text quoted, not summarised
  • Pick one obligation and ask which single mechanism guarantees it
  • Check whether compliance checks reference the provision they satisfy
  • Check the commit history to see whether checks preceded the code they verify

Common questions

Where do most software compliance failures actually occur?

In the translation step between the published rule and the running system, which in most organisations belongs to nobody. Compliance owns the policy, engineering owns the code, and the conversion happens informally in a meeting and produces no artefact anyone can check.

How do you tell which obligations are engineering work?

By what satisfies them. If a person doing something satisfies the obligation it is process work, and if only the system behaving a certain way satisfies it, it is engineering work with no process substitute available.

Why quote regulatory text instead of summarising it?

Because the summary is where the requirement loses its force. 'Any individual accessing any information system' becomes 'users need MFA', and the second version leaves the admin console, reporting tool, and database bastion outside the scope the first version clearly covered.

Which assertions require an experiment rather than an inspection?

The ones about system behaviour over a sequence, such as whether an original record can be reconstructed from an audit trail after modification and deletion. Reading the schema tells you what could be reconstructed in principle, while only running the sequence tells you what actually can be, and these are where the findings concentrate.

Why does the order of writing checks and code matter so much?

Because a check written afterwards encodes what the implementation already does, so it passes while proving nothing about the obligation. Written from the obligation first, the same check frequently fails on its first run, and that failure is the entire value.

What is a compliance obligation, as distinct from a policy?

A compliance obligation is a requirement published by a regulator in operative language, such as a rule that an audit trail must record who deleted a record. A policy is an internal document describing how an organisation intends to meet that requirement, and the two are not the same thing: a policy can be accurate on paper while the system it describes enforces none of it.

How long does mapping obligations to code usually take?

The obligation list itself, quoted rather than summarised, takes about an afternoon for a defined scope. Converting each item into an assertion and tracing checks to provisions takes longer and depends on how many system obligations exist, but the mapping document is the highest-value artefact and produces value before the check suite is complete.

Is this approach worth it for a small regulated product?

Yes, because the failure mode it prevents, a policy that nobody has connected to running code, is not a function of company size. A small product with a few system obligations can complete the split and the assertions in a short amount of time, and the resulting list is what makes coverage answerable later instead of guessed at.

Start a project