Praneet Sah
Compliance

GDPR for developers: what actually has to be built

GDPR is usually handed to engineers as a policy document, but data residency, erasure, portability, consent and 72-hour breach notification are all architecture problems. Here is what each one actually requires you to build.

The framing problem

GDPR usually arrives at an engineering team the wrong way round. Someone signs a European customer, a privacy policy gets written, a cookie banner gets installed, and the compliance work is declared done. The engineers are asked to add a checkbox.

Then the first real request arrives — a user in Berlin asking for everything you hold about them to be deleted — and it becomes clear nobody knows how to do that. Their rows are in Postgres, their events in the analytics platform, their tickets in a helpdesk SaaS. Their email is in a marketing tool, a billing provider, and a CRM. Their session recordings are in a replay product nobody remembers enabling. Their name is in application logs retained for a year. A database copy from three weeks ago sits in object storage, and another is in a warehouse a nightly job refreshes.

The policy said you would delete their data within a month. The system has no idea where it all is.

That is the shape of the whole problem. Several of GDPR's core requirements are engineering requirements with a documentation footnote, not the reverse, and there is no legal-only shortcut: you cannot write your way to deleting data from a system with no deletion path, or to detecting a breach in 72 hours if nothing alerts. The policy describes a capability; the capability has to exist. What follows is the engineering half of that, not legal advice — applicability is a question for counsel.

Data residency: less absolute than assumed, more architectural than hoped

Residency is the requirement most often stated too strongly and then built too weakly. Sales hears "our data must stay in Europe," a promise gets made, and engineering discovers that meeting it means considerably more than picking a different AWS region.

The nuance worth understanding is that GDPR does not, by itself, require that EU residents' personal data be stored inside the EU; it constrains transfers outside the EEA, permitting them under specific mechanisms. Hard residency requirements — data physically stays in region, full stop — more often come from somewhere adjacent: a customer contract, a procurement rule, a sector-specific national rule, or your own decision that running infrastructure in Frankfurt is less painful than negotiating transfer mechanisms with every enterprise buyer. The legal analysis belongs to counsel; the engineering consequence is identical either way.

And usually the architecture cannot do it. The primary database is the easy part; what breaks residency is everything around it. The search index in a US-hosted cluster. The workers running at your default deployment target. The object storage bucket created years ago in us-east-1 because that is what the tutorial said. The monitoring platform that receives a copy of a great deal of data and is almost never regionalised. The error tracker capturing request payloads, every SaaS tool in the support stack, and increasingly the model provider behind whatever AI feature you shipped last quarter.

Three broad approaches. Regional deployment of the whole stack — an independent EU instance with EU customers routed to it — tells the cleanest story and costs the most to operate: two of everything, and a permanent temptation to let the halves drift. It also forces a question many products have never answered, because if you have any cross-tenant feature — a shared marketplace, global search, an admin console — regional isolation is a product decision rather than only an infrastructure one. Regional data plane with a shared control plane — personal data in region, orchestration and billing global — is where most mature SaaS converges, at the cost of real discipline about what the control plane may see, since that boundary is enforced by your code rather than by geography. Provider residency controls are far cheaper where they cover the data in question; the work is verifying coverage honestly and noting the usual carve-outs for metadata, support access, telemetry and failover.

Whichever you choose, the durable artifact is a data flow map: every system that receives personal data, where it runs, under what basis. Building it is usually the moment the team discovers three tools nobody accounted for.

The right to erasure is not a DELETE statement

This is where most GDPR engineering work lives, and where the gap between policy and system is widest. The naive implementation is DELETE FROM users WHERE id = $1, possibly a soft delete, and a note in the ticket saying done. It is inadequate for an unsubtle reason: the person's data is not only in that row, and modern architectures produce a remarkable number of downstream copies.

Work through the list honestly. Replicas usually handle themselves, but verify. Search indexes do not: a Postgres delete removes nothing from Elasticsearch, Algolia, or a vector store — and vector stores are especially forgotten, because an embedding does not look like personal data until you remember it was generated from the user's own content. Caches outlive the row if the TTL is generous or absent. The warehouse holds copies of rows that no longer exist upstream, and is optimised for append-heavy analytical loads rather than surgical deletion, which is exactly why teams put this off. Application logs hold personal data if you have ever logged a request body, an email, or a user object. Object storage holds uploads, avatars, exports. Event streams are append-only by design.

Then everything outside your infrastructure — the CRM, email platform, support desk, payment processor — where some vendors have deletion APIs, some a form, some a two-week support ticket, and some have retention obligations that legitimately override the request. And then AI pipelines, the least-resolved part: content in a vector store or a prompt-logging table is tractable, but model weights fine-tuned on user data are not, which is the strongest practical argument for routing user data into retrieval and never into weights.

The way to build this so it stays correct is a deletion orchestrator: one service that keeps a registry of every system holding personal data, fans out deletion jobs, retries failures, and records what succeeded and when. Idempotent, because deletions get retried; auditable, because "we deleted it" is a claim you may have to support. The registry is the important part — it has to be updated when someone adds a data store, which means the deletion path belongs in the definition of done for any feature touching user data.

