# Change Reference to Value

> Make a small shared object immutable and compare it by content, so it can be copied freely instead of tracked by identity.

- kind: technique
- category: organizing-data
- canonical: https://aipatterns.dev/refactoring/techniques/change-reference-to-value
- inspired-by: https://refactoring.guru/change-reference-to-value

## Problem

A small object, such as a currency, a date range or a geo point, is shared by reference and mutated in place. Mutating it through one holder silently changes it for every other holder, and code has to defensively copy it or look it up in a registry.

## Solution

**Make the object immutable, give it value equality, and replace in-place mutation with methods that return a new instance.**

## Before

```ts
class DateRange {
  constructor(public start: Date, public end: Date) {}

  extendBy(days: number): void {
    this.end = new Date(this.end.getTime() + days * 86_400_000);
  }
}

const promoWindow = new DateRange(new Date("2026-11-01"), new Date("2026-11-30"));
const blackFriday = { name: "BF", window: promoWindow };
const cyberMonday = { name: "CM", window: promoWindow };

cyberMonday.window.extendBy(2); // blackFriday's window also moved
```

## After

```ts
class DateRange {
  constructor(readonly start: Date, readonly end: Date) {
    Object.freeze(this);
  }

  extendBy(days: number): DateRange {
    return new DateRange(this.start, new Date(this.end.getTime() + days * 86_400_000));
  }

  equals(other: DateRange): boolean {
    return (
      this.start.getTime() === other.start.getTime() &&
      this.end.getTime() === other.end.getTime()
    );
  }
}

const promoWindow = new DateRange(new Date("2026-11-01"), new Date("2026-11-30"));
const blackFriday = { name: "BF", window: promoWindow };
const cyberMonday = { name: "CM", window: promoWindow.extendBy(2) };
```

## Why

- Sharing is safe. Nothing can change the range under another holder.
- No registry or defensive copies needed; equality is by content.
- Values are easier to reason about in concurrent code and to cache.

## Steps

1. Check the object has no identity that matters: two instances with the same data should be interchangeable.
2. Remove setters one at a time. Replace each mutation with construction of a new instance, and reassign at the caller. Run the tests after each.
3. Mark fields `readonly` and freeze the instance if your language supports it.
4. Add `equals` (and `hashCode` in Java-like languages). Replace identity comparisons with it.
5. Remove any registry or lookup used to share instances.

## Watch out

- Fields holding mutable objects defeat immutability. `Date` is mutable in JavaScript; `readonly` stops reassignment, not `range.start.setDate(...)`. Copy on the way in or use an immutable date type.
- Don't convert real entities (a customer, an account) whose changes must be visible everywhere. Those need identity.
- Frequent changes to a large value object allocate many copies. Usually negligible; measure if it sits in a hot loop.
- The inverse is [Change Value to Reference](https://aipatterns.dev/refactoring/techniques/change-value-to-reference.md).

## Related

- Reduces [Inappropriate Intimacy](https://aipatterns.dev/refactoring/smells/inappropriate-intimacy.md) through shared mutable state.
- Inverse: [Change Value to Reference](https://aipatterns.dev/refactoring/techniques/change-value-to-reference.md).
- Often follows [Replace Data Value with Object](https://aipatterns.dev/refactoring/techniques/replace-data-value-with-object.md). Shared immutable values are the basis of [Flyweight](https://aipatterns.dev/design-patterns/flyweight.md).

## Agent checklist

- [ ] The object has no meaningful identity.
- [ ] All setters and mutating methods were replaced with methods returning new instances.
- [ ] Nested mutable fields are copied or replaced with immutable types.
- [ ] Equality is by value and identity comparisons were updated.
- [ ] Tests pass.
