One Hidden Failure in Learning Apps: Drifting Content IDs
A lot of teams version their lesson text but forget to lock content identifiers. Then analytics, bookmarks, and completion history point to the wrong content after updates.
Step 1: Treat content IDs as immutable contracts
type Lesson = {
lessonId: string; // never reused
version: number; // can change
title: string;
body: string;
};
Step 2: Add migration tables instead of in-place rewrites
CREATE TABLE lesson_migration (
old_id TEXT PRIMARY KEY,
new_id TEXT NOT NULL,
changed_at TIMESTAMP NOT NULL
);
Step 3: Resolve user state through migration at read time
def resolve_lesson_id(raw_id, migration_map):
return migration_map.get(raw_id, raw_id)
Pitfalls to avoid
- Recycling old IDs for different content.
- Backfilling analytics without a migration lookup.
- Shipping content edits before updating search indexes.
Validation checklist
- Historical completion records still open the intended lesson.
- Analytics trend lines remain continuous across content updates.
- Migration map is tested with multi-hop updates.