GDPR for Engineers: Building Privacy-Compliant Systems

Implement GDPR compliance as an engineering practice, not a legal checkbox. Covers data minimization, consent management, right to erasure, data portability, privacy by design patterns, and the technical architecture that makes compliance maintainable.

GDPR for Engineers: Building Privacy-Compliant Systems

This reference is for engineers building systems that must meet the General Data Protection Regulation (GDPR) requirements. Use it when designing, implementing, or auditing data flows, storage, and processing pipelines. It applies when systems must support data subject rights, record processing activities, enforce consent, and ensure accountability across the data lifecycle.

Designing for Data Subject Rights

Implementing the right to access, rectify, erase, and port personal data requires clear data modeling and integration patterns.

Retrieve Personal Data on Request

When a data subject requests their personal data, the system must return all relevant information in a structured, commonly used format. The request is typically initiated via an API endpoint, such as GET /api/v1/users/{id}/personal-data.

A minimal implementation must return:

{
  "userId": "u-7b9c2e",
  "email": "[email protected]",
  "name": "Alice Johnson",
  "consents": [
    {
      "id": "c-3a1f7d",
      "purpose": "marketing",
      "grantedAt": "2023-04-15T10:30:00Z",
      "expiresAt": "2024-04-15T10:30:00Z",
      "withdrawnAt": null,
      "source": "website",
      "legalBasis": "consent"
    }
  ],
  "processing-activities": [
    {
      "system": "customer-portal",
      "purpose": "account management",
      "retentionPeriod": "5 years",
      "legalBasis": "contract"
    },
    {
      "system": "analytics",
      "purpose": "user behavior tracking",
      "retentionPeriod": "18 months",
      "legalBasis": "legitimate interest"
    }
  ]
}

The API must be idempotent and include a X-Request-ID header for tracing. Errors are returned as HTTP 400–500 with a standard error body:

{
  "error": "data_not_found",
  "message": "No personal data found for user with ID 'u-7b9c2e'",
  "details": {
    "userId": "u-7b9c2e",
    "requestedAt": "2023-11-20T14:22:11Z"
  }
}

Update and Delete Data: Rectification and Erasure

Rectification requests update existing data. The system must support partial updates using PATCH /api/v1/users/{id}/personal-data.

PATCH /api/v1/users/{id}/personal-data
Content-Type: application/json

{
  "name": "Alice Smith",
  "preferences": {
    "newsletter": true,
    "smsNotifications": false
  }
}

The server must:

Erasure (right to be forgotten) requires cascading deletion and logging:

-- Trigger erasure via stored procedure
CALL erase_user_data(
  user_id := 'u-7b9c2e',
  reason := 'user_request',
  retention_period := '7 years'
);

This procedure:

A common failure: forgetting to remove user data from embedded systems (e.g., email templates, CRM, or third-party analytics). The system must maintain a data_subject_rights table that tracks every right execution and its status.

Consent must be granular, recorded, and auditable.

The system must support POST /api/v1/consents to record new consent:

POST /api/v1/consents
Content-Type: application/json

{
  "userId": "u-7b9c2e",
  "purpose": "marketing",
  "source": "mobile-app",
  "legalBasis": "consent",
  "grantedAt": "2023-11-20T15:10:00Z",
  "expiresAt": "2024-11-20T15:10:00Z",
  "consentText": "I agree to receive marketing emails and push notifications."
}

The API returns 201 Created and includes a Location header. The consents table must enforce uniqueness per userId + purpose.

A critical failure: expiresAt is NULL when purpose is marketing but source is email. The system must validate that expiresAt is set when legalBasis is consent.

Withdrawal of consent must be tracked and propagated:

PATCH /api/v1/consents/consent-id
{
  "withdrawnAt": "2023-11-21T09:30:00Z",
  "reason": "user requested in settings"
}

The system must:

A common pattern: a consent_version table with id, userId, purpose, version, effectiveFrom, validUntil, withdrawnAt, updatedBy.

Each data processing activity must reference a legal basis: consent, contract, legal obligation, public task, or legitimate interest.

The system must support a processing-activity table with a legalBasis field and a legalBasisDetails JSON column.

{
  "purpose": "user profile enrichment",
  "system": "data-warehouse",
  "legalBasis": "legitimate interest",
  "details": {
    "justification": "improves user experience and personalization",
    "balancingTest": {
      "userInterest": "high",
      "businessInterest": "medium",
      "impactOnUser": "moderate",
      "riskOfInfringement": "low"
    }
  }
}

Failure mode: legalBasis is stored as a string ("consent") but no validation is done. A missing details field causes downstream systems to fail when generating privacy notices.

Recording and Publishing Processing Activities

The GDPR requires a record of all processing activities. This record must be available upon request and updated regularly.

Processing Activity Schema

The system must maintain a processing_activities table with these fields:

Automating the Record

Use a cron job to generate the record in JSON and HTML formats:


# /etc/systemd/system/gdpr-process-activities.service

[Unit]
Description=Generate GDPR Processing Activities Record
After=network.target

[Service]
Type=oneshot
ExecStart=/opt/gdpr/bin/generate-processing-activities-record.sh
User=app
Group=app
Environment=NODE_ENV=production

