2026-05-12 · 5 min read
Why every question keeps its last 50 revisions
We built question revisions with a 50-snapshot cap. Here's why the cap is the load-bearing decision.
We just shipped question revisions. Every save creates a snapshot; the last 50 are kept; restoration is a single API call. The interesting design decision wasn’t the snapshot — it was the cap.
Why a cap exists
The naive version of revisions is “keep everything forever.” That’s how Git works, that’s how Google Docs works, and that’s the version most people expect. We don’t keep everything.
Three reasons:
Storage compounds. A typical question is maybe 2KB. A team that edits 20 questions a day for a year is ~14MB of revision data per question — small per question, but multiplied by tens of thousands of questions across thousands of organizations, the cost is real and the value of the 5000th revision is approximately zero.
Recovery doesn’t need it. The use case for revisions is “I edited this last Tuesday and I want to roll back” or “Sarah deleted half my answer and I want to see what was there.” Both are solved by the last 50 snapshots. Nobody wants revision #847 from eight months ago.
Storage bloat is silent debt. The day you realize you’re spending $400/mo on revision storage for content that gets restored ~3 times a year is the day you wish you’d capped it.
Why 50
50 is mostly arbitrary, but it’s arbitrary on purpose.
If you edit a single question 50 times in a row to fine-tune the answer, you’ll wrap around. In practice, nobody does this — the people we asked who edited the same question that many times were testing the feature, not iterating on content. 50 is enough for “rolling back to last week” and not so much that storage runs away.
The cap is exposed in the API response. Every revision lists index (where it is in the buffer) and oldestRetained (the boundary). If you actually need to keep more than 50, write an export hook into your own audit log. We won’t fight you.
How it works under the hood
Each save writes a row to QuestionRevision keyed by (questionId, createdAt). On insert, a trigger checks the count for that question and deletes the oldest row if count > 50. The diff between any two revisions is computed at request time, not stored — diff text is cheap, diff storage isn’t.
-- Simplified
CREATE TRIGGER cap_revisions_per_question
AFTER INSERT ON "QuestionRevision"
FOR EACH ROW EXECUTE FUNCTION evict_oldest_revision();
The eviction is idempotent: if you somehow end up with 53 revisions for a question (concurrent writes), the next save brings you back to 50.
What we didn’t ship
We didn’t ship per-org revision-retention configuration. Configuring this would be the obvious enterprise-feature ask, and we’ll add it when an enterprise customer needs more than 50. For everyone else, the cap is invisible.
We didn’t ship visual diff in the public API. The dashboard renders a side-by-side diff using diff-match-patch on the client; the API just returns the raw text of each revision. If you want to render your own diff UI, you have the data; if you want to use ours, sign into the dashboard.
We didn’t ship branching revisions. The model is linear — one chain per question. Branching revisions are useful for documentation systems that need to coexist across product versions; they’re overkill for FAQs and they would have doubled the model complexity.
What this is really about
A working principle of thefaqapp: every feature ships with the constraint that makes it sustainable to operate. “Revisions, capped at 50” is a feature. “Revisions, unlimited” is a tax on the next year.
The cap is the load-bearing decision.