Immutability — Detailed#
Mutable vs immutable#
classDiagram
class MutablePoint {
-x: int
-y: int
+setX(int)
+setY(int)
}
class ImmutablePoint {
+x: int
+y: int
+ImmutablePoint(x, y)
+withX(int) ImmutablePoint
+withY(int) ImmutablePoint
}
ImmutablePoint returns a new instance on every "change". Holders of the old one are unaffected.
How to make a class immutable#
- All fields
final. - No setters.
- Don't expose mutable internals (defensive copy on construction + getters).
- Don't
this-leak during construction. - Make the class itself
final(or seal it) so subclasses can't add mutable state.
Java records (since 14) and Kotlin data class val ... give this for free.
Why it matters for concurrency#
sequenceDiagram
participant T1 as Thread 1
participant T2 as Thread 2
participant V as Immutable Value
T1->>V: read
T2->>V: read
Note over T1,T2: no lock needed — value never changes
No race conditions on an object that can't be mutated. This is the easiest concurrency strategy that exists.
Persistent data structures#
For collections, naive copy-on-write is O(N). Persistent / functional data structures share structure under the hood — operations are O(log N) or O(1):
| Structure | Used by |
|---|---|
| Persistent vector (32-way trie) | Clojure, Scala, Immutable.js |
| HAMT (hash array mapped trie) | Clojure, Scala, .NET ImmutableDictionary |
| Persistent map / set | functional langs everywhere |
Where immutability shows up in this site#
- CRDTs — each replica works with immutable causal histories.
- Event sourcing — events are immutable; state is a projection.
- Block & event logs — Kafka, WAL, blockchain — append-only by design.
- Configuration service / Feature flags — config snapshots passed around the codebase as immutable values.
When not to be immutable#
- Tight inner loops on huge arrays in performance-critical code (allocation pressure).
- Hot data structures with high update rates (counters, caches).
- I/O buffers that must be reused.
Even then, you can localise mutation behind an immutable interface (mutable builder → build → freeze).
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Pub/Sub & message brokers | topics, consumer groups, delivery semantics | pub-sub-pattern |
HLD |
Leader/follower replication | sync/semi-sync/async replication, failover | replication-leader-follower |
HLD |
LSM vs B-Tree engines | WAL, memtable, SSTables, compaction | storage-engines-lsm-btree |
HLD |
Event sourcing + CQRS | commands -> events; separate read model | event-sourcing-cqrs |
LLD |
Data structures & complexity | Big-O, common DS, latency numbers | data-structures-complexity |
LLD |
Creational patterns | Singleton, Factory, Builder, Prototype | creational-patterns |
LLD |
Behavioural patterns | Strategy, Observer, State, Command, Chain | behavioral-patterns |
LLD |
Threading & deadlocks | thread states, Coffman, lock ordering | threading-and-deadlocks |
LLD |
Immutability | immutable types, persistent collections | immutability |