Logging is one of those things that accretes. Each developer who touches a service adds a line in the style they're used to, and over time you get a codebase where some errors are logged as exceptions, some as structured objects, some as plain strings, and some not at all. Test coverage is patchy in the same way. Nothing is obviously wrong with any single line, but the whole thing is harder to trust than it should be.

The work here was building a standardised logging system for an AI assistant service. It wasn't really about adding functionality. It was about coherence: defining how logs get created, what they contain, and how they're tested, then making that the path of least resistance so it stays consistent as more people add to the code.

What standardisation actually involves

In practice that meant a few things. A logging module that every part of the service imports. An interface developers use to emit logs instead of calling a logging library directly. Tests that check the shape and content of what gets logged. And enough documentation that the next person doesn't have to reverse-engineer the intent.

Going through the module instead of the library directly is about control. When all output passes through one layer, you can enforce a consistent structure, apply transformations uniformly, and change behaviour in one place rather than at every call site. It's the same instinct that has you wrap fetch in a single HTTP client rather than calling fetch all over a codebase.

The AI-specific problem

AI-adjacent services bring a logging problem that's easy to underestimate. You want enough detail to understand what happened: which input produced which output, where in a multi-step pipeline things went wrong, what the model was asked to do. But you have to be careful about what you keep.

User-provided input to an AI service can contain personal information. Names, travel details, payment data. Log request bodies indiscriminately and that data ends up in your log aggregation system, which probably has weaker access controls than your primary data stores and quite possibly keeps things longer than it should.

The right move is to make the logging layer responsible for this boundary, not the developers who call into it. You decide what's safe to log when you design the module, not at every call site. Adding a new instrumented line then doesn't depend on the developer remembering the rule. It works correctly by default. It's the same principle as any invariant the system relies on: enforce it once, at the boundary, instead of trusting every caller to get it right.

Health probes: the quiet dependency

A smaller change in the same spirit was correcting readiness and liveness probe URLs in a fare search service. The probes were pointing at endpoints that had been renamed at some point and never updated in the Kubernetes config.

Readiness and liveness probes are how the orchestration platform decides whether a pod is healthy and ready for traffic. Get them wrong and the platform either never sends traffic to a pod that's perfectly fine, or keeps restarting pods that don't need it. Both are confusing to diagnose unless you immediately suspect the probe config. It's a one-line fix. It's also the kind you'd much rather catch before production than after.