Home/Learn/Full-Stack Integration/Debugging Across the Seam

Debugging Across the Seam

Advanced
Deploying Both

When something fails between two deployables, the first job is deciding which side it is on. A method for that is worth more than knowledge of any particular bug.

Overview

The hardest bugs in a full-stack app are not in either half — they are in the gap, where each side looks correct in isolation. The frontend developer sees a failed request and blames the API; the backend developer sees a 200 in the logs and blames the client. A short, ordered method resolves nearly all of these in minutes: look at the network tab, reproduce with curl, then read the server log for the same request id. This concept is the closing one because it is what you actually do on the job.

Which Side Is It On

Five checks, in order. Stop as soon as one answers the question.

Network tab, status, curl, payload, logs
// 1. Did the request leave the browser?
//    Network tab, Fetch/XHR filter. No row at all -> a client bug:
//    a handler that never fired, a guard that returned early, a
//    URL built as "undefined/problems" from a missing env var.

// 2. What status came back?
//    (failed) with no status  -> network, DNS, CORS or CSP
//    401/403                  -> auth: check the Request Headers for
//                                the cookie or Authorization header
//    422                      -> read the body; it names the fields
//    5xx                      -> server side; go to the logs

// 3. Does curl reproduce it?
curl -i https://api.aicancode.org/problems \
     -H "Origin: https://aicancode.org" -b "sid=..."
//    curl works, browser fails  -> CORS, cookies, or CSP
//    curl fails too             -> a genuine server bug

// 4. Is it the payload? Compare what you SENT with what the API
//    expects. "Copy as cURL" from the network tab gives the exact
//    request, headers included.

// 5. Server logs, filtered by the request id from step 2.

// The single most useful habit: check the network tab before
// forming a theory. Most disagreements about whose bug it is end
// there.

The Recurring Cast

Bugs that live specifically in the seam, and their tells.

Nine symptoms with a single likely cause each
// "CORS error" that is really a 500
//   The server crashed before the CORS middleware ran, so no headers.
//   Tell: the server log has a stack trace at the same timestamp.

// Works in Postman, fails in the browser
//   Postman sends no Origin and ignores CORS. Always a CORS or
//   cookie difference.

// 401 only in production
//   Cross-site cookie. See the domains concept.

// 401 only on the first load, then fine
//   Server-side rendering fetched without forwarding cookies.

// 422 with an empty form error
//   The field names disagree — camelCase vs snake_case — so the
//   server rejects fields the client never sent under those names.

// Works locally, 404 in CI or production
//   A case-sensitive filesystem. './Button' vs './button'.

// Intermittent failures under load
//   A connection pool exhausted, or a request timeout shorter than
//   the p99. Look at latency percentiles, not averages.

// Data appears twice
//   A retried POST without an idempotency key, or a double-submit
//   from a button that was not disabled.

Making the Next One Easier

What to have in place so the seam is observable by default.

Request ids, structured logs, both-end tracking
// 1. A request id on every request, echoed in the response and
//    logged on both sides. One id turns "it broke yesterday" into a
//    log line. (See the errors concept.)

// 2. Structured logs, not printed strings — so you can filter
log.info("request", path=..., status=..., ms=..., user_id=..., request_id=...)

// 3. A /health endpoint that checks the database, and an uptime
//    check hitting it. Know before the user tells you.

// 4. Error tracking on BOTH ends, joined by the request id.
//    Client-side Sentry catches what never reaches your server —
//    which is precisely the class of bug this concept is about.

// 5. Alert on rate and shape: a spike in 401s means auth broke; a
//    spike in 422s usually means the frontend started sending
//    something new; a spike in 499/timeouts means something is slow.

// 6. A staging environment with real domains and real TLS. Local
//    cannot show you a cross-site cookie failure; staging can.

// The measure of a good setup is how long it takes to answer
// "which side is this on?" — minutes with the above, an afternoon
// without.

Key Points to Remember

  • 1Check the network tab before forming a theory — no request row at all means the bug is client-side
  • 2A CORS error with no server headers is often a 500 that crashed before the CORS middleware ran
  • 3Postman and curl ignore CORS and send no Origin, so "works in Postman" always points at CORS or cookies
  • 4Server-rendered requests need cookies forwarded explicitly, which is why a 401 can appear only on first load
  • 5A request id echoed and logged on both ends is the single highest-value piece of full-stack observability

Interview Questions

Sign in to ask Aria
1

A request fails in the browser but works in Postman. How do you diagnose it?

Medium
2

How do you decide whether a bug is in the frontend or the backend?

Medium
3

What would you put in place so the next cross-stack bug is faster to diagnose?

Hard

Ask Aria about Debugging Across the Seam

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…