Back to Product Decisions
Technical Strategy

JSON-in-TEXT Over a Normalized Schema

Atlas PM Agent stores complex artifacts (PRD sections, persona empathy maps, story acceptance criteria, RICE scores). The schema could either normalize each into proper relational tables or keep them as JSON inside TEXT columns.

THE DECISION

Stored complex artifacts as JSON-in-TEXT columns, parsed at read time. Versioning was added only on PRDs, where the stakes are highest.

Reasoning

Schema was changing weekly while the artifact pipeline was being designed — every normalized change would have meant a manual Turso migration

libSQL over HTTP doesn't support multi-statement transactions, so 'one column, one row, one parse' avoided a class of partial-write bugs

Versioning every artifact would have doubled the table count for marginal value — PRDs change often and matter most, so they got the prd_versions table; backlog edits stayed unversioned for now

The backlog is consumed as a tree by the UI and the AI tools — it's almost always read whole, so JSON-in-TEXT actually matches the access pattern

Trade-offs

What I Gained

Fast schema iteration during 0→1

Fewer tables, fewer migrations, no partial-write windows

Read shape matches usage shape (whole-tree fetch)

What I Lost

Querying inside artifacts (e.g. 'find all stories with priority high') requires app-side filtering

No referential integrity inside JSON blobs

Backlog edits via chat aren't reversible — a known gap to close

Outcome

Shipped the seven-stage pipeline without ever blocking on a migration. The cost was real — adding any cross-artifact query means parsing in app code — but at this stage, schema velocity was worth more than query power.

Lessons Learned

JSON-in-TEXT is a 0→1 accelerant, not a destination. It's the right call when the schema is still moving and queries are predictable; plan the migration to relational columns the moment either of those changes.