Where Should the Truth Live? A Practical Rule for Sharing State and Configuration

tfc

Hatched by tfc

Apr 14, 2026

9 min read

82%

0

Why the question of ownership matters more than you think

Who decides where a piece of truth belongs: you, your teammate, or the invisible code that binds your project together? That choice is the single architecture decision that most often determines whether a system becomes easy to reason about, or becomes a slow, accidental tangle of bugs and meetings.

This is not a question about tools. It is a question about responsibility. When multiple parts of a system need the same information, you must decide who owns that information. That decision shapes how teams coordinate, how components evolve, and how fragile the whole system feels when things change.

If you have ever chased a bug that appears in one place but not another, or argued over whether a configuration file belongs in one repo or another, you already know the pain. The surprise is that the same simple rule that helps components in an interface coordinate also helps teams and projects coordinate at scale. Understanding that rule will change how you design code, configuration, and collaboration.


The tension: consistency versus autonomy

Two impulses pull in opposite directions.

On one hand, consistency matters. When two parts of a system must move in step, duplicating the information across both parts creates a risk of drift. Two versions of the same truth will eventually disagree. That disagreement costs debugging time, incident rotation, and trust.

On the other hand, autonomy matters. Teams and people like to own, change, and ship without asking permission. When every change requires conversations with multiple owners, velocity grinds to a halt. The world of modern development prizes independent contributors who can add configuration files, new workflows, and features without hitting a bottleneck.

The tension is simple: centralize to avoid contradiction, or distribute to preserve autonomy. Both are right in certain ways. The practical question is: how do you get the benefits of both without inheriting their worst weaknesses?


A single compact rule that bridges interfaces and infrastructure

There is a compact, practical rule that answers the question of ownership in most cases. It has two parts and it applies whether you are designing a user interface, a CI pipeline, or a monorepo of services.

Part 1: For each unique piece of truth, pick one owner. That owner is the place where the authoritative value lives. Do not duplicate the same piece of truth in multiple owners.

Part 2: Choose the owner to be the closest common parent of all consumers. If two components both need a value, lift the value up to their nearest shared parent. If two teams need a configuration, put the canonical configuration where both can depend on it without crossing organizational boundaries.

This is not a slogan. It is an operational rule that leads to clear design choices. It explains why putting a piece of state into a parent component reduces bugs in a UI. It also explains why placing shared configuration into a well chosen higher level project file reduces confusion across teams. The core logic is the same: moving truth to the nearest place that can serve everyone avoids redundancy while minimizing unnecessary coupling.

When truth is shared, lift responsibility to the place that best serves all consumers.


Imagining ownership as a lattice: a mental model for decisions

Concrete decisions are easier when you have a simple map. Think of your system as a lattice of nodes. Each node is a logical owner: a component, a library, a repository, a team, or an organizational unit. Edges represent dependency relationships.

Every unique fact or configuration value travels along this lattice to its consumers. The owner is the node where that fact rests. Two principles guide where to place it.

  1. Proximity principle: prefer the nearest node that is a common ancestor of all consumers. The nearer the owner, the less incidental coupling you create. The further up you go, the broader your coupling becomes.

  2. Stability principle: prefer nodes whose lifetime and stability match the lifetime of the fact. If a configuration is likely to change when a particular service evolves, keep the truth near that service. If it is stable across many services, consider a higher level owner.

These two principles create a useful tradeoff surface. When the nearest common ancestor is unstable, you might accept a slightly more distant owner that is more stable. When consumers are sprawling across unrelated parts of the lattice, you might choose to introduce a small shared module or a contract that both can depend on.

Analogy: imagine a multi apartment building and the thermostat settings. If only two adjacent apartments coordinate over heating in a shared hallway, the thermostat sits in the hallway that both access. If the entire building needs coordinated heating, the boiler room is the owner. If every apartment needs its own comfort control, each apartment keeps its own thermostat. The right placement keeps coordination costs low while minimizing how much one team interferes with another.


Two practical patterns that implement the rule

Pattern 1: Lift and pass. When pieces of state are shared across UI components, remove duplicates, lift the state to the nearest common parent, and pass it down. This keeps a single source of truth and makes changes predictable. Implementation details matter: keep the lifted state minimal, expose a narrow API to children, and minimize the number of callbacks. Make the owner an explicit interface, not a free for all.

Pattern 2: Configuration as code, with local override. For project configuration, codify the canonical value at the nearest shared place: a repository root, a shared module, or a configuration package. But allow local overrides only where they are necessary and documented. Provide automation that validates overrides and propagates changes. The idea is not to forbid autonomy, but to make clear what is canonical and what is an exception.

