Make the server choose one durable store

A deployable service should not guess which durable store is the source of truth. If the runtime points at two stores, the safest behavior is to refuse startup.

Yesterday's useful lesson was small, but it is exactly the kind of small rule that keeps systems from becoming mysterious later.

I was hardening a deployable sync server. The server can run against a durable local database in normal operation, and it also has a simpler file-backed store for development and fallback use. Both modes are useful. Both modes are dangerous if the process is allowed to start with both configured at once.

That is the public lesson: when a service has more than one possible durable backend, runtime configuration should select exactly one source of truth.

The bug shape

Multiple storage adapters often start as a convenience. A file-backed adapter makes early tests easy. An embedded database makes operations more realistic. A hosted event backbone may come later. Each adapter has a legitimate job.

The risk is not that there are several adapters in the codebase. The risk is that a deployed process can be ambiguous about which adapter owns canonical facts.

If a server sees two durable paths and quietly picks one, the operator now has to know hidden precedence rules. If a path is blank but treated as valid, the process might create state somewhere unexpected. If a port or store setting is malformed and the server still starts, the failure moves from deploy time into runtime.

That is backwards. Configuration mistakes should be cheap, loud, and early.

The runtime boundary I want

Diagram showing deploy-time environment validation selecting exactly one durable store before the server starts, while ambiguous or invalid configuration stops startup.
The service can support multiple adapters, but one running process should have one canonical durable store.

The useful boundary is a small resolver that runs before the server starts:

  • Read the environment once.
  • Reject blank durable-store values instead of normalizing them into surprise defaults.
  • Reject mutually exclusive store settings when more than one is present.
  • Reject invalid listener configuration before binding a port.
  • Return a typed runtime configuration that the server uses without reinterpreting the environment later.

That keeps startup honest. After the resolver succeeds, the rest of the server can be boring: open the chosen store, expose the API, and write canonical events through one path.

Why this matters for event-backed systems

Durable event systems depend on trust in the log. If the process might write one session to a file and the next session to an embedded database because the environment changed or precedence was misunderstood, the event log stops being an explanation of the system. It becomes another thing to investigate.

A strict startup check protects the operational story. When a projection is wrong, I want to ask whether the event exists, whether it replayed, and whether the read model caught up. I do not want to ask which store the server secretly chose.

This also makes agent-led implementation safer. Agents are good at moving through code quickly, but deployment configuration is where small misunderstandings become persistent state. A failing test around runtime selection gives the agent loop a hard boundary: do not ship a process that can start ambiguously.

The test is part of the feature

The implementation detail I care about is not just the resolver. It is the regression test around the resolver.

The tests should prove both the allowed cases and the rejected ones. One durable database path is accepted. One fallback file path is accepted. Two store paths fail. Blank store values fail. Invalid ports fail. The deployable server imports the same resolver that the tests exercise, so the rule does not live only in documentation.

That kind of test is cheap, and it prevents a class of future mistakes that would be expensive to diagnose from production symptoms.

The rule I am taking forward

It is fine for a system to have multiple storage adapters. It is fine for local development, tests, and hosted operation to use different backends. What is not fine is a live server that has to guess which durable store is canonical.

For deployable event-backed services, I want this rule at startup:

Support many adapters in code; select exactly one durable store in runtime.

If configuration violates that rule, the server should not limp forward. It should stop before writing a single fact.

That is not defensive overengineering. It is how the system keeps its source of truth believable.

All posts Back to projects