Immutability.

Once you create a piece of data, can you change it? This question seems simple, but the answer shapes how we write code. Today we explore mutable and immutable data.

Mutable means changeable. Mutable data can be modified after it is created.

You create a list. You add an item. The same list now has more items. You did not create a new list. You changed the existing one.

You create a record with a name field. You update the name. The same record now has a different name. The record was mutated.

Mutation is in-place change. The data object stays the same object, but its contents change. If you have a reference to it, you see the new contents.

This is how most programming languages work by default. You create something, then you modify it as needed.

Immutable means unchangeable. Immutable data cannot be modified after it is created.

You create a string. You cannot change its characters. If you want a different string, you create a new one. The original remains as it was.

You create an immutable list. You cannot add items to it. If you want more items, you create a new list with the additional items. The original list is untouched.

Immutability is a constraint. Once data exists, it exists exactly as created. Forever. No modifications. No surprises.

If you want different data, you make a new copy with the changes. The old data remains unchanged.

Why would we want data we cannot change? This seems like a limitation. But it brings powerful benefits.

First: predictability. If data cannot change, you always know what it contains. You looked at it earlier, it had value X. It still has value X. Nothing could have changed it.

With mutable data, you cannot be sure. Something else might have modified it. You must always check. You must always worry.

Second: safety. If you pass immutable data to a function, you know that function cannot change it. Your data is safe. You do not need to make a defensive copy.

With mutable data, any code that has a reference can modify the data. You cannot trust that your data will remain unchanged.

Third: reasoning. Immutable data is easier to think about. It does not change over time. There is no before and after. There is just: it is what it is.

Immutability has costs.

If you want to change something, you must create a copy with the change. This means allocation. Memory. Time.

With mutable data, you modify in place. No extra memory. With immutable data, you create a new version. More memory, at least temporarily.

For small data, this cost is negligible. For large data, it can matter.

Modern languages optimize this. They share unchanged parts between versions. If you change one field of a large record, the new record shares all other fields with the original. This is called structural sharing.

Still, immutability is not free. There is overhead. The question is whether the benefits outweigh the costs.

To understand why immutability helps, consider aliasing.

Aliasing is when multiple references point to the same data. You have variable A and variable B. Both point to the same list.

With mutable data, this is dangerous. You modify through A. The data changes. Someone using B sees the change. They did not expect it. They did not ask for it. The data they relied on has silently changed.

This is a common source of bugs. One part of code changes data. Another part expected it unchanged. The bug is subtle because nothing explicitly passed new data. The change happened through a shared reference.

With immutable data, aliasing is safe. A and B point to the same data. But nobody can change it. A sees the data. B sees the same data. It will always be the same data.

Sharing immutable data is safe. Sharing mutable data requires caution.

Mutable data and concurrency do not mix well.

Concurrency means multiple things happening at once. Two threads. Two processes. Two parts of code running simultaneously.

If both access mutable data, chaos ensues. One thread writes. The other reads. What does it see? Half-written data? Old data? New data? The answer depends on timing. On luck.

This is a race condition. The outcome depends on who wins the race. It is unpredictable. It causes bugs that appear and disappear randomly.

Immutable data eliminates this problem. Nobody can write. Everyone reads the same unchanging data. No race. No conflict. Safe concurrency.

Despite its problems, mutable data has its place.

Performance-critical code sometimes needs mutation. Building up a large structure piece by piece is faster with mutation than with many copies.

Local mutation is often fine. If data is created, mutated, and used all within one function, and never escapes, the risks are contained. Nobody else can see the mutations.

Some algorithms are naturally expressed with mutation. In-place sorting. Graph algorithms that update visited nodes. Fighting against the natural expression adds complexity.

The key is scope. If mutable data stays local, under your control, mutation is manageable. If mutable data is shared widely, problems multiply.

Prefer immutability when data is shared.

If data crosses function boundaries, immutability prevents accidental modification. If data lives in multiple places, immutability prevents synchronization problems.

Prefer immutability for data that represents facts. A historical record. A snapshot of state. These should not change because they represent what was, not what is.

Prefer immutability when reasoning about code. If you need to understand what a piece of code does, immutable data makes it easier. You know data input equals data output.

Start with immutability. Introduce mutation only when you have a clear reason. This is safer than starting mutable and trying to add safety later.

Real programs often mix both approaches.

You might have an immutable data model. The records that represent your domain are immutable. But you build them up using mutable builders. Local mutation, immutable result.

You might have immutable messages passed between components. Each component can have mutable internal state. Immutable interfaces, mutable implementations.

The key is clear boundaries. Know what is mutable and what is not. Document it. Enforce it where possible. Do not let mutable and immutable mix unexpectedly.

Let us summarize.

Mutable data can be changed after creation. Immutable data cannot.

Immutability provides predictability. Data you see is data you can rely on.

Immutability provides safety. Nobody can change your data without your knowledge.

Immutability costs extra allocations but enables structural sharing.

Mutable data is dangerous with aliasing and concurrency.

Choose mutable for local, performance-critical operations. Choose immutable for shared data and clearer reasoning.

Start immutable. Add mutation only with clear justification.