Concurrency

Immutability and concurrency: interview questions and how to answer them

An immutable object never changes after construction, so readers can share it without coordinating every field access with a writer.

Written and reviewed by Sahil Srivastav

ConcurrencyBackend engineeringInterview preparation

What it actually is

Immutability means observable state is fixed after construction. Sharing a reference is safe only when the entire reachable object graph is immutable; a final field pointing to a mutable list is not enough.

It removes races by removing writes, but it does not make a whole algorithm atomic. Replacing an immutable value in a shared variable still needs visibility and, when updates depend on the old value, a CAS or lock.

Why it matters in production

Immutable request and configuration snapshots prevent readers from seeing half-applied changes and make retries easier to reason about. They also reduce lock scope: build a new value, validate it, then publish the reference.

The cost is allocation and copying. For large structures use persistent data structures, copy-on-write, or immutable boundaries around mutable ownership rather than copying blindly.

How it works

Safe construction

Validate before publishing, keep fields private and final where the language supports it, and do not let `this` escape from the constructor.

Deep immutability

Defensive copies are required for arrays, maps, dates, and nested objects. An unmodifiable view is not a copy if another owner can mutate the backing collection.

Publication

A safely constructed object still needs a happens-before edge when published across threads, such as a volatile reference, lock, final-field guarantees, or concurrent collection.

Atomic replacement

A shared immutable snapshot can be updated with compare-and-set. The update function must tolerate retries because another thread may win the CAS.

Implementing it

Use immutable DTOs at thread and request boundaries. Keep mutable builders local, then publish one complete value.

Document ownership for collections; “unmodifiable” prevents one caller’s mutation but does not transfer ownership safely by itself.

Measure allocation before optimising it. A race-free copy is cheaper than debugging a shared mutable cache, but hot paths may need structural sharing.

Interview questions and how to answer them

Is `final List<T>` immutable?

No. Final prevents replacing the reference; callers can still mutate the list unless the list itself is copied and not exposed.

Can immutable objects be shared without locks?

Yes after safe publication. The object’s state cannot change, but the reference still needs a visibility guarantee.

How do you update an immutable shared snapshot?

Construct a new snapshot and CAS it into the shared reference. If CAS fails, reread and recompute; the function must be safe to run more than once.

When is immutability a poor fit?

When copying dominates the workload or state is naturally owned by one actor. Use ownership and message passing there, rather than forcing every update through large copies.

Answers that lose the round

  • Calling a shallowly copied object immutable
  • Publishing `this` during construction
  • Assuming immutability gives atomic compound updates
  • Using an immutable wrapper around a mutable backing list
  • Mutating a field through an accessor
  • Copying enormous graphs on every small update without measuring

Practise in a real repository

Explaining a concept and enforcing it in code are different skills, and machine coding rounds test the second. Gronex ships broken backend repositories whose test suites assert the invariant rather than the happy path.

FAQ

Are strings always immutable?

In languages where the standard string type is immutable, its value is fixed; a container holding strings can still be mutable.

Does immutability prevent stale reads?

No. It prevents torn mutation; visibility and publication decide whether a thread sees the latest snapshot.

What is copy-on-write?

Readers share a snapshot while writers make a new copy, trading write cost and memory for lock-free reads.

Related

More backend concepts