Backups are the honest exception. You cannot surgically edit an immutable snapshot without destroying its value as a recovery artifact. The workable position: delete from live systems promptly, keep backup retention bounded and actually enforced so residual copies expire on a known schedule, and — the part teams miss — make the restore runbook re-apply pending deletions before restored data goes live. Without that step, a restore silently resurrects people who asked to be forgotten.

Portability requires a data model that can answer one question

Portability is the quiet one. Fewer people exercise it, so it gets less attention, and then a request arrives and the team discovers it cannot produce the answer. Read as an engineering requirement: on request you hand a person the data they provided, in a structured, commonly used, machine-readable format. JSON and CSV qualify. A PDF does not — a PDF is a picture of data, not data.

The hard part is not serialisation. It is that producing the export requires the data model to support a coherent "everything about this person" query, and many data models cannot. Personal data ends up spread across tables referencing the user inconsistently — some by foreign key, some by an email string copied at write time, some in a JSON blob nobody has read since it was written. No single traversal reaches all of it, so building the export means enumerating every table by hand. Do that once and keep it current: a per-entity export definition saying how to find this person's records and what shape they take on the way out, assembled by one exporter driven by the same registry as the deletion orchestrator, because both answer the same underlying question from opposite directions.

Two judgement calls recur. Portability covers data the person provided, which is narrower than everything you hold — derived scores and inferences sit in more ambiguous territory, and where that line falls is worth asking before you build. And exports often contain other people's data: one person's message history is also someone else's words, so the exporter needs to filter or redact rather than dump joined rows.

DPAs constrain which vendors you can use

Data Processing Agreements look like paperwork, and engineers usually never see one. They are an engineering constraint, because they determine the set of tools you may build with. The mechanic is straightforward: if you are a controller — you decide why and how personal data is processed — every third party processing it on your behalf needs a written agreement governing what they may do. That covers the obvious infrastructure and the long tail: analytics, error tracking, transactional email, the support desk, the scheduling tool someone connected to a calendar, the AI provider behind your summariser.

This becomes an engineering problem at the moment of choosing a dependency. A developer comparing two vector databases or two speech providers is weighing latency, price and API ergonomics. The questions that actually gate the decision are different ones: is there a DPA at all, which regions do they process in, are subprocessors disclosed, do they retain or train on the data, can you get deletion on request. A vendor failing those is unusable for EU personal data regardless of how good the API is, and finding out during evaluation costs an hour where finding out after the integration is built costs considerably more.

Subprocessors catch people. Your processors have their own processors, and that chain has to be disclosed and generally authorised — meaning your vendor's vendor moving regions can affect your position with nothing in your codebase changing. The control is a maintained subprocessor list, plus treating "we added a SaaS dependency" as an event that triggers review rather than a pull request nobody thinks twice about.

Consent is not the answer to every question

The reflex response to any privacy requirement is a consent checkbox. Sometimes that is correct; often it is unnecessary or actively wrong, and understanding why requires a concept engineers rarely get told about: lawful basis. Processing personal data requires one, and consent is only one of several. Others commonly relevant to a product include contractual necessity — you need the data to deliver the service the person signed up for — legal obligation, and legitimate interests, which requires balancing your interest against the person's rights. Which basis applies to which activity is genuinely a legal determination, but the engineering implications matter, because the wrong basis builds the wrong system.

If the basis is contractual necessity, asking for consent is not merely unnecessary — it is misleading. You cannot meaningfully offer someone a choice about storing the email address they need in order to log in, and presenting it as a choice implies a withdrawal right you cannot honour.

If the basis genuinely is consent — marketing is the standard example — the bar is high in ways that shape both the UI and the data model. Consent must be a freely given, specific, informed, unambiguous affirmative action, which rules out pre-ticked boxes, bundling several purposes into one checkbox, and inferring agreement from continued use. It must be as easy to withdraw as to give. And you must be able to demonstrate it, which is the clearest engineering consequence of all: a boolean column saying marketing_consent = true is not a record of consent. A record of consent says which purpose, which version of the wording shown, at what timestamp, through which interface, and when it was withdrawn — an append-only event log rather than a mutable flag, because the question you will eventually answer is not "do they consent now" but "what exactly did they agree to, and when."

The failure mode in both directions is the same: treating consent as a UI element rather than as a consequence of a legal determination.

72 hours means detection, not a plan

Breach notification is where GDPR turns into an infrastructure requirement most sharply, because it imposes a clock. The obligation, broadly, is to notify the relevant supervisory authority of a personal data breach without undue delay and, where feasible, within 72 hours of becoming aware of it — with a risk-based threshold for when notification is required and a higher one for telling affected individuals directly. The specifics belong to counsel; the phrase engineers should focus on is becoming aware of it.

Because breaches are frequently discovered long after they occur, and often by someone else — a researcher, a customer, a vendor, or the attacker. If your detection story is "we would notice," the clock is not the real problem; the problem is that it does not start for months, and when it does you are simultaneously working out what happened, what data was involved, whose it was, and what to say, from scratch and under time pressure.

