Tradeoffs
These are consequences of the design present in the code. Alternatives below are comparisons, not claims that the team formally evaluated or rejected them.
| Current design | Benefit | Cost / when to revisit |
|---|---|---|
| Complete JSON snapshot on each save | Load one review body; retain earlier versions; shared domain types map directly to storage. | Repeated data grows history. Nested SQL is less convenient than typed columns. A TypeScript type change does not migrate old JSON; preserve read compatibility. |
| Synchronous review_index projection | Lists avoid full reviews; snapshot and projection commit together. | Every save also maintains derived fields. Changes to report rules need a backfill and agreement with fallback readers. |
| Command batches over HTTP | Named operations centralize role/state rules; multiple edits share one save request. | Large execute switch and an explicit wire contract. Commands are not stored as a replayable event log. |
| Optimistic version check | No editing session lock; a repeated request can return its accepted result without executing twice. | Concurrent editors can conflict. The client discards unsaved drafts on 409; it does not merge fields. |
| Client calculation and local computation drafts | Immediate feedback without a calculation request per cell. Column saves are explicit. | Draft, saved scenario, and saved Final are different states. UI blockers and server rules must agree. |
| Server-rendered initial data, API refreshes | First rows arrive with the page; matching client state avoids another fetch. | The same query has page and API callers. Changes to parsing, scope, or initial-data keys must cover both. |
| SQL row scope plus in-memory access checks | Filter visibility before data leaves the database; check individual loads and writes. | Both implementations need equivalent rules. Low-level store calls require an authorized caller. |
| Clerk metadata for staff scope | No duplicate staff/session table in Postgres. | Metadata and demo roster labels live outside review storage; deployment and tests must provision matching staff IDs. |
| Bound policy copies and explicit rule versions | Published tables and deployed code do not silently change a review’s bound calculation. | Historical versions remain supported. apply_rules updates implementation IDs, not the saved amount table. |
| Polling inbox/catalog/news | Reuses ordinary HTTP reads; no live-connection service. | Updates can lag by five minutes and multiple mounted surfaces may poll. |
| Local disk plus hosted object storage | Simple local setup; hosted PDFs survive across app instances. | Two adapters to test; hosted uploads need CORS, completion validation, and orphan-object cleanup outside review saves. |
| CSS appearance attributes and deferred font loading | One shell supports multiple layouts and palettes; only used faces need to download. | Large CSS surface and boot-script compatibility. Palette, layout, and hydration changes need browser verification. |
How to assess a change
Preserve correctness, authorization, and data compatibility first. Measure latency and
payload effects on the path being changed. A normalized model can use joins within one
database request; a different HTTP verb can also batch writes. Neither alternative is
automatically slower. The current
src/lib/perf/budgets.ts values
are targets, not measured results or proof against alternatives.
Use Change checklist and limits for the contracts to check during review.