Skip to content

Configuration Inheritance Tree

Structural model — instantiates Controlled Inheritance Propagation

Arranges configuration into a parent-to-child tree so each scope inherits its ancestors' values by default and declares only what it needs to differ.

Configuration Inheritance Tree is the substrate the whole pattern stands on: it arranges settings into a hierarchy of scopes — global → environment → service → instance — where a child inherits every value from its parent unless it sets its own. Its defining idea is that the tree is the propagation channel: a value set once at a high scope reaches every descendant that hasn't overridden it, and "unset" means "inherit from my parent," not "empty." Because each node has a single parent, there is exactly one path a value can travel and one obvious place it lives. That single-owner-per-path structure is what separates it from a cascade — this mechanism is about fall-through, not about arbitrating a precedence war between competing declarations.

Example

A payments platform runs about 40 services across dev, staging, and prod. Rather than maintain 120 near-duplicate config files, the team keeps a tree. A root defaults.yaml holds fleet-wide values (request timeout, log level, retry policy); an environment layer sits below it (prod raises the log level to WARN and sets the real database hosts); a service layer sits below that (the ledger service pins a larger connection-pool size); and a single canary instance overrides one feature flag at the leaf. A running service resolves each key by walking root → env → service → instance and taking the nearest value that was actually set.

When security asks to shorten the default request timeout across the whole fleet, the change is one line at the root — and every service that never overrode it inherits the new value on next deploy, while the handful that pinned their own are untouched. The tree turns "edit 40 files and hope you caught them all" into "edit one, and know exactly who inherits it."

How it works

  • Declare the lineage. An explicit parent chain of scopes, one path from root to each leaf.
  • Catalog what may be set. Which keys exist, and at which scope each is allowed to be set or overridden.
  • Resolve by nearest-ancestor fall-through. For each key, the effective value is the one set closest to the node; an unset key falls through to the parent; a key unset all the way up takes a built-in default.
  • Store diffs, not copies. A child records only where it departs from its parent, so its config file shows its genuine differences and nothing else.

Its distinguishing move is single-owner resolution: because every scope has one parent, there is never ambiguity about where a value came from — you walk up until you find it, with no specificity contest.

Tuning parameters

  • Number of layers — more scopes (global / region / env / service / instance) give finer targeting but a longer walk and more places a value can hide; fewer layers stay legible but blunt.
  • Inheritable vs pinned keys — which keys fall through and which are fixed at exactly one scope. Pinning region high up stops descendants drifting; making it inheritable lets them tune it.
  • Merge semantics for structured values — whether a child's list or map replaces the parent's or deep-merges with it. Replace is predictable; merge is DRY but can smuggle in inherited entries the child never wrote.
  • Null vs unset — whether a child may clear an inherited value (explicit null) or only replace it. Allowing clears adds power and a footgun.
  • Scope-set permissions — which keys a given layer's owners may set, so a service can't quietly override a security default meant to stay global.

When it helps, and when it misleads

Its strength is that it deletes copy-paste config: a fleet-wide change becomes a one-line edit, every setting has a single obvious home, and each node's file shrinks to just its real differences. It is the reason a 500-node fleet can share one policy without 500 duplicated files.

Its central failure mode is action at a distance. Because a value can be inherited from far up the tree, reading a leaf's own file tells you what it overrides, not what it actually runs with — and a change three scopes up silently alters hundreds of descendants at once. Over-deep trees make the effective value genuinely hard to predict without tooling, which is the coupling the DRY principle warns about when reuse hardens into hidden dependency.[1] The classic misuse is burying a value high in the tree to "fix" one leaf, quietly changing its siblings too. The discipline is to keep the tree shallow, store only real diffs, and never trust a leaf file alone — read the resolved config, which is Effective Configuration Diff's job.

How it implements the components

Configuration Inheritance Tree realizes the substrate side of the archetype — the lineage and the defaults that flow along it:

  • lineage_model — the scope hierarchy (global → … → instance) is the lineage; each node names its single parent, defining the one path a value travels.
  • inheritable_property_catalog — the set of keys, and the scope at which each may be set, is the catalog of what is allowed to propagate down the tree.
  • default_propagation_rule — nearest-ancestor fall-through: unset means inherit from the parent, so a value set once reaches every non-overriding descendant.

It does NOT resolve competing declarations by weight — that precedence contest is CSS Cascade and Specificity Rule; it does not compute or annotate the resolved end-state — that's Effective Configuration Diff; and it does not retire stale overrides — that's Override Expiry Workflow.

  • Instantiates: Controlled Inheritance Propagation — supplies the lineage-and-defaults substrate the rest of the pattern operates on.
  • Sibling mechanisms: Effective Configuration Diff · CSS Cascade and Specificity Rule · Object-Oriented Class Inheritance · Inheritance Lint or Static Analysis · Lineage Impact Analysis Report · Override Expiry Workflow · Policy Inheritance Matrix · Prototype Delegation Chain · Permission Inheritance with Explicit Denial · Platform Variant Option Model · Schema Extension and Override Check · Template Clause Override Register · Trait or Mixin Composition Rule

Notes

The tree assumes a single parent per node — one path from root to leaf. The moment a node must draw from two unrelated parents (a "diamond"), fall-through alone cannot say which wins, and you have to bolt on an explicit precedence rule. That boundary is exactly where this mechanism hands off to CSS Cascade and Specificity Rule or a trait/mixin composition rule.

References

[1] The DRY principle — Don't Repeat Yourself, from Hunt & Thomas's The Pragmatic Programmer — is what motivates configuration inheritance: express each fact once. The same source is clear that reuse creates coupling, and a shared ancestor value is a dependency every descendant carries whether or not it knows it.