Lindsay Edwards

A cross-cutting concern only cuts what flows through it

On this page

There is a comforting phrase that hides a lot of bugs: “that is handled centrally.” Audit logging, validation, entity mapping, tenant scoping. Handled centrally. The trouble is that “central” usually means “in a pipeline,” and a pipeline only touches the requests that actually flow through it. Everything that takes a shortcut is silently exempt, and nothing warns you.

The audit log that covered some of the writes#

One service registered a single pipeline behaviour: an audit logger, wrapped around every command sent through the mediator. Genuinely central, for anything that went through the mediator. But a good number of the endpoints never sent a command at all. They took the database context directly and saved inline, because it was two lines shorter. Those writes skipped the pipeline entirely, so the audit log quietly covered a subset of changes, and no compiler or test flagged the gap. The audit trail was not wrong so much as partial, which is arguably worse, because you trust it. It is a CCTV system wired to the front door but not the loading bay: it looks like coverage right up until the thing you needed on tape happened at the exit nobody filmed.

The validation that was done two different ways#

The same shape showed up in validation. Validators were registered, and then invoked by hand in each endpoint that remembered to. About eighteen endpoints did. The parallel set of simpler endpoints bound straight to the entity and validated nothing, cheerfully accepting empty, oversized, and out-of-range input, even though there was already central machinery to turn a validation failure into a clean 400. Two contributors had solved “validate the input” two different ways, and the guarantee diverged along the seam between them.

If a guarantee is enforced per-file by convention, its coverage is inversely proportional to the number of files and contributors. It does not scale; it dilutes.

The fix in both cases is the same idea: if something is meant to be cross-cutting, it has to be enforced in a place every request genuinely passes through, a real validation step in the pipeline, an endpoint filter, a single write path, not a habit you are trusting every handler to remember. And then, ideally, a test that fails when a new endpoint bypasses it.

The dependency that was referenced but never ran#

A smaller cousin of the same disease: the project referenced a source-generator whose job is to produce compile-checked mapping code, exhaustive, so a new enum value that you forget to map becomes a build error. Except there was not a single usage of it anywhere. No annotated class, no generated method, no import. Every mapping was still hand-written, including the enum conversions, with a “anything else, return null” fallback arm, which is exactly the silent-drift bug the generator exists to prevent. The dependency was paid for in build time and gave nothing back, while the codebase kept the very habit it was supposed to remove.

So audit that your tools are actually invoked, not merely installed. A referenced generator that nothing uses is worse than not having it, because it looks like the problem is solved.

The common thread#

All three are the gap between “we have a central mechanism for this” and “every path actually uses it.” A pipeline behaviour, a validator, a code generator: each is only as cross-cutting as the fraction of the code that routes through it. Before you rely on a central guarantee, the question is not “does the mechanism exist,” it is “can anything reach production without going through it.” If the answer is yes, what you have is not a guarantee. It is a convention with good branding.

Keep reading