Engineering notes

Why your CI dashboard doesn't catch dependency drift

Classic CI dashboards are tuned for the green-or-red question. They answer it well. They just can't answer the harder one — the one that wakes you up at 2 a.m.

Published · Bowerpost engineering

Every engineering lead has lived the same scene. Green CI, clean main, dashboard happy. Then, six weeks later, a service starts timing out in production because a transitive dependency rearranged itself under a stale lockfile. The build was never broken — your system was. Your dashboard had no opinion about that.

That gap is what we mean by CI dashboard dependency drift: a slow, silent divergence of dependency versions across repos that build pipelines, by design, can't see. This post is about why the dashboards you already trust handle the easy half of the problem, what they miss, and how Bowerpost's nightly digest and drift graph fill the rest.

What a CI dashboard actually shows

A conventional CI dashboard is, first and foremost, a build-status surface. It answers four questions, and it answers them quickly: which branch last turned red, which run is blocking a deploy, which tests failed, and which flaky tests have started hiding real failures. Everything in the dashboard is keyed to pipeline runs — commit, workflow, exit code, duration.

That model is right. Pipelines are the unit of “does this code still build?”, and the dashboard's job is to make that answer legible. It does the job well. The trouble starts where this model ends — at the border of a single repo.

Where it goes blind

The moment your platform is more than one repository, the dashboard's per-pipeline view starts to miss the things that hurt most. Every CI run is its own universe; it builds the lockfile it has and tests the dependencies it resolved. Nothing in that view knows what your other services resolved last week. So when service A bumps lodash from 4.17.20 to 4.17.21, and service B keeps a floating semver range that resolves to 4.17.19, every dashboard for both repos stays green. The system is split.

Multiply that by hundreds of dependencies and dozens of repos. Lockfile skew, semver-range creep, transitive resolution, security advisories that landed in a range you technically consume but don't pin to — none of these have a pipeline that fails on them. They show up, if at all, as background noise in a Renovate pull request, then drift again the next time a long-lived branch re-merges.

Why drift survives a green build

The honest framing is a semantic one. CI dashboards answer “can this commit build the code I have right now?” They do not answer “is the platform I'm shipping composed of consistent, current dependencies?” Those two questions drift apart as soon as a single project has multiple consumers of the same package, or as soon as a long-lived branch carries stale pins across a release.

The classic case is the lodash scenario above. Or an @types/node pinned in two repos to two majors. Or a transitive crypto library that quietly downgraded because a sibling bump relaxed a peer range. None of these break a build in the moment — they accumulate, and one day a runtime feature depends on the newer shape, and the older thing fails at 2 a.m. in a way no CI run could have flagged.

From build status to engineering health

Build status is one signal. It's necessary, not sufficient. Continuous engineering health is the broader idea: at any given moment, a lead should be able to answer “what is the state of my platform?” — across CI status, dep drift, stale PRs, and incident triage — without cobbling it together from six logs. CI dashboards cover the first term well; the other terms are what Bowerpost exists to cover in the same shape.

What makes engineering health hard is that it's inherently cross-repo and inherently asynchronous. Individual tools are reactive per-event; the platform view has to be nightly, batched, and opinionated.

Enter Bowerpost: the nightly digest + drift graph

Bowerpost reads every connected repo once a night and produces two surfaces that fill the gap your CI dashboard leaves open. The first is the nightly digest: a single email summarising what changed today — failed pipelines, newly stale PRs, new drift between repos — instead of forty separate pings. The second is the drift graph at /dashboard/dep-drift, which renders every cross-repo version disagreement as a node-level map so you can see, at a glance, where two services have quietly split on the same package.

Both surfaces are batched by design: they're the answer to “what's the state of my platform?” rather than “what just happened in this PR?” CI keeps its job — the green/red signal, the per-pipeline view, the run blocked on this exact commit. Bowerpost owns the other half: the per-system, cross-repo, nightly view that no dashboard could ever have surfaced without becoming a different tool.

If you've ever lost a weekend to a drift bug postmortem, this is the version of the workflow where that shows up as a one-line item in your digest the morning before it bites. The point isn't alerts — the point is visibility at the system level, in a form an engineering lead can act on.

You can see both surfaces today. The home page at / shows what the digest produces; connecting a repo takes an afternoon, and the onboarding flow walks through it. If you'd rather talk it through first, write to bowerpost-2@polsia.app — we read every message.