Concrete example: suppose two services need the same feature flag. You can implement a small feature flag service that is maintained at a level both services depend on. That service is the owner. Consumers fetch the flag from that service. If a service has a local reason to behave differently, it declares an explicit override with a documented rationale, and the override is validated in CI.

Concrete example 2: a multi component form in an application. The form has inputs spread across several small components. Rather than each component holding local copies of the same values, lift the core form state to the closest parent that encloses all the inputs. Child components receive current values and change callbacks. Validation and submission are handled at the owner, keeping behavior consistent.


The contract layer: making ownership explicit and automated

Ownership is social as well as technical. Decisions about where truth lives must be visible to developers, reviewers, and automated systems. A small contract layer makes ownership enforceable.

What does a contract look like? It is a minimal, machine readable specification that defines:

  • the canonical source for a value,
  • the allowed ways to read the value, and
  • the permitted points of override, if any.

A contract can be as light weight as a README and a lint rule, or as formal as a package with a public API and automated checks. The important part is that the contract is enforced by the tools you already use: static checks, CI pipelines, and code review automation.

Think in terms of three levels: owned, borrowed, and overridden. Owned means the node is the canonical place for the fact. Borrowed means a consumer reads the fact from an owner but does not own it. Overridden means a consumer provides a local exception that must include a justification and a test that demonstrates why the override is correct.

This model reduces arguments. When someone proposes a change, reviewers can look at the contract, see whether the change keeps the single source of truth intact, and either accept it or push it into the appropriate owner.


How to decide in practice: a short decision algorithm

When you encounter a duplicated piece of truth, run this five step algorithm.

  1. Identify all consumers. Write down every component, service, or team that reads or writes the value.
  2. Find the nearest common ancestor in the ownership lattice. Ask: who can most cheaply serve this value? What node is above all consumers with minimal extra coupling?
  3. Evaluate stability. Will this value change with the lifecycle of the candidate owner? Prefer owners whose maintenance cadence matches the value.
  4. Define the contract. Decide how consumers read the value, what an override looks like, and how changes are propagated and tested.
  5. Automate enforcement. Wire a lint rule, a CI check, or a small build step that verifies the value is read from the owner and that any override includes the required justification.

Follow this algorithm early. Ownership decisions hardened late become technical debt. Moving truth after many dependent consumers exist is costly. Treat ownership as a product decision subject to review, not an afterthought.


Common pitfalls and how to avoid them

Pitfall: lifting everything to the highest level. You might be tempted to centralize all truth at the top because it seems simple. The result is heavy coupling, long change windows, and political friction. Avoid this by applying the proximity principle. Centralize only when many parts truly require the same truth.

Pitfall: leaving ownership implicit. If it is unclear who owns a configuration, people will copy, reconcile, and fight. Make ownership explicit with a contract and with tooling.

Pitfall: treating code as governance avoidance. Converting governance debates into code without social alignment just hides the problem. Use automated checks to enforce social standards, not to replace conversations.

Pitfall: overusing overrides. Overrides solve immediate problems but create future drift. Allow them sparingly and require explicit justification and tests.


Key Takeaways

  1. Pick one owner for every unique piece of truth and place it at the nearest common parent of all consumers. This minimizes duplication without over centralizing.

  2. Use a light weight contract that specifies how the truth is exposed, read, and when it may be overridden. Make the contract machine enforceable through CI or linters.

  3. Apply a simple five step algorithm when you find duplicated facts: identify consumers, find nearest ancestor, check stability, define contract, automate enforcement.

  4. Keep lifted state minimal and provide a narrow interface to consumers. For configuration, prefer code based modules that teams can depend on, with documented and controlled override points.

  5. Revisit ownership as the system evolves. What was local may become shared; be prepared to lift responsibility upward before duplication becomes entrenched.


A final reframing to take back to your codebase or org

The question of where truth lives is not a technical nicety. It is the interface between architecture and human coordination. When you place a value deliberately, you shape the patterns of communication, the frequency of deployment, and the kinds of mistakes your team will make.

Think of ownership as an act of custodianship, not domination. When you lift truth to the right place, you are choosing who will be responsible for accuracy, documentation, and change. When you leave ownership implicit, you invite duplication and friction.

Next time you find two places that disagree, do not start arguing about correctness. Ask a different question: who should bear the responsibility of keeping these places in sync? Answer that, write the contract, and automate the rule. You will reduce outages, free developer time, and make your system easier to reason about.

Ownership is the human side of architecture. Design who will care for the truth, and the rest becomes engineering work.

Sources

← Back to Library

Hatch New Ideas with Glasp AI 🐣

Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)

Start Hatching 🐣