as.
Writing /
Production reliability · 21 Oct 2026 · 4 min read

A failed API call is where debugging starts

How I think about tracing a failed request across application code, database access, and downstream dependencies.

backendreliabilitydebugging
On this pageFirst, describe the failure preciselyFollow one request before widening the searchMake each signal answer a questionKeep competing explanations openUseful evidence should not expose the userA fix should come with a way to check it

I have worked on both sides of an API: building application features and investigating failed requests. My reliability work involved REST APIs, MongoDB, and downstream dependencies. My development work now includes backend APIs and service integrations.

Those two sides of the work meet at a familiar question: a request failed, but where did the useful explanation stop?

An error shown by a client is evidence about what the client observed. I still need to work out what happened along the request path.

First, describe the failure precisely

"The API is failing" leaves too many possibilities open. Did it return an error response? Did the connection fail? Did a request time out? Did it return a successful status with a result the caller could not use?

I would start by recording the operation, the approximate time, the observed response, and whether the problem affects one request shape or several. That gives the investigation a boundary.

Even a specific status needs interpretation. A 504 response indicates that a gateway or proxy did not receive a timely upstream response. It does not, by itself, identify the component responsible for the delay.

Increasing a timeout might change what the user sees. It would not explain why the request took longer than expected.

For a typical application, I would sketch a simple path:

Caller -> API handler -> application operation -> dependency

Then I would ask what evidence exists at each boundary. Did the application receive the request? Did validation finish? Was a database or external-service call attempted? Was a response sent back?

This is a generic investigation model, not a description of an internal deployment.

I want to separate "this step completed" from "I found no error for this step." Missing evidence can mean logging is incomplete, the wrong time window was searched, or the request never reached that component.

Make each signal answer a question

More log lines are not automatically more useful. I care about whether a signal can narrow the investigation.

SignalQuestion it helps answer
Request identifierAm I looking at the same request across the available records?
Route and outcomeWhich operation failed, and how was that failure reported?
Dependency durationWhere did this attempt spend time?
Error categoryWas the failure validation, retrieval, connectivity, or something else?

The available signals will vary between applications. I would start with what is recorded, then identify the gaps that prevent me from following the request.

I would also keep the scope of a measurement clear. Total request duration and the duration of one database call answer different questions. A process-level resource measurement cannot tell me which request caused a problem without additional evidence.

Keep competing explanations open

Suppose one request fails and a similar request succeeds. The difference may be in the input, the records being retrieved, the application state, or a dependency's response. I would compare those differences before making a broad infrastructure conclusion.

Likewise, a database-related error can describe several situations: an unavailable connection, a rejected query, an unexpected record shape, or an application assumption that does not hold. The word "database" is a starting point for investigation, not a complete diagnosis.

A useful next check is one that can rule out an explanation. Repeating the same request without changing what I observe may only produce another copy of the symptom.

Useful evidence should not expose the user

A request body can contain information that has no place in a troubleshooting log. I would rather record a safe identifier and a narrow description of the failure than copy a full profile or document into logs for convenience.

The same care applies when sharing an example outside the team. A small, synthetic input can explain a failure pattern without exposing a customer's records, internal addresses, or configuration.

A fix should come with a way to check it

Before changing code or configuration, I would state what the change is meant to correct and what observation would support that explanation. After the change, I would repeat the relevant check and consider adjacent behavior that could be affected.

I do not want "the error disappeared" to carry more certainty than the evidence supports. I want to be able to explain the original behavior, the reason for the change, and the limits of what was verified.

Related articles

Why I separate controllers, services, and repositories ↗