Meeting the timeline is therefore about what exists before the incident — a short list of unglamorous infrastructure.

Detection that fires on its own. Alerting on anomalous access — bulk reads, unusual export volume, access from new geographies, privilege escalation, repeated authorisation failures — routed to someone actually on call. Not a dashboard someone might look at.

Audit logs good enough to reconstruct scope. Within 72 hours you must characterise the breach: which records, which individuals, what categories of data. That is only answerable from a log recording who accessed what, when, and whether it succeeded, held somewhere the compromised system cannot rewrite. It is the same control SOC 2 asks for, built the same way, described in SOC 2 audit logging for developers — which is why teams pursuing both should build it once.

A data inventory. "Which individuals were affected" is unanswerable without knowing what data lived in the compromised system and whose it was — the same map residency, erasure and portability depend on.

A rehearsed process. Who decides whether the threshold is met, who contacts counsel, who drafts the notification, who talks to customers — decided in advance and walked through once, because a process first executed during a real incident is not a process, it is an improvisation.

The 72-hour requirement is unmeetable by policy alone. A plan for what you do after a breach is worth something; infrastructure that tells you a breach happened is what makes the plan usable at all.

What good looks like

None of this is exotic. A team in reasonable shape can point at a data flow map listing every system holding personal data and where each runs; a deletion orchestrator that fans out across all of them; an exporter driven by the same registry; a vendor checklist gating new dependencies on DPA, region and retention; a consent event log rather than a boolean; and alerting that would actually wake someone up.

That is a few weeks of deliberate engineering if you start early, and a difficult retrofit if you start after the first enterprise contract is on the table. It sits alongside the other controls that are cheap to design in and expensive to add later — audit logging, RBAC, SSO, encryption posture — which is the argument for treating compliance as an architecture concern from the beginning, and the same inventory work feeds directly into answering security questionnaires.

None of it makes you compliant on its own — compliance is a legal determination made against your circumstances, and the other half of this conversation belongs to a lawyer. But the engineering half is the half that has to be built, and no amount of policy writing builds it.

Frequently asked

Do we need GDPR compliance if we're a US company with no EU offices?
Often yes, and this is the single most common misreading. The regulation is written to apply extraterritorially: it reaches organisations outside the EU that offer goods or services to people in the EU, or that monitor the behaviour of people in the EU. Having no EU entity, no EU staff and no EU servers does not by itself put you outside its scope — what matters is whether you are deliberately serving or tracking people who are there. The practical test engineers can apply is whether the product is targeted at those users: pricing in euros, EU-language localisation, EU-specific marketing, or analytics that profile visitors from EU countries all point toward being in scope. A purely domestic US product that happens to get incidental EU traffic is a genuinely different case from one with an EU signup funnel. Because the line is fact-specific and consequential, this is a question to put to counsel rather than to settle by reading a blog post — but the engineering answer is that you should assume in-scope and build accordingly if EU users are part of the plan, since retrofitting erasure and residency later is far more expensive than designing for them.
Does 'right to erasure' mean we have to delete data from backups immediately?
In practice, no, and any vendor promising instant backup deletion is either misunderstanding the requirement or misrepresenting their architecture. Backups are immutable snapshots by design — that is the entire point of them — and rewriting a backup to remove one person's rows would destroy its integrity as a recovery artifact. The widely used approach is to delete from live systems promptly, document that residual copies persist in backups, define a bounded backup retention period after which those copies expire on their own, and put a technical control in place so that if a backup is ever restored, the pending deletions are re-applied before the restored data becomes live again. That last part is the piece teams skip and the piece that actually makes the position defensible. Write the approach down, make the retention window real rather than aspirational, and verify that your restore runbook includes the replay step. Whether that specific approach satisfies your obligations in your jurisdiction and circumstances is a question for counsel.
Is a cookie banner enough for GDPR compliance?
No, and treating the banner as the deliverable is how teams end up with a compliant-looking website sitting on top of a non-compliant system. A consent banner addresses one narrow slice: consent for certain tracking technologies at the point of collection. It does nothing about where personal data is stored, whether you can actually delete a person's data across every system that holds it, whether you can export it in a structured format, whether every processor touching that data is under an agreement, or whether you would detect a breach in time to report it. Worse, a badly built banner is often actively misleading — if the analytics script fires before consent, or the reject option is buried, the banner is documenting a consent you did not actually obtain. Build the banner, but treat it as the smallest item on the list, not the list.
Which of these should we build first if we can only do one?
Erasure, because it is the requirement that most constrains your architecture and the one that gets exponentially harder the longer you wait. Building an inventory of every place personal data lands — primary database, replicas, search indexes, caches, warehouse, logs, object storage, each third-party processor — and wiring a single deletion orchestrator to fan out across them is a substantial piece of engineering when you have five systems, and a rewrite when you have thirty. Portability tends to fall out of the same work, because both requirements need the same thing underneath: a coherent, authoritative answer to the question of what data you hold about a given person and where it lives.

Have a project like this?

Book a call

Praneet Sah

Independent app developer. Builds full-stack products end to end — web, iOS, Android, AI agents, telecom — and has shipped every project referenced on this page personally.