intermediate

Immutability where useful

Use immutable value objects or controlled mutation when it protects invariants, simplifies concurrency, or makes state transitions easier to reason about.

Immutable value objects—`Money`, `Email`, `DateRange`—cannot change after creation; operations return new instances. This protects invariants, simplifies reasoning in concurrent code, and makes equality depend on values not identity.

Controlled mutation remains valid for aggregates with clear lifecycle (entity with ID) when changes pass through domain methods.

On interviews: contrast mutable DTO passed everywhere with immutable value type and explain debugging benefits.

Common pitfalls: shallow immutability with mutable nested arrays, defensive copy forgotten on getters, and immutability theater with public setters elsewhere.

The trade-off is allocation churn versus clarity—profile hot paths before rejecting immutability outright.

Checklist:

  • Use immutability for values without identity.
  • Deep-freeze or copy nested structures.
  • Mutate entities only through domain methods.