Production fell a day behind and nothing said a word
engineering5 min read
Builder · Applied AI

Production fell a day behind and nothing said a word

A build that fails pages you inside a minute. A deploy pipeline that quietly stops shipping says nothing at all, and a site on yesterday's code looks identical to one on today's. Production sat twenty-eight commits and nineteen hours behind main while every dashboard I owned stayed a calm, untroubled green. So I built a radar that catches the deploy that never happens.

A build that fails will page you inside a minute. A deploy pipeline that quietly stops shipping new commits does something worse. It says nothing at all. A production site serving yesterday's code looks identical to one serving today's, right down to the green checkmark on the last deploy that actually went out. I learned this the usual way, after the fact. Production had been sitting twenty-eight commits and roughly nineteen hours behind the tip of main, serving a stale build the entire time, and every deploy dashboard I owned was calm and untroubled and completely wrong. So I built a radar with one job: catch the deploy that never happens.

The failure a webhook cannot report

Deploy tooling is built around events. A build started. A build succeeded. A build failed. Those are the moments something can fire a webhook, and every one of them is a thing that happened. The failure that burned me was the opposite kind. It was a thing that stopped happening. Auto-deploy had quietly quit promoting commits, so there was no failed build to alert on and no error to catch, only a widening quiet where new deploys used to land. You cannot subscribe to an event that never fires. The one signal I needed was the exact signal an event stream can never send.

A stall is a comparison, not an event

So the radar does not wait to be told. It reads three signals and folds them with one small pure function. First, what is actually live, the commit the running deployment reports at the site's version endpoint. Second, the event stream, for the deploys that do happen. Third, the tip of main, read straight from GitHub, so a paused pipeline still shows up as behind. The verdict falls out of comparing what is running against where the code really is. That is what makes a stall visible even when nothing at all is deploying. I stopped asking whether a deploy had failed. I started asking whether one was missing.

what is live
a1b2c3 · 28 behind
the event stream
last READY: a1b2c3
the tip of main
9f4e7d · HEAD
classifyDeploy( live, events, main )one pure function · now injected · no Date inside
STALLEDlive is behind main, nothing in flight
The fold. What is live, the last deploy event, and the tip of main all go into one pure classifier. Here live sits twenty-eight commits behind main with nothing building, so the verdict is a stall.
js
// One pure classifier, `now` injected, no Date inside it.
// The dashboard, the probe, and the off-platform watcher all
// share it, so they can never disagree about what "stalled" means.
classifyDeploy({ liveSha, mainSha, behind, building, age });
//   liveSha === mainSha        -> 'current'
//   a build is in flight       -> 'building'
//   behind, deploy in flight   -> 'lagging'
//   behind, nothing deploying  -> 'stalled'   // the silent one
// The absence of a fresh deploy is the whole signal.
28commits behind main, nineteen hours, and not one alert fired
00+6+14+21+28+280CURRENT
currentlaggingstalled (no signal)building
what is live the running SHA / /api/versionthe event stream Vercel deploy webhooksthe tip of main read straight from GitHub
Production's verdict across recent windows. Green is current, yellow is a few commits behind, red is a stall with no deploy in flight, and blue is a build finally running. The long red middle is the day the site sat behind main saying nothing, then heals back to current.

The watcher lives off the thing it watches

One move matters more than it looks. The worst possible moment for a deploy pipeline is the day the platform running it is having its own bad day, and a stall detector that lives on that same platform goes down right alongside it, exactly when you need it awake. So the alerting half runs somewhere else entirely. It sits on a schedule on separate infrastructure, off the platform it watches. It reads the live version, compares it to main, classifies with the same function the dashboard uses, and pushes a message the instant it sees a stall. If the host is down, the watcher is still up. That is the whole reason it lives out of band.

The morning it caught the real thing

This is not a hypothetical. The first stall the radar ever flagged was the one that made me build it in the first place. I opened the dashboard one morning and the verdict was red. Production was twenty-eight commits behind main, and the oldest of those commits had been waiting to ship for roughly nineteen hours. The threshold that trips a stall is twenty minutes. A commit that lands on main should be live within that window, and past it, with nothing building, the pipeline is stuck rather than slow. Nineteen hours is that same window missed more than fifty times over, in total silence. Auto-deploy had quietly paused, and the last deploy that did go out was still sitting there green, which is the whole reason nothing had told me. The fix took a few minutes once I could see it. Seeing it was the part that had been impossible.

js
// classify.mjs: the real thresholds, and the branch that trips a stall.
export const DEFAULT_THRESHOLDS = {
  staleMinutes: 20, // behind main longer than this, nothing building -> stalled
  graceMinutes: 8,  // freshly behind: a deploy may still be coming -> lagging
};

// ...behind main, nothing in flight, and past the stale window:
if (ageMinutes == null || ageMinutes >= th.staleMinutes) {
  return out('stalled', 'behind-no-build',
    `Production is ${howBehind}, ${ageMinutes}m with no build in flight. ` +
    `Auto-deploy may be paused or failing.`);
}

Where it stops

Now the honest limits, because they carry the most weight. The reader is fail-soft. It reports only SHAs, states, and timestamps, never a word about what any deploy contains, and the whole page is admin-only. The webhook half accepts signed deliveries alone and fails closed the moment its secret goes missing, so nothing can spoof it into a false all-clear. And it tells me production is behind. It does not tell me why the pipeline stopped. That part of the diagnosis stays mine. This is the same shape as the heartbeat I built for scheduled jobs, turned now on the one system every other green quietly depends on. A deploy that silently did nothing is just a cron that silently did nothing, with the entire site sitting downstream. That is why the two of them share a screen on the burn dashboard.

Experience it yourselfSee the deploy radar
ShareXLinkedInHacker NewsEmail

Get the next one

An occasional note when something genuinely new ships here — essays, free tools, projects. No schedule, no filler, easy out.

Need something like this built?

I design and ship AI tools, full-stack apps, and data pipelines — end to end, to production. Tell me the problem in a sentence; I'll give you an honest read on fit within a day.

Work with me →