You write code that processes user data. You store it in databases, send it through APIs, log it in monitoring systems. But have you ever taken GDPR training? Most programmers design system architectures, choose data storage methods, and decide on security mechanisms - and all of this directly impacts compliance with data protection regulations. The problem is that GDPR is still treated as “a matter for lawyers and DPOs,” while it’s actually the technical decisions of developers that determine whether a company will be exposed to fines reaching 20 million euros or 4% of annual turnover.
Quick navigation
- GDPR is not just a topic for the legal department - programmers make key architectural decisions affecting personal data protection
- Privacy by Design must be in the code - principles of data minimization, pseudonymization, and encryption should be implemented from the first line of code
- 10 most common mistakes are logging personal data, storing unnecessary data, lack of encryption, hardcoded tokens, lack of data deletion mechanism
- Right to be forgotten is an architectural challenge in the world of microservices, distributed databases, and backups
- Penalties are real - in 2023 alone, GDPR fines exceeding 2.1 billion euros were imposed, and most violations had a technical basis
- EITT has trained over 500+ experts in personal data protection, conducting 2,500+ trainings with a rating of 4.8/5
Why GDPR concerns programmers
When we talk about GDPR, most companies think of lawyers, Data Protection Officers, and HR departments. Meanwhile, programmers are the first line of defense - they decide whether data will be encrypted, whether logs will contain personal data, whether users will be able to delete their accounts.
Technical decisions = GDPR compliance decisions
By choosing how to store passwords, you decide on the security of users’ personal data. By designing the database structure, you determine how easy it will be to execute a data deletion request (right to be forgotten). By implementing the logging system, you establish whether the company will be exposed to personal data leakage in production logs.
Let’s look at a concrete example: a developer decides to save full HTTP request objects in the error monitoring system. Requests contain user data - email, phone number, sometimes even social security number. The monitoring system stores this data for 90 days, without encryption, accessible to the entire technical team. That’s already three GDPR violations:
- Lack of data minimization - user ID would be sufficient for debugging
- Lack of appropriate security - personal data stored without encryption
- Excessive access - everyone on the team sees personal data, even though not everyone needs it
Responsibility doesn’t lie only with the DPO
Many programmers believe that the Data Protection Officer or legal department is responsible for GDPR. This is a misconception. The DPO supervises compliance and advises, but technical teams implement solutions. If your code causes a personal data leak, the company is liable - regardless of whether the DPO knew about the problem.
In practice, this means that every developer must know:
- What is personal data (not just name and surname - also IP, cookie ID, biometrics)
- What are the legal bases for processing (consent, contract, legitimate interest)
- What rights users have (access, rectification, deletion, portability)
- What security measures are required (encryption, access control, audit)
Privacy by Design and Privacy by Default in developer practice
Privacy by Design is not a buzzword - it’s concrete principles for designing systems that protect personal data “out of the box.” In practice, this means that data protection must be built into the application architecture from the very beginning, not added later as a “patch.”
Data minimization in code
The principle of minimization says: collect only the data that is actually needed to achieve the purpose. In practice, this means asking “do I really need this field?” for every data model.
Wrong:
// User registration - collecting everything "just in case"
const user = {
email,
password,
firstName,
lastName,
phoneNumber,
dateOfBirth,
address,
socialSecurityNumber,
mothersMaidenName,
favoriteColor
}
Correct:
// Collecting only what's needed to provide the service
const user = {
email, // needed for login
password, // needed for authentication
firstName // needed for personalization
}
// Rest of the data collected only if and when needed
Pseudonymization and anonymization
If you can process data in a way that makes it impossible to identify a person without additional information - do it. In practice, this often means using IDs instead of personal data.
Wrong:
-- Activity log with personal data
INSERT INTO activity_log (user_email, action, timestamp)
VALUES ('john.doe@example.com', 'login', NOW());
Correct:
-- Activity log with user ID
INSERT INTO activity_log (user_id, action, timestamp)
VALUES (uuid_v4(), 'login', NOW());
Data encryption at rest and in transit
Personal data should be encrypted both during transmission (TLS/SSL) and storage. This is basic, but many applications still don’t do this.
Minimum:
- HTTPS throughout the application (not just on the login page)
- Hashed passwords (bcrypt, argon2 - never MD5 or SHA-1)
- Sensitive data encrypted in the database (payment cards, health data)
- Database backup encryption
Example of encrypting sensitive fields:
from cryptography.fernet import Fernet
class EncryptedField:
def __init__(self, key):
self.cipher = Fernet(key)
def encrypt(self, data: str) -> bytes:
return self.cipher.encrypt(data.encode())
def decrypt(self, encrypted_data: bytes) -> str:
return self.cipher.decrypt(encrypted_data).decode()
# Use in model
class User:
email = models.EmailField()
encrypted_phone = models.BinaryField() # Encrypted phone number
Access control at code level
Not everyone in the company needs access to all personal data. The “need to know” principle should be implemented at the application level.
Example:
@require_permission('view_user_personal_data')
def get_user_details(user_id):
user = User.objects.get(id=user_id)
return {
'email': user.email,
'phone': user.phone, # Visible only to those with permission
'address': user.address
}
@require_permission('view_user_public_data')
def get_user_public_profile(user_id):
user = User.objects.get(id=user_id)
return {
'username': user.username,
'avatar_url': user.avatar_url
# No personal data
}
10 most common developer mistakes violating GDPR
1. Logging personal data in plaintext
Problem: Production logs contain users’ personal data - email, first name, last name, and sometimes even passwords or payment card numbers.
Solution: Never log personal data. If you must - log only the user ID, and retrieve data from the database when needed.
# WRONG
logger.info(f"User {user.email} logged in from {user.ip_address}")
# CORRECT
logger.info(f"User {user.id} logged in", extra={'user_id': user.id})
2. Storing unnecessary data
Problem: “It might come in handy” - the application collects more data than it needs, violating the principle of minimization.
Solution: Before adding a field to a form, ask: “Is this absolutely necessary for providing the service?” If not - don’t collect it.
3. Lack of encryption for sensitive data
Problem: Personal data (especially special categories - health data, biometric) stored in plaintext.
Solution: Encrypt sensitive data in the database. Use application-level encryption, not just TDE (Transparent Data Encryption).
4. Hardcoded credentials and tokens
Problem: API keys, database passwords, tokens saved in source code - often end up in Git repositories, from where they can leak.
Solution: Use environment variables, secret managers (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault).
# WRONG
DB_PASSWORD = "super_secret_123"
# CORRECT
DB_PASSWORD = os.environ.get('DB_PASSWORD')
5. Lack of data deletion mechanism
Problem: The application has no user account deletion function or “deletion” is just a soft delete (flag deleted=true).
Solution: Implement real data deletion + procedures for deleting data from backups, logs, cache.
6. Passing personal data in URLs
Problem: Personal data in query params or path parameters - they end up in server logs, proxy, browsers.
Solution: Personal data only in POST/PUT request body, never in URL.
// WRONG
fetch(`/api/users?email=${email}&phone=${phone}`)
// CORRECT
fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ email, phone })
})
7. Lack of storage time limits
Problem: Data stored “forever” - old accounts of inactive users, logs from 5 years ago.
Solution: Implement data retention policies - automatic deletion of old data after a specified time.
# Cron job deleting inactive accounts after 2 years
def cleanup_inactive_accounts():
two_years_ago = datetime.now() - timedelta(days=730)
User.objects.filter(
last_login__lt=two_years_ago,
account_deleted=False
).delete()
8. Sharing test databases with real data
Problem: Developers use production database copies in dev/test/staging environments.
Solution: Anonymize data before copying to non-prod environments or use synthetic test data.
-- Data anonymization before export
UPDATE users SET
email = CONCAT('user_', id, '@example.com'),
phone = CONCAT('+48', LPAD(id, 9, '0')),
first_name = 'Test',
last_name = CONCAT('User', id);
9. Lack of user consent validation
Problem: The application processes data without checking whether the user has given consent (if consent is the legal basis).
Solution: Store consent information in the database + timestamp + consent text + method of expression.
class Consent(models.Model):
user = models.ForeignKey(User)
purpose = models.CharField(max_length=255) # e.g., "marketing"
granted = models.BooleanField()
granted_at = models.DateTimeField()
consent_text = models.TextField() # Consent text at the time of expression
ip_address = models.GenericIPAddressField() # Proof of consent expression
10. Lack of audit for access to personal data
Problem: No one knows who and when accessed a user’s personal data.
Solution: Log all operations on personal data - who, when, what data, for what purpose.
@audit_access('personal_data_view')
def view_user_profile(request, user_id):
user = User.objects.get(id=user_id)
# Automatically logged: request.user.id, user_id, timestamp, IP
return render(request, 'profile.html', {'user': user})
How to build GDPR-compliant applications - developer checklist
Planning and design phase
- Conduct Data Protection Impact Assessment (DPIA) if the application processes data on a large scale or special category data
- Identify all personal data that will be processed in the system
- Determine legal basis for each processing purpose (consent, contract, legal obligation, administrator’s interest)
- Design data model with minimization in mind - only necessary fields
- Plan architecture with separate databases/tables for different data categories (public, personal, sensitive)
Implementation phase
- Use HTTPS throughout the application - enforce redirect from HTTP to HTTPS
- Implement secure password hashing (bcrypt, argon2, min. 10 rounds)
- Encrypt sensitive data in the database (health data, biometric, financial)
- Don’t log personal data - only user IDs
- Validate and sanitize all inputs - protection against SQL injection, XSS
- Implement access control mechanisms - role-based or attribute-based access control
- Add rate limiting - protection against brute force and data scraping
- Use prepared statements - protection against SQL injection
- Implement secure session management - HTTPOnly, Secure, SameSite cookies
GDPR compliance functionalities
- Right of access - endpoint/function to download all user data in readable format (JSON, PDF)
- Right to rectification - ability to edit personal data by the user
- Right to erasure - account deletion function + all related data
- Right to data portability - data export in machine-readable format (JSON, CSV, XML)
- Right to restriction of processing - ability to block account without deleting data
- Right to object - ability to withdraw consent for processing
- Consent management - interface for expressing/withdrawing consents + audit log
- Data retention policies - automatic deletion of old/inactive data
Security and monitoring
- Implement audit logging - who, when, what data, what operation
- Configure alerting for suspicious activities - mass access to data, unusual queries
- Regularly update dependencies - scan vulnerabilities (Dependabot, Snyk)
- Conduct code review with security focus - check every PR for data leakage
- Test security - penetration testing, vulnerability scanning
- Prepare incident response plan - what to do in case of data breach
Documentation
- Document data processing - what data, for what purpose, how long, who has access
- Prepare privacy policy - in language understandable to users
- Document user consents - what the user accepted and when
- Maintain register of processing activities (if the company employs 250+ people or processes data on a large scale)
Deployment and operations
- Encrypt backups - personal data in backups is equally sensitive
- Limit access to production - only senior engineers, with audit log
- Anonymize data before copying to nonprod environments
- Monitor access logs - who and when logged into systems containing personal data
- Regularly conduct security audits of infrastructure
Right to be forgotten - architectural challenge
The right to erasure (colloquially “right to be forgotten”) is one of the most difficult GDPR requirements to implement in modern, distributed systems. In theory simple: user requests data deletion, you delete data. In practice: where is all this user’s data?
Challenge 1: Distributed databases
In microservices architecture, user data can be distributed across dozens of services, each with its own database. Deleting a user requires orchestrating operations across all services.
Solution: Event-driven data deletion
# User service publishes event
def delete_user(user_id):
user = User.objects.get(id=user_id)
user.delete()
# Publish event to all services
publish_event('user.deleted', {
'user_id': user_id,
'timestamp': datetime.now().isoformat()
})
# Each service subscribes to event and deletes its data
@subscribe('user.deleted')
def handle_user_deleted(event):
user_id = event['user_id']
Order.objects.filter(user_id=user_id).delete()
Review.objects.filter(user_id=user_id).delete()
# ...
Challenge 2: System logs
Logs may contain personal data (if you didn’t follow previous advice) and are usually immutable. How to delete data from logs?
Solution: Don’t log personal data
If you’ve already logged it - you must:
- Identify all log files containing user data
- Remove relevant lines from logs (difficult in systems like Elasticsearch, Splunk)
- Remove data from log archives
Better: don’t log personal data from the start. Use only user IDs.
Challenge 3: Database backups
Backups may contain deleted user data for months or years (depending on retention policy).
Solution: Backup encryption + exception list
- Encrypt backups with a key that can be rotated
- Maintain a list of “deleted users” - when restoring from backup, check this list and don’t restore data of those users
- Alternatively: don’t restore personal data from backups older than X months
Challenge 4: Cache and queues
User data may be in Redis, Memcached, RabbitMQ, Kafka. How to delete it?
Solution: Cache invalidation + time-to-live
def delete_user(user_id):
# Delete from database
User.objects.filter(id=user_id).delete()
# Delete from cache
cache.delete(f'user:{user_id}')
cache.delete(f'user_profile:{user_id}')
cache.delete(f'user_orders:{user_id}')
# Publish event to queues
publish_event('user.deleted', {'user_id': user_id})
Set TTL on cache - personal data should not be cached for a long time.
Challenge 5: Third-party services
If you pass data to external services (analytics, CRM, email marketing), you must ensure data deletion there too.
Solution: Data Processing Agreements + API deletions
- Sign DPA (Data Processing Agreement) with each processor
- Implement automatic data deletion through processor APIs
- Document the deletion process
def delete_user_from_third_parties(user_id, email):
# Google Analytics
ga_client.user_deletion_request(user_id)
# Mailchimp
mailchimp_client.delete_member(email)
# Intercom
intercom_client.delete_user(user_id)
# Segment
segment_client.track_deletion(user_id)
Best practice: “Tombstone” pattern
Instead of immediate deletion, mark the user as “to be deleted,” then run an asynchronous process that deletes data from all systems.
class User(models.Model):
# ... user fields
deleted_at = models.DateTimeField(null=True)
deletion_completed = models.BooleanField(default=False)
def mark_user_for_deletion(user_id):
user = User.objects.get(id=user_id)
user.deleted_at = datetime.now()
user.save()
# Launch asynchronous task
delete_user_data_task.delay(user_id)
@celery_app.task
def delete_user_data_task(user_id):
# Delete from all services
delete_from_service_a(user_id)
delete_from_service_b(user_id)
delete_from_cache(user_id)
delete_from_third_parties(user_id)
# Mark as completed
User.objects.filter(id=user_id).update(deletion_completed=True)
Penalties for GDPR violations - examples
Penalties for GDPR violations are not an abstract threat - they are real financial and reputational consequences that have affected hundreds of companies in Europe. The maximum fine is 20 million euros or 4% of annual global turnover (whichever is higher).
Largest GDPR fines in history
Amazon (2021): 746 million euros Violation: Illegal processing of users’ personal data for advertising purposes. Amazon did not obtain proper consent for ad personalization based on user behavior.
Lesson for developers: Implement granular consent control - users must be able to express separate consent for each processing purpose.
Meta (Facebook, 2023): 1.2 billion euros Violation: Transferring data of European users to the US without proper safeguards. Lack of compliance with the CJEU ruling in Schrems II.
Lesson for developers: If you transfer data outside the EEA - you must have the appropriate legal basis (Standard Contractual Clauses) and technical measures (encryption, pseudonymization).
Google (2019): 50 million euros Violation: Lack of transparency and improper obtaining of consents for data processing for ad personalization purposes in the Android system.
Lesson for developers: Consent must be informed, specific, and unambiguous. “I agree to everything” is not valid consent.
British Airways (2020): 22.5 million euros Violation: Data breach of 400,000 customers (including payment card data) through an attack on the website. Insufficient security measures.
Lesson for developers: Basic security is not enough. You must implement defense in depth - multi-layered data protection.
H&M (2020): 35.3 million euros Violation: Excessive employee monitoring - detailed records of conversations with supervisors about private life, stored on a shared server.
Lesson for developers: The principle of minimization also applies to employee data. Don’t collect and store more than necessary.
Violations with technical basis
Most GDPR fines result from technical errors that could have been avoided:
- Lack of encryption - British Airways, Marriott
- Insufficient access control - H&M, Danish taxi company
- Lack of consent validation - Google, Meta
- Data transfer outside EEA without safeguards - Meta, Google
- Excessive data collection - TikTok, Clearview AI
- Lack of data deletion mechanism - various e-commerce companies
How to avoid penalties?
- Treat security as a priority from the first line of code
- Implement all user rights (access, deletion, portability, rectification)
- Document everything - what data, for what purpose, on what basis, how long
- Team training - every developer must understand GDPR basics
- Audit regularly - code review, penetration testing, vulnerability scanning
- Have an incident response plan - what to do in case of data breach (72h to report to supervisory authority)
GDPR training for development teams at EITT
GDPR is not just regulations and paragraphs - it’s concrete technical challenges that developers face every day. EITT has trained over 500+ IT experts, conducting 2,500+ trainings with a rating of 4.8/5. Our GDPR training for programmers is not a boring legal lecture - it’s practical workshops showing how to implement data protection in real code.
What does the training cover?
Module 1: GDPR for developers - basics
- What is personal data in the context of web and mobile applications
- Legal bases for processing - when you need consent and when you don’t
- User rights and how to implement them technically
- Penalties for violations - case studies from the IT industry
Module 2: Privacy by Design in practice
- Designing GDPR-compliant architecture
- Data minimization at the data model level
- Pseudonymization and anonymization - when, how, why
- Data encryption at rest and in transit - best practices
- Access control at the application level
Module 3: Secure coding for GDPR
- How not to log personal data (and what instead)
- Secure storage of passwords and tokens
- Protection against attacks - SQL injection, XSS, CSRF in the context of GDPR
- User consent validation in code
- Implementation of data retention policies
Module 4: User rights - implementation
- Right of access - how to build “download my data” function
- Right to erasure - architectural challenge in microservices
- Right to data portability - formats, API, export
- Right to rectification and restriction of processing
- Audit of access to personal data
Module 5: Incident response and audits
- What to do in case of data breach - 72h to report
- How to prepare incident response plan
- Monitoring and detecting violations
- Penetration testing and vulnerability scanning
- Code review from GDPR perspective
Module 6: GDPR in team practice
- Git security - how not to commit personal data and credentials
- Dev/test/staging environments - data anonymization
- Third-party services and Data Processing Agreements
- Cloud compliance - AWS, Azure, GCP in the context of GDPR
- GDPR checklist for code review
Who is this training for?
- Developers (junior, mid, senior) - who write code processing personal data
- Tech Leads - responsible for team compliance
- Architects - designing GDPR-compliant systems
- DevOps Engineers - securing infrastructure
- QA/Testers - testing GDPR compliance
- Product Managers - defining functional requirements considering GDPR
Training benefits
- Avoid penalties - GDPR fines reach billions of euros, most due to technical errors
- Speed up development - instead of rewriting code, start with GDPR compliance
- Increase user trust - data protection is a competitive advantage
- Prepare for audits - you’ll know what auditors check
- Develop competencies - security and privacy are must-have skills in 2026
- Certificate - confirm your competencies on CV
Training format
- On-site training - 2 days of intensive workshops (16h)
- Online training - 4 sessions of 4h each (flexible schedules)
- In-house workshops - tailored to your team’s needs and technology stack
- Code review as part of training - we’ll analyze your code from a GDPR perspective
Why EITT?
EITT has trained 500+ experts and conducted 2,500+ trainings with a rating of 4.8/5. Our trainers are not just lawyers - they are practitioners with development experience who understand your challenges. We conduct training based on real case studies, code examples, and hands-on exercises.
Check available GDPR training dates or contact us and let’s discuss a custom program for your team.
FAQ
Do I need to know GDPR as a developer?
Yes. If your code processes users’ personal data (email, name, IP, cookie ID, etc.), you make decisions affecting the company’s GDPR compliance. Choosing how to store passwords, designing database structure, implementing logging system - all of this directly impacts personal data protection. The DPO and legal department can advise, but you implement solutions. Lack of GDPR knowledge does not exempt from responsibility.
What data is considered personal data?
Broader than you think. Personal data is any information allowing identification of a natural person - directly or indirectly. This is obviously name, surname, email, phone, social security number. But also: IP address, cookie ID, user ID (if it can be linked to a person), geolocation, device fingerprint, biometric data, facial photos, purchase history, browsing history. Rule: if it’s possible to determine a person’s identity based on this data (independently or by combining with other data) - it’s personal data.
Do I have to delete data from backups?
Technically GDPR requires data deletion “in all systems,” but provides an exception for backups - provided they are encrypted, secured, and not used for normal data processing. In practice: you must ensure that when restoring from backup, you won’t restore data of users who requested deletion. Solution: maintain a list of “deleted users” and don’t restore their data from backups or don’t restore personal data from backups older than X months.
What are the consequences of improper logging of personal data?
If logs contain personal data and there’s a breach (e.g., through unauthorized access to the monitoring system), it’s a GDPR violation. The penalty can be up to 10 million euros or 2% of annual turnover. Example: a company logged full HTTP requests containing payment card data. Logs leaked through unsecured Elasticsearch. Result: GDPR fine + user lawsuits + destroyed reputation. Solution: don’t log personal data - only user IDs, which you can expand to data when needed (with audit log).
Does user consent to everything solve the compliance problem?
No. First, consent must be freely given, specific, informed, and unambiguous. “I agree to everything” is not valid consent. Second, even if you have consent, you must follow principles: data minimization, security, limited storage time. Third, the user can withdraw consent at any time - you must have a mechanism to handle this. And fourth, for some purposes (e.g., contract performance) consent is not needed - you can process data on another legal basis. In short: consent is not a universal solution.
How long can I store personal data?
Only as long as necessary to achieve the purpose for which it was collected. If the user has an account - you can store data for the duration of the relationship. If you’re performing a contract - for the duration of the contract + time required by regulations (e.g., 5 years for accounting documentation). If you have consent for marketing - until its withdrawal. After this time, you must delete data or anonymize it (so that a person cannot be identified). In practice: implement data retention policies - automatic deletion of old data after a specified time.
Personal data security is not a luxury, but a standard that should be built into every line of code. GDPR is not an obstacle in development - it’s a framework that helps build applications worthy of user trust. At EITT, we’ll train your team so that GDPR stops being an abstract threat and becomes a natural part of the software development process. Check our trainings or contact us - let’s talk about how we can help your team.
Read Also
- GDPR and Employee Pay Data – How to Reconcile Transparency with Privacy?
- Preparing Your Organization for Compliance with Data Protection Regulations Including GDPR
- GDPR in Practice - Data Protection Reform in Healthcare
Develop Your Skills
This article is related to the training Practical about RODO/GDPR - EU data protection reform. Check the program and sign up to develop your skills with EITT experts.