Why Building Small Projects is the Best Way to Learn Programming
Forget endless tutorials — real skills are forged through building, breaking, and fixing your own small projects.
Learning to Code
May 20, 2025, 2:43 PM
4 min
The Problem: Schema Changes on 2TB Tables
-- The migration that would lock our users table for 47 minutes
ALTER TABLE auth_user ADD COLUMN last_active_at TIMESTAMP;
The Solution: Triple-Phase Migration
Phase 1: Add Nullable Column
# migration_0001.py
class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='user',
name='last_active_at',
field=models.DateTimeField(null=True),
)
]
Phase 2: Backfill with Django Batch Updates
# Backfill script
from django.db import transaction
with transaction.atomic():
for user in User.objects.iterator(chunk_size=5000):
User.objects.filter(pk=user.pk).update(
last_active_at=user.session_set.latest().created_at
)
Phase 3: Set NOT NULL
-- Final migration (run during low traffic)
ALTER TABLE auth_user ALTER COLUMN last_active_at SET NOT NULL;
Key Metrics
Approach | Downtime | CPU Spike |
---|---|---|
Direct ALTER TABLE | 47 min | 82% |
Triple-Phase | 0 | 23% |