[Install]
WantedBy=timers.target

The script:

#!/bin/bash
set -euo pipefail

OUTPUT_DIR="/var/gdpr/processing-activities"
DATE=$(date +%Y-%m-%d)

mkdir -p "$OUTPUT_DIR/$DATE"

# Export as JSON
psql -c "SELECT json_agg(row_to_json(t)) FROM (
  SELECT * FROM processing_activities
) t" > "$OUTPUT_DIR/$DATE/activities.json"

# Export as HTML
curl -X POST \
  -H "Content-Type: application/json" \
  -d @- \
  http://localhost:3000/api/generate-html \
  < "$OUTPUT_DIR/$DATE/activities.json" \
  > "$OUTPUT_DIR/$DATE/activities.html"

# Archive and publish
tar -czf "$OUTPUT_DIR/$DATE/activities.tar.gz" -C "$OUTPUT_DIR/$DATE" .
aws s3 cp "$OUTPUT_DIR/$DATE/activities.tar.gz" s3://company-gdpr/records/

The output is accessible at https://gdpr.company.com/records/{date}/.

Audit Trail for Processing Activities

Each change to a processing activity must be logged in processing_activity_audit_log:

CREATE TABLE processing_activity_audit_log (
  id BIGSERIAL PRIMARY KEY,
  activityId UUID NOT NULL,
  changedBy UUID,
  action VARCHAR(20) NOT NULL, -- 'create', 'update', 'delete', 'audit'
  oldData JSONB,
  newData JSONB,
  timestamp TIMESTAMPTZ DEFAULT NOW(),
  metadata JSONB
);

A POST /api/v1/audits/processing-activities endpoint triggers an audit:

POST /api/v1/audits/processing-activities
{
  "activityId": "a-1f9b2c",
  "auditorId": "u-3a4b5c",
  "notes": "Reviewed for compliance with GDPR Article 30",
  "status": "verified"
}

This creates a record in processing_activity_audit_log and updates processing_activities.status to audited.

Ensuring Accountability

Accountability requires traceability across systems and events.

Data Protection Impact Assessments (DPIAs)

The system must support triggering a DPIA for high-risk processing.

POST /api/v1/dpias
{
  "title": "New user onboarding workflow",
  "description": "End-to-end onboarding with identity verification",
  "riskLevel": "high",
  "processingActivities": [
    "a-1f9b2c",
    "a-4d5e6f"
  ],
  "dataController": "[email protected]",
  "dueDate": "2024-03-31"
}

The system stores the DPIA in dpias table and generates a dpias_progress table tracking steps: initiated, dataMapping, riskAssessment, consultation, approval, review.

Data Flow Diagrams and Metadata

For each data processing activity, the system must generate and store a data flow diagram (DFD) in a standard format.

{
  "id": "dfd-7a8b9c",
  "title": "User profile sync across systems",
  "description": "Sync user data from customer portal to analytics and CRM",
  "source": "customer-portal",
  "destination": ["analytics", "crm"],
  "dataElements": [
    {
      "name": "userId",
      "type": "UUID",
      "source": "customer-portal",
      "target": ["analytics", "crm"],
      "sensitivity": "high",
      "retention": "5 years",
      "processingPurpose": "user analytics"
    }
  ],
  "metadata": {
    "author": "[email protected]",
    "createdAt": "2023-11-20T12:00:00Z",
    "lastUpdated": "2023-11-20T12:15:00Z"
  }
}

The system must expose a GET /api/v1/diagrams/{id} endpoint, returning the DFD in JSON or SVG.

Monitoring and Testing Compliance

Compliance must be tested and monitored continuously.

Compliance Test Suite

A test-compliance.sh script runs before deployment:

#!/bin/bash
set -euo pipefail

# Test 1: All consent records have valid legal basis
psql -c "
  SELECT count(*) FROM consents
  WHERE legal_basis NOT IN ('consent', 'contract', 'legal obligation', 'public task', 'legitimate interest')
" | grep -q '0'

# Test 2: All processing activities have at least one data transfer country
psql -c "
  SELECT count(*) FROM processing_activities
  WHERE data_transfer_countries IS NULL OR array_length(data_transfer_countries, 1) = 0
" | grep -q '0'

# Test 3: Data subject rights are logged
psql -c "
  SELECT count(*) FROM data_subject_rights
  WHERE right = 'erase' AND status = 'pending'
" | grep -q '0'

# Fail if any test fails
if ! (test 1 && test 2 && test 3); then
  echo "Compliance tests failed"
  exit 1
fi

The test suite is run as part of CI, with failures reported in a compliance-report.json file.

Compliance Monitoring

A compliance-monitoring service runs every 15 minutes, checking:

It logs results to a compliance_check_log table and sends alerts via POST /api/v1/alerts.

Failure mode: the monitoring service runs but does not detect a missing_audit — because the lastAudited field is null but not NULL in the database. Use IS NULL in the query, not = NULL.

Summary

GDPR compliance for engineers is not a checklist but a system of traceable, testable, and monitored data flows. From consent to erasure, from processing records to accountability, every action must be logged, versioned, and verifiable. The system must not just store data but prove that it processes data according to law.

This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.