Immutable Data Structures vs State-Based Persistence: Pick a Side
Append-only history versus overwrite-in-place storage — which model should own your source of truth. A decisive verdict on auditability, performance, and the cost of forgetting.
The short answer
Immutable Data Structures over State Based Persistence Pick A Side for most cases. Immutability wins because it preserves the one thing you cannot reconstruct later: history.
- Pick Immutable Data Structures if need auditability, time-travel, undo/replay, reproducible debugging, concurrent reads without locks, or any domain where 'what changed and why' is a real question — finance, healthcare, event-driven systems, collaborative editing
- Pick State Based Persistence Pick A Side if have a high-churn dataset where only the current value matters, storage is genuinely constrained, and nobody will ever ask how a record reached its present state — caches, sensor latest-reading, session blobs
- Also consider: They're not mutually exclusive. The strongest pattern is an immutable event log as the source of truth with a mutable, disposable state projection (read model) on top. CQRS/event sourcing exists precisely so you stop choosing.
— Nice Pick, opinionated tool recommendations
What you're actually choosing
This isn't a library bake-off — it's a stance on whether the past is allowed to survive. State-based persistence keeps one cell per fact and overwrites it: UPDATE users SET email = ?. Fast, intuitive, and lossy. The previous email is gone the instant the write commits. Immutable data structures never overwrite; they append a new version and leave the old one intact, so the current value is a function of accumulated history rather than a slot you keep clobbering. Git is immutable. A bank ledger is immutable. Your average CRUD table is state-based and proud of it. The trap is that state-based feels like the default because it maps to how we think about 'the' value — but 'the' value is a projection, not the truth. Decide which one you're willing to lose, because state-based persistence quietly throws away the more expensive one every single day.
Where immutability earns its keep
Auditability is the headline: you get a complete, tamper-evident trail for free, not as a bolted-on audit_log table that drifts out of sync with reality. Debugging stops being archaeology — replay the event stream and watch the bug happen instead of guessing from a corpse. Concurrency gets dramatically easier because immutable structures are inherently thread-safe; readers never see a half-written value and you skip whole categories of locking. You also get time-travel queries ('state as of last Tuesday'), trivial undo/redo, and reproducible builds of derived state. Structural sharing (persistent data structures à la Clojure, Immutable.js) means versioning doesn't cost a full copy per change — old and new share unchanged subtrees. The price is real: more storage, more bytes to move, and you must design compaction/snapshotting so the log doesn't grow unbounded. Pay it. The capabilities it buys are otherwise unrecoverable.
Where state-based persistence is the honest answer
I don't pick winners by pretending the loser is useless. State-based persistence is correct when history has zero value: a Redis cache, a 'last seen' timestamp, a device's most-recent telemetry reading, an ephemeral session. Keeping a full version chain for a value that's overwritten every 200ms is not discipline, it's hoarding — you'd burn storage and write amplification to archive noise nobody will query. It's also simpler to reason about for small teams and trivial schemas: one row, one truth, no projection layer, no compaction strategy, no 'rebuild the read model' ceremony. If your data is genuinely disposable and your scale is modest, immutability is overhead cosplaying as rigor. The failure mode is using state-based persistence for data that turns out to matter — and you almost never know in advance which records a future auditor, lawsuit, or incident review will demand the history of. When in doubt, that doubt is the answer.
The pattern that ends the fight
The mature move isn't either/or — it's event sourcing with CQRS. Keep an append-only immutable log as the canonical source of truth, then derive a mutable, fully disposable state projection (the read model) optimized for queries. Writes go to the log; reads hit the projection; if the projection corrupts or you change its shape, you delete it and replay the log to rebuild. You get immutability's audit trail and replayability with state-based persistence's fast, simple reads. The cost is operational complexity: eventual consistency between log and projection, snapshotting so replay isn't O(all history), and schema-evolution discipline on events you can never delete. That complexity is why I still pick immutability as the primary stance — because in this hybrid the immutable log is the truth and the mutable state is the cache, never the reverse. Get the direction of dependency right and everything downstream is recoverable.
Quick Comparison
| Factor | Immutable Data Structures | State Based Persistence Pick A Side |
|---|---|---|
| Auditability & history | Complete, tamper-evident trail by construction | None unless you bolt on a separate audit log |
| Read/write speed & simplicity | More storage and bytes moved; needs compaction | Single overwrite-in-place, fast and intuitive |
| Concurrency safety | Inherently thread-safe; readers never see partial writes | Needs locking; risk of torn/contended writes |
| Storage footprint | Grows with history; structural sharing softens it | Minimal — one value per fact |
| Debuggability & recovery | Replay the log to reproduce and rebuild state | Post-mortem guesswork from current state only |
The Verdict
Use Immutable Data Structures if: You need auditability, time-travel, undo/replay, reproducible debugging, concurrent reads without locks, or any domain where 'what changed and why' is a real question — finance, healthcare, event-driven systems, collaborative editing.
Use State Based Persistence Pick A Side if: You have a high-churn dataset where only the current value matters, storage is genuinely constrained, and nobody will ever ask how a record reached its present state — caches, sensor latest-reading, session blobs.
Consider: They're not mutually exclusive. The strongest pattern is an immutable event log as the source of truth with a mutable, disposable state projection (read model) on top. CQRS/event sourcing exists precisely so you stop choosing.
Immutability wins because it preserves the one thing you cannot reconstruct later: history. State-based persistence destroys information on every write, and the day you need to know "how did we get here" — for an audit, a debug, a regulator — you can't. You can always derive current state from an immutable log; you can never derive a lost log from current state. Optimize the recoverable side.
Related Comparisons
Disagree? nice@nicepick.dev