# Change Value to Reference

> Replace many identical copies of an entity with one shared instance looked up from a registry, so updates are seen everywhere.

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

## Problem

Each order holds its own copy of a `Customer` object built from the same data. When one copy changes (a new credit limit, a renamed company), the others stay stale. The data is really one entity, but the code treats it as many values.

## Solution

**Keep one instance per identity in a repository or registry, and have every owner hold a reference to that instance instead of creating its own.**

## Before

```ts
class Customer {
  constructor(readonly id: string, public creditLimit: number) {}
}

class Order {
  readonly customer: Customer;
  constructor(customerId: string, creditLimit: number, public total: number) {
    this.customer = new Customer(customerId, creditLimit);
  }
}

const a = new Order("C-77", 5_000, 1_200);
const b = new Order("C-77", 5_000, 3_900);
a.customer.creditLimit = 10_000; // b still sees 5_000
```

## After

```ts
class Customer {
  constructor(readonly id: string, public creditLimit: number) {}
}

class CustomerRegistry {
  private byId = new Map<string, Customer>();

  get(id: string, creditLimit: number): Customer {
    let c = this.byId.get(id);
    if (!c) {
      c = new Customer(id, creditLimit);
      this.byId.set(id, c);
    }
    return c;
  }
}

class Order {
  constructor(readonly customer: Customer, public total: number) {}
}

const customers = new CustomerRegistry();
const a = new Order(customers.get("C-77", 5_000), 1_200);
const b = new Order(customers.get("C-77", 5_000), 3_900);
a.customer.creditLimit = 10_000; // b sees 10_000
```

## Why

- One update reaches every holder. No sync code, no stale copies.
- Less memory when many objects share the same entity.
- Identity checks (`a.customer === b.customer`) now mean "same customer".

## Steps

1. Decide where shared instances live: an existing repository, a unit-of-work, or a new registry. Avoid a global singleton if a scoped object will do.
2. Add a lookup that returns the existing instance for an id or creates it once.
3. Change the owner's constructor to accept a `Customer` instead of building one. Update callers to fetch it from the registry. Run the tests.
4. Remove the old creation path.

## Watch out

- Shared mutable state means any holder can change the entity for everyone. Make sure that is the intended semantics.
- The registry's lifetime matters. A process-wide cache in a server leaks data between requests and tenants; scope it to a request or transaction.
- In distributed systems, a reference is often an id plus a fetch, not a pointer. Don't hold stale shared objects across network boundaries.
- If the object never changes, keep it as a value; sharing gives no benefit.
- The inverse is [Change Reference to Value](https://aipatterns.dev/refactoring/techniques/change-reference-to-value.md).

## Related

- Fixes [Duplicate Code](https://aipatterns.dev/refactoring/smells/duplicate-code.md) in the form of duplicated entity data and the [Shotgun Surgery](https://aipatterns.dev/refactoring/smells/shotgun-surgery.md) of keeping copies in sync.
- Inverse: [Change Reference to Value](https://aipatterns.dev/refactoring/techniques/change-reference-to-value.md).
- The registry is often a [Factory Method](https://aipatterns.dev/design-patterns/factory-method.md); sharing immutable instances is [Flyweight](https://aipatterns.dev/design-patterns/flyweight.md).

## Agent checklist

- [ ] The object is an entity with identity and changes that all holders must see.
- [ ] The registry's scope (request, transaction, process) matches the data's ownership.
- [ ] Owners receive instances instead of constructing them.
- [ ] No caller still builds its own copy.
- [ ] Tests pass, including one that shows an update is visible through every holder.
