GDPR Right-to-Erasure Engineering: Actually Deleting Users From Complex SaaS Systems
A Berlin B2B SaaS vendor with around eighty thousand end users received a GDPR right to erasure request from a former customer in early 2026. The product manager clicked "Delete user" in the admin tool, the row disappeared from the primary database, and a confirmation email went out within the hour. Thirteen days later the same person sent a follow-up email containing screenshots of their old support tickets — fetched, with full original PII, from a Zendesk export the SaaS had imported into its analytics warehouse two years earlier. The data subject filed a complaint with the Berliner Beauftragte für Datenschutz und Informationsfreiheit. The fine was modest. The reputational cost was not.
The GDPR right to erasure is the data protection obligation engineering teams underestimate the most. On paper it is one Article — Article 17 — and a handful of well-known exceptions. In a real production system it is a distributed problem that touches every database, every queue, every search index, every analytics warehouse, every backup tape, every third-party processor, and every audit log a team has accumulated over a decade of shipping. Half-implemented deletion flows are the norm, not the exception, and they are exactly what regulators and auditors are now looking for. This post is a practical engineering blueprint for actually meeting the obligation across a complex SaaS — not a checklist, but the architectural decisions and code patterns that hold up under scrutiny.
What Article 17 Actually Requires — and the GDPR Right-to-Erasure Clock
Article 17 of the GDPR gives a data subject the right to obtain erasure of personal data concerning them when one of six grounds applies — most commonly that the data is no longer necessary for the original purpose, the subject withdraws consent and there is no other legal basis, or the subject objects under Article 21 and there is no overriding legitimate ground. The controller must act "without undue delay," which the EDPB interprets as a maximum of one calendar month, extendable by two months for complex cases provided the subject is informed of the extension and the reasons.
Three exceptions matter to engineering teams. First, the controller can retain data necessary for compliance with a legal obligation — invoice metadata under tax law (typically ten years in Germany), KYC records under AML rules, employment records, and similar regulated retention. Second, the controller can retain data necessary for the establishment, exercise or defence of legal claims. Third, the controller can retain data for archiving in the public interest, scientific research, or statistical purposes provided the processing is genuinely anonymised or strictly safeguarded. Outside these exceptions the obligation is absolute: the data goes.
The obligation engineers tend to miss is in Article 19. When a controller has disclosed personal data to recipients — third-party processors, sub-processors, integration partners — they must communicate the erasure to each, unless that proves impossible or involves disproportionate effort. In a typical SaaS that means the email service, the analytics processor, the support tooling, the billing system, the data warehouse, and any customer integrations the user authorised. "We deleted them in our database" is not a complete answer.
The Architecture Problem: Where User PII Actually Lives
A modern SaaS spreads user data across far more places than the architecture diagrams admit. In a Symfony plus Next.js stack of the kind we typically work on, an honest inventory covers ten or more locations: the primary PostgreSQL database, read replicas and reporting copies, the Redis cache, the message queue (which carries serialised user payloads in retry queues for hours), Elasticsearch or OpenSearch indexes, S3 or object storage for uploads and exports, application logs and the event stream, an analytics warehouse such as BigQuery or Snowflake, the data lake that feeds it, third-party processors (email, CRM, support, A/B testing), and the backups of every persistent store.
Each location has a different deletion mechanism. A PostgreSQL row is straightforward; a search index is eventually consistent and needs a refresh; a message queue cannot be selectively edited; an analytics warehouse is typically append-only with partitioned tables that make point deletes expensive; a third-party processor is reachable only through whatever DSR API it exposes. Backups are their own world, covered below.
A deletion strategy therefore has to start as a data inventory exercise. Map every system that holds anything tied to a user identifier, marking which holdings are direct PII, which are pseudonymised events, which are derived aggregates with no PII once the user id is removed, and which are subject to legal retention. This map is the artefact a regulator will ask for in 2026. A focused code quality audit often surfaces the silent holdings — old microservices writing to a forgotten S3 bucket, ad-hoc CSV exports on a shared drive, integration-test fixtures in a public reporting tool — that no one had on their list.
The Backup Problem: Legitimate Retention vs. Operational Reality
Backups are where most "we support GDPR deletion" claims quietly fall apart. The good news is the European Data Protection Board has published reasonably pragmatic guidance: a controller does not need to surgically delete a record from every backup tape the moment a request is received. The bad news is the controller does need a defensible answer for what happens to the deletion if and when the backup is restored.
The accepted pattern in 2026 is pending erasure on restore, sometimes called the "delete on resurrection" pattern. The controller maintains a durable list of erased subject identifiers — separate from the operational database, in an append-only store with its own backup discipline. When a backup is restored, the restore process consults this list and re-applies every erasure before the restored data is exposed to any production system or human. Combined with a defensible backup retention policy — typically thirty to sixty days for full operational backups, longer only where law explicitly requires it — this satisfies the obligation in almost every supervisory authority's published guidance.
The implementation is not glamorous. The erasure ledger has to be written before the operational deletion runs, so a crash mid-flow does not lose the request. Restore runbooks must include the ledger replay step as a mandatory checklist item, failing closed if the ledger cannot be reached. And the team has to rehearse a real restore at least quarterly, because a runbook that has never been executed is not a runbook.
Tombstones, Pseudonymisation, and the Audit-Log Problem
The hardest decision is what to do with records that reference the user but cannot simply disappear. A foreign key in an order table, an audit log showing that user X approved transaction Y, an immutable event store the system depends on for replay — none can be hard-deleted without breaking system consistency or, in some cases, another regulatory requirement (financial audit trails, FinTech transaction logs).
The right pattern is deletion of personal data, retention of the structural record. The order row stays, but the customer-id foreign key is replaced with a tombstone marker and every PII column overwritten. The audit log entry stays, but the actor identifier is replaced with a stable pseudonym untraceable back to the original user without a key that has been destroyed. The event store stays, but a replacement projection is built that excludes erased subjects.
A typical Symfony implementation looks like this. A User entity has its PII columns nullified or set to a fixed sentinel, a deleted_at is stamped, and the row is detached from any unique constraint that included the email. A UserPseudonym table maps the original user id to an opaque random identifier. Every reference table — orders, audit logs, comments, tickets — is updated in a single transaction to point at the pseudonym. Free-text fields are scrubbed via a regex sweep with manual review for edge cases.
// src/Service/UserErasure/Eraser.php
public function erase(User $user, ErasureRequest $request): void
{
$this->em->wrapInTransaction(function () use ($user, $request) {
$pseudonym = $this->pseudonymiser->createFor($user);
$this->ledger->record($user->getId(), $request);
$this->scrubProfile($user);
$this->repointReferences($user, $pseudonym);
$this->scrubFreeTextColumns($user, $pseudonym);
$this->em->persist($user);
});
$this->bus->dispatch(new SubjectErasedEvent($user->getId(), $pseudonym));
}
The SubjectErasedEvent is what fans the deletion out asynchronously to every other system: the search index drops the document, the analytics consumer rewrites or anonymises the subject's events, the third-party processors get DSR API calls, the object-storage purge job removes uploaded files, and a final verification job confirms each downstream system reported success. Done correctly, the synchronous part of the deletion takes milliseconds; the rest is observable, retryable, and traceable through the event bus.
Orchestrating Deletion Across Microservices and Third Parties
The orchestration is where teams either get this right or quietly fail audits. The pattern that works is a DSR pipeline — a dedicated, observable, testable workflow service rather than ad-hoc deletion code scattered across modules. The pipeline owns a single state machine per request: received, acknowledged, primary erased, downstream dispatched, downstream confirmed, verified, completed. Each step writes to a durable store, each transition emits an audit event, each downstream call has its own retry policy with explicit failure handling.
The third-party leg is the messiest. Each processor exposes a different DSR API, with different authentication, status codes, and SLAs; some still require a web form, a handful still require email. The realistic engineering answer is an adapter per processor with a uniform internal interface, integration tests, and circuit breakers so an outage at a single provider does not block the rest of the pipeline. Ten to fifteen adapters is typical; teams with rich integration ecosystems sometimes need thirty.
A Symfony Messenger-based implementation uses one queue per processor with retry middleware, exponential backoff, and a dead-letter queue. A nightly reconciliation job either retries with fresh credentials, escalates to a human, or — in the rare cases where the processor genuinely cannot be reached — records a documented "disproportionate effort" exception under Article 19. That exception register is itself an artefact a regulator will ask for: never empty (the team is hiding failures), never long (the team has chosen unreliable processors).
For teams modernising older PHP codebases, this orchestration is often the trigger for legacy code optimization work — the deletion pipeline cannot be retrofitted onto a monolith with no event bus and no clear domain boundaries without first untangling the data flow.
Verifying Deletion: The Audit Artefact That Closes the Loop
The final piece is verification. A request marked "completed" in the pipeline is worth nothing if the team cannot prove deletion when asked. The right approach is to generate, at completion time, a deletion certificate — a structured document recording the request id, the subject pseudonym, the timestamp at each stage, the list of systems queried, the third parties confirmed, and any exception-register entries. The certificate is signed cryptographically and stored in an append-only audit store.
What turns this from theatre into evidence is an automated sweep that, twenty-four hours after completion, re-queries every system in the inventory for any record carrying the subject identifier or an unmasked email, name, or phone number tied to the request. Any hit is a finding: it goes into an incident queue, the certificate is marked provisional, the gap is closed, and the post-mortem updates the inventory. Run regularly, this sweep also catches the silent holdings — the new microservice shipped last month that no one added to the inventory map.
For teams selling into regulated sectors, the deletion certificate is also a sales artefact. Enterprise procurement is increasingly asking suppliers to demonstrate, not describe, their erasure flow. A redacted real example closes those conversations far faster than a privacy-policy paragraph.
Closing
GDPR right to erasure is not a checkbox; it is an architectural property of a SaaS, and one that betrays the maturity of the underlying engineering more than almost any other compliance requirement. Teams that get it right have a documented PII inventory, an erasure ledger that survives backup restores, a pseudonymisation pattern that does not break their audit trail, a DSR pipeline with per-processor adapters, and an automated verification sweep producing a signed certificate per request. Teams that get it wrong meet the headline obligation in the primary database and quietly carry years of residual PII in their warehouse, their search index, and their support tooling — until a follow-up email or a procurement questionnaire surfaces the problem.
Wolf-Tech helps Berlin and EU SaaS teams design, implement, and validate right-to-erasure flows that hold up to both regulator scrutiny and enterprise procurement review. Contact us at hello@wolf-tech.io or visit wolf-tech.io for a free consultation.

