Skip to content
Kien

One identifier to a timeline

· 5 min read

Every booking investigation starts the same way: a ticket hands you one string — a client reference, a booking id, a trace-id, a PNR — and a strong opinion about whose fault it is. The method that works is boring and always the same: route the identifier, widen it, build the timeline, and only then allow yourself a hypothesis.

  • debugging
  • observability
  • logging
  • investigations
On this page
  1. Route the identifier before you read anything
  2. The timeline comes before the theory
  3. None of this works unless the logs were built for it
  4. Absent, empty, null
  5. The chain you cannot read
  6. What the method buys

Every booking investigation I run starts the same way: a ticket hands me one string and a strong opinion. The string is a client reference, or a booking id, or a trace-id, or a flight PNR — whatever survived the journey from a customer's complaint through a partner's support desk to us. The opinion is usually about whose fault it is. The opinion is wrong often enough that I have learned to treat it as decoration and start from the string.

What follows is the method, stated as a method, because I think the ordering is the whole value.

Route the identifier before you read anything

The first move is not to read logs. It is to ask: which source can widen this identifier into the others? A client reference resolves through the booking store into our internal booking id and the supplier's reference. A trace-id goes straight to the request logs. A PNR routes to the flight side and comes back with the order around it. Each identifier has exactly one source that can exchange it for the full set, and going to the wrong source first means reading logs that cannot contain your string and concluding, falsely, that nothing happened.

So: one string in, the full ring of identifiers out. Only then does reading begin.

The timeline comes before the theory

With the identifiers in hand, I build one artifact and nothing else: a timeline. Five questions, in wall-clock order —

what came in from the client; what we sent upstream to the supplier; what came back; what we wrote to our own store; and what we told the customer.

That is the entire skeleton. No hypothesis is allowed until all five columns have either an entry or an explicit "no record". The discipline sounds pedantic and pays for itself on the first pass, because most wrong root causes are wrong in the same way: they explain three of the five points and quietly assume the other two. The classic booking mystery — customer charged, supplier has nothing — is unsolvable from opinion and nearly mechanical from a timeline, because the timeline shows you exactly which arrow the money crossed and which arrow the confirmation did not.

None of this works unless the logs were built for it

Here is the part that is easy to miss: the method is not cleverness. It works because of three decisions made when the logging was designed, long before any particular incident.

First, the incoming request is snapshotted at the first line of the handler, before any mutation. Not "the request, roughly, after parsing" — the thing the client actually sent, preserved before defaulting, enrichment, or validation touched it. Without this, every investigation begins with an argument about what the client sent, and that argument cannot be won from evidence.

Second, every upstream call is logged with its exact request and response — exact enough that a failed call can be re-fired as the same request it originally sent. There is a command that rebuilds that request from nothing but a trace-id. That turns "I think the supplier would reject this" into a test you can run, which is the difference between a hypothesis and a fact.

Third, when I summarise a request into a field table, the table's rows come from the endpoint's own validation schema — not from whatever fields happened to catch my eye. The schema is the complete list of what the endpoint can accept, so nothing is silently dropped from the summary. A hand-picked field list omits exactly the field you did not think mattered, which is exactly the field that did.

Absent, empty, null

One habit deserves its own section because it decides cases. A field in a request has three distinct states, and they mean different things: absent (the client never sent the key), empty (the client sent it, deliberately blank), and null (the client sent it, explicitly cleared). A summary that flattens these into "no value" destroys the distinction that often is the root cause — an absent field falls through to a default; an explicit null may override one. When the difference between two of those behaviours is a booking made under the wrong account, the three states get three different markers in the field table, always.

The chain you cannot read

Sometimes the timeline has a gap that is not a logging failure: a link in the chain lives in a repository or a system I cannot read — a partner's middleware, a supplier's internals. The temptation at that point is strong, because the timeline is nine-tenths complete and one hypothesis looks better than the other.

The honest output in that situation is not a root cause. It is the two surviving hypotheses, stated plainly, plus the query — or the log line, or the question to the other team — that would separate them. That is a less satisfying document to write and a far more useful one to receive, because a confident root cause that turns out wrong costs the next person a day of looking in the wrong place, and a fork with a decision procedure costs them one query.

What the method buys

I have never timed an investigation with and without this structure, so I have no speed-up figure to offer — only the observation that the failure mode changed. Investigations used to end with "we think"; they now end with a timeline that a second person can check without re-deriving it, and when the answer is "we cannot know from our side", the document says so and says what would settle it.

One string in. A timeline out. The hypothesis last, and only if it survives all five columns.

Related posts

· 6 min read

In integration work the defect usually lives in a system you cannot read, so a log stops being a debugging aid and becomes evidence you may have to show a third party. What that changes about the design of the record — capture as it left, keep the body on a 200, make it re-firable — and the three constraints that stop the evidence from becoming its own outage.

  • logging
  • integrations
  • observability
  • evidence