GDPR Compliance Cheat Sheet
Outlines core GDPR principles, data subject rights, lawful bases for processing, and breach notification requirements for engineering teams.
Core Principles (Article 5)
The foundational principles that all GDPR processing must satisfy.
- Lawfulness, fairness, transparency- Processing must have a legal basis and be clearly communicated to data subjects
- Purpose limitation- Data collected for one purpose cannot be reused for an incompatible purpose
- Data minimization- Collect only the data necessary for the stated purpose
- Accuracy- Personal data must be kept accurate and up to date
- Storage limitation- Data must not be kept longer than necessary; define retention periods
- Integrity and confidentiality- Appropriate security (encryption, access control) must protect the data
- Accountability- Controllers must be able to demonstrate compliance, not just achieve it
Lawful Bases for Processing (Article 6)
At least one basis must apply before processing personal data.
- Consent- Freely given, specific, informed, and unambiguous agreement, revocable at any time
- Contract- Processing necessary to perform a contract with the data subject
- Legal obligation- Required to comply with a law the controller is subject to
- Vital interests- Necessary to protect someone's life
- Public task- Necessary for a task carried out in the public interest or official authority
- Legitimate interests- Necessary for the controller's legitimate interest, balanced against the data subject's rights
Data Subject Rights (Chapter III)
Rights individuals can exercise over their personal data.
- Right to access (Art. 15)- Obtain confirmation and a copy of data being processed about them
- Right to rectification (Art. 16)- Correct inaccurate or incomplete personal data
- Right to erasure (Art. 17)- 'Right to be forgotten' - deletion when data is no longer necessary or consent is withdrawn
- Right to restrict processing (Art. 18)- Limit how data is used while a dispute is resolved
- Right to data portability (Art. 20)- Receive data in a structured, machine-readable format to transfer elsewhere
- Right to object (Art. 21)- Object to processing based on legitimate interests or direct marketing
Implementing a Right-to-Erasure Endpoint
Example API pattern for handling GDPR deletion requests.
from datetime import datetime, timedeltadef handle_erasure_request(user_id): # 1. Verify the requester's identity before acting # 2. Check for legal retention obligations (e.g. tax records) if has_legal_retention_hold(user_id): anonymize_user(user_id) # anonymize instead of hard delete else: delete_user_data(user_id) # 3. Propagate deletion to downstream processors/backups queue_deletion_for_processors(user_id) # 4. Respond within one month (Art. 12(3)), extendable by two more # months for complex requests deadline = datetime.utcnow() + timedelta(days=30) log_erasure_request(user_id, deadline)
Breach Notification Rules (Art. 33-34)
Timelines and thresholds for reporting a personal data breach.
- 72-hour rule- Notify the supervisory authority within 72 hours of becoming aware of a breach, where feasible
- Risk assessment- No notification required if the breach is unlikely to result in risk to individuals
- High-risk breaches- Must also notify affected data subjects directly and without undue delay
- Documentation- All breaches must be logged internally, even if not reportable to the authority
International Data Transfer Mechanisms (Chapter V)
Legal mechanisms required before personal data leaves the EEA for a non-adequate country.
- Adequacy decision- European Commission has determined the destination country provides essentially equivalent protection; no further safeguard needed
- Standard Contractual Clauses (SCCs)- EC-approved contract templates binding the importer to GDPR-equivalent obligations; most common mechanism post-Schrems II
- Binding Corporate Rules (BCRs)- Internal intra-group rules approved by a lead supervisory authority for multinational transfers
- Transfer Impact Assessment (TIA)- Required alongside SCCs to evaluate whether destination-country surveillance laws undermine the contractual safeguards
- Derogations (Art. 49)- Narrow exceptions (explicit consent, contract necessity) usable only for occasional, non-repetitive transfers
DPIA Trigger Screening Logic
Article 35 requires a Data Protection Impact Assessment before processing likely to result in high risk; encode the screening as a repeatable check.
def dpia_required(processing): high_risk_triggers = [ processing.get('systematic_profiling', False), processing.get('large_scale_special_category_data', False), # Art. 9 data processing.get('systematic_public_monitoring', False), processing.get('automated_decision_with_legal_effect', False), processing.get('large_scale_processing', False) and processing.get('vulnerable_subjects', False), processing.get('new_technology', False), # e.g. biometric identification processing.get('data_matching_or_combining', False), ] # WP29 guidance: two or more criteria met is a strong indicator a DPIA is needed score = sum(bool(t) for t in high_risk_triggers) return score >= 2def run_dpia_workflow(processing): if dpia_required(processing): return { 'status': 'DPIA_REQUIRED', 'steps': ['describe_processing', 'assess_necessity_proportionality', 'identify_risks_to_individuals', 'identify_mitigations', 'consult_dpo', 'consult_authority_if_residual_high_risk'] } return {'status': 'DPIA_NOT_REQUIRED', 'rationale_logged': True}
Data Processing Agreement Clauses (Art. 28)
Mandatory contractual terms when engaging a processor (e.g. a cloud vendor or SaaS subprocessor).
- Processing scope and duration- Subject matter, nature, purpose, and duration of processing must be documented
- Instructions-only processing- Processor may only act on documented controller instructions, including for international transfers
- Confidentiality commitment- Personnel authorized to process data must be bound by confidentiality obligations
- Security measures (Art. 32)- Processor must implement appropriate technical and organizational measures
- Sub-processor authorization- General or specific written authorization required before engaging sub-processors, with flow-down of equivalent obligations
- Breach notification duty- Processor must notify the controller without undue delay after becoming aware of a personal data breach
- Deletion or return of data- At contract end, processor must delete or return all personal data, and delete existing copies unless law requires storage
- Audit rights- Processor must make available information demonstrating compliance and allow audits/inspections
Record of Processing Activities (ROPA) Schema (Art. 30)
Structured record every controller of a certain size must maintain and produce on request from a supervisory authority.
processing_activity: name: "Customer support ticketing" controller: "Example Corp" dpo_contact: "[email protected]" purpose: "Resolve customer support requests and track SLAs" lawful_basis: "contract" data_categories: - "contact details" - "support ticket content" data_subject_categories: - "customers" recipients: - name: "Zendesk" role: "processor" dpa_signed: true international_transfers: - destination: "United States" mechanism: "SCCs" tia_completed: true retention_period: "3 years after ticket closure" security_measures: - "encryption at rest" - "role-based access control" last_reviewed: "2026-01-15"
Reversible Pseudonymization for Analytics Pipelines
Art. 25 privacy-by-design technique: separate identifying tokens from analytical data so re-identification requires a protected lookup step.
import hmac, hashlib, osPSEUDONYM_KEY = os.environ['PSEUDONYM_HMAC_KEY'] # stored in KMS, rotated periodicallydef pseudonymize(user_id: str) -> str: # Deterministic per key version so joins across tables still work, # but the raw user_id cannot be derived without the key return hmac.new(PSEUDONYM_KEY.encode(), user_id.encode(), hashlib.sha256).hexdigest()def build_analytics_row(raw_event): return { 'subject_token': pseudonymize(raw_event['user_id']), # safe for the analytics warehouse 'event_type': raw_event['event_type'], 'timestamp': raw_event['timestamp'], # raw_event['user_id'] and other direct identifiers are dropped here }# Re-identification (e.g. to serve an Art. 15 access request) requires a# separate, access-controlled lookup table mapping token -> user_id,# which itself should be encrypted and audit-logged on every read
Build 'privacy by design' into schema migrations: tag personal-data columns with a retention policy and TTL at creation time, so automated purging runs from day one instead of becoming a manual audit project years later.