3. Data Models and Query Languages
3.6 Production failure catalog for this chapter
| Symptom | Underlying modeling decision |
|---|---|
| Page makes 200 DB queries to render 50 rows | N+1, from an ORM or a GraphQL resolver without batching |
| Rename a company → 400,000 stale copies of the old logo URL | Denormalized human-meaningful data with no update process |
| Two sources of the same relationship disagree | Many-to-many stored on both sides |
| Timeline shows a stale like count / old avatar | Fast-changing data was denormalized into a materialized view |
| One document is 14 MB and every read pulls all of it | Embedded a genuine one-to-many where the model only supports one-to-few |
| Migration locks the table and takes the site down | Schema-on-write UPDATE rewriting every row |
Read code full of if (!user.first_name) forever | Schema-on-read with no migration and no shape observability |
| Recursive SQL query never terminates | Cycles in the graph; no visited-set, no depth bound |
| One graph query melts the cluster | Supernode with millions of edges |
| Public API DoS'd by one clever query | GraphQL without depth/complexity limits |
| Rebuilt projection produces different numbers | Non-deterministic event processing (external lookup, now()) |
| Rebuild sends 200,000 confirmation emails | Side effects inside a replayable projection |
| Cannot honour a GDPR erasure request | Immutable multi-user event log; no crypto-shredding designed in |
| Analytics dashboard is wrong after a source rename | Schema drift with schema-on-read semantics in the pipeline |
| Model works in training, garbage in production | One-hot encoder not persisted — training/serving skew |