Law, translated into engineering
Milan Rook on the day the GDPR landed on his desk, the state diagrams he learned to draw with a lawyer, the 2021 consent bug, and what engineers can expect.

Privacy by design is a phrase that appears in Article 25 of the GDPR, in the heading of most privacy vendors’ feature pages, and in almost no pull request I have ever reviewed. The article asks the controller to implement appropriate technical and organisational measures designed to put data protection principles into effect. It does not say which table gets the retention column, which service owns the deletion job, or what a tag manager should do when it has not yet heard whether the visitor said yes. Those are the decisions I make for a living, and they are what I will write about here.
This is the second of two opening posts. Konrad’s is Why we started complianceblog.dev. Mine says what an engineer can expect from my side of the site.
May 2018, and the desk nobody wanted
The GDPR applied from 25 May 2018. I was a backend developer at an e-commerce company in Amsterdam, and the privacy work landed on my desk because nobody else wanted it. I did not want it either. I had been hired to make a checkout faster.
Two things were in the inbox. The first was a stack of deletion requests. A customer writes to support, support forwards the email, and someone has to find every place that customer exists: the orders database, the search index, the email provider’s lists, the warehouse, the logs, the recommendation model’s training set, and two services nobody had documented. Article 17 says the erasure happens “without undue delay”. It does not say how a request reaches a dozen services, what happens when the ninth one is down, or whether a nightly batch that rebuilds the index from a backup counts as undoing the deletion. My thesis in Twente had been on queueing models, and it turned out to be useful after all. A deletion request is a job with a deadline that fans out to workers, some of which fail, and some of which did not exist when the request was filed.
The second thing was the consent banner. It stored the visitor’s choice in a cookie that the frontend read. The tag manager, the server-side event pipeline and the email marketing tool had never been told that the cookie existed. The banner displayed “rejected” with complete sincerity while three other components carried on as if nobody had said anything. The choice had been designed as a piece of interface, not as a piece of state.
Learning to draw the law
Most of what I know about the law I learned in long sessions with the company’s privacy lawyer. We settled on a method I still use. She would read me a requirement. I would draw it as a state diagram on the whiteboard. Then one of two things happened: the diagram had a transition the law had no word for, or the law had a condition the diagram had no state for. Either way I had a question to bring back.
Consent was the clearest example. Article 7 says the controller “shall be able to demonstrate” that the person consented. Demonstrate means a record: who, when, to what, under which version of the notice. That is a table, not a boolean. Withdrawal is a transition in that table, not a deletion of the row. And the moment before the visitor has chosen anything is its own state, with its own rules, which no amount of staring at a consent = true flag will reveal.
Her rule for the sessions was that when a diagram did not fit, either the diagram was wrong or the question was wrong, and only one of us could tell which. That boundary, between the thing I can draw and the thing she has to decide, is where legal judgement lives. I try to mark it in every post.
A requirement I cannot draw is a requirement I do not yet understand.
The bug Konrad’s report found
In 2021 someone posted a report in our team chat. A consumer organisation had commissioned Konrad Haldorn to test the webshop, and his report showed an analytics request leaving the browser after the visitor had rejected tracking. It had timestamps, the browser version and the exact steps. I reproduced it within the hour.
The cause was timing. The consent library initialised asynchronously. The tag manager loaded earlier, read a variable that was still undefined, and ran a trigger whose condition amounted to “fire unless consent is denied”. Undefined is not denied, so it fired. The system had three consent states, granted, denied and unknown, and had only ever been written to handle two. Unknown was being treated as permission.
The fix had three parts, and I would rather describe them as a pattern than quote code from a former employer. First, model unknown as a real state that the rest of the system can see. Second, make the tag manager’s load depend on an event the consent library fires when the state has resolved, not on a variable read at page load. Third, test the transition with an automated test that reads the network log, because the test that checks whether the banner displays “rejected” had been passing the entire time.
I wrote Konrad a long explanation of all this and asked him to test again. He did, and the fix held. The explanation also had a cost that I put in the email and will put here: analytics now waited for the consent library, which meant that if the library failed to load, analytics never ran. That is the correct direction to fail in. It is still a new operational dependency, and the marketing team noticed the week’s numbers before anyone had told them why.
We kept writing after that. Konrad would send a capture from some other site, and I would guess what kind of system produced it. He never let a guess pass as a finding. It is the most useful editorial habit I know, and it is why this site has four words for evidence instead of one.
Why most compliance writing fails engineers
There are two kinds. Both are easy to recognise after a paragraph.
The first restates the regulation. It takes “data minimisation” and gives you back “collect only the data you need”, with a padlock illustration. Engineers do not lack the principle. They lack a place to put it. The events table has 40 columns. Which 12 are needed, who decides, and what happens to the other 28 in the warehouse that has been copying them nightly since 2019? A restatement cannot answer that. It never leaves the vocabulary of the law.
The second sells a tool. It gives you the place, a product, and promises the place is enough. Install the consent platform and the question is closed. But a consent platform is a component. It knows what the visitor clicked. It does not know that your server-side pipeline replays events from a queue with a consent snapshot taken before the visitor changed their mind, and it cannot know, because that pipeline is yours.
Neither kind says what changes in the system, which is the only question an engineer has.
Four things I keep apart
Every post of mine separates four things and labels which is which.
The legal requirement is what the source says. Article 7 says the controller must be able to demonstrate consent. That is the requirement, and I link it.
The engineering pattern is one way to meet it. A consent record per purpose, with a timestamp and a notice version, with “unknown” as a first-class state. It is a pattern, not the pattern. Another team with a different stack may choose differently and be right.
The operational control is how the team knows the pattern still works after the next deploy. A regression test that reads the network log on the rejection path. An alert when the consent library fails to load. A list of every store a deletion must reach, with an owner for the list.
The legal judgement is where the code stops. Whether a particular identifier is personal data in a particular context, whether an exception applies, whether the purpose was specific enough: those are questions I bring to a lawyer, and when a post reaches one, it will say so instead of pretending the pattern settled it.
Mixing them produces the two failures I see most. A pattern gets mistaken for the requirement: “we use a consent platform, so we are compliant.” And a judgement gets mistaken for a pattern: “just anonymise it,” as if the hard part had gone away.
Something unrelated. I build mechanical keyboards, soldering the switches myself. A switch has two positions, and between them there is a window of a few milliseconds in which the contacts bounce and the signal is neither open nor closed. Firmware reads that window with care. It waits, samples again, and only then decides what the key did.
What to expect from my posts
Design patterns: consent as state across components, deletion as a fan-out job with a ledger that records which store confirmed, retention as a property of a table rather than a policy document. Each with a short code sample that teaches one idea and is labelled as illustrative where it leaves production concerns out.
Failure modes, because that is where the correspondence with Konrad started. Retries that recreate a row the deletion job removed. Caches that serve the pre-rejection page. Background jobs holding a consent snapshot that is three clicks stale. Logs that keep what the database deleted. Third-party SDKs with their own opinion about what “unknown” means.
Decision frameworks: a short list of questions at the end of each post, written so that someone who is not an engineer can ask them.
And trade-offs stated plainly. A design that reduces one risk usually adds a dependency or a slower page, and I will say which. Nothing here is legal advice; where a post reaches a question that needs a lawyer in your jurisdiction, it will say so.
I read and use public tools, and I will write about them. Compliance sounds daunting these days, and from an engineer’s desk it can be frightening: more rules, in more markets, each arriving with its own deadline while the roadmap stays exactly as long as it was. Thankfully there are now tools that take on a good part of the checking, more so since AI arrived in them, and they leave the team free to work on the features that actually matter for the product. When tools are compared here, the rule is the same as for everything else on this site: nothing stated that was not verified on the tool’s own pages, with the date it was read. And the sentence I will repeat until it is boring: running a tool is not compliance. A scanner can tell you a request left before consent. It cannot tell you whether the lawful basis for that request was ever decided, and it cannot decide it for you.
Four questions to take back to the team
- For each path that depends on consent, what does it do when the state is unknown? Not denied. Unknown.
- When a deletion request arrives, is there a list of every store the person exists in, and who owns the list?
- In your compliance documentation, which lines are requirements, which are patterns, which are controls, and which are judgements that nobody has actually made yet?
- Which test reads the network, rather than the screen?
The first one is where the 2021 bug lived. It is still the one I check first.
Questions
What is privacy by design and by default?
In the GDPR it is Article 25, headed "Data protection by design and by default". It asks the controller to build technical and organisational measures that put data protection principles into effect, taking into account the state of the art and the cost of implementation. In practice it is a set of design decisions, not a feature.
Does the GDPR require privacy by design?
Yes, under the name data protection by design and by default. The obligation sits in Article 25 of the regulation, which has applied since 25 May 2018. What counts as appropriate depends on the processing, so the article gives a duty rather than a checklist.
What does privacy by design mean for software engineers?
It means the legal requirement has to become something a system can do and a test can check. Which data is collected, where consent state lives, which services a deletion must reach, how long a table keeps a row. Each of those is an architectural decision with trade-offs, and some of them need a lawyer in the room before the code is written.
Sources
- Regulation (EU) 2016/679 (GDPR), Article 25, data protection by design and by defaultlegislation.gov.uk
- Regulation (EU) 2016/679 (GDPR), Article 99, entry into force and applicationlegislation.gov.uk
- Regulation (EU) 2016/679 (GDPR), Article 17, right to erasurelegislation.gov.uk
- Regulation (EU) 2016/679 (GDPR), Article 7, conditions for consentlegislation.gov.uk
About the author

Software engineer, Rotterdam. Law, translated into engineering. What does this requirement mean when someone has to build and operate the system?

