The Drift Series Part One: Nothing is Failing, and That's the Problem

Part 1 of The Drift Series: What 20 production repos taught us about code that ships anyway.

Jonathan Gordon

Closeup of a brick wall with a large vertical crack

Key findings

ReWeaver AI’s DriftDetector scanned 20 open-source repositories (several of them widely used) across nine production-readiness dimensions: accessibility, reliability, testability, user experience, architecture, security & privacy, AI code governance, maintainability, and design consistency.

Researchers then hand-reviewed 125 of the resulting findings, reading the actual code at a pinned commit for each one. This post covers five of those findings in detail: silent failures that passed code review, passed testing, and shipped to production without ever surfacing as an error, a failed test, or a crash.

Finding

Repo Type

Domain

Severity

Logging out doesn’t end the server-side session

AI knowledge platform (logout handler)

Reliability

Critical

The core feature fails silently and returns a blank form

Haircare ingredient analyzer

Reliability

Critical

A billing enforcement lock can silently fail to lock

LLM knowledge base platform

Reliability

Critical

A banned partner may never receive the ban notification

Link attribution/ short-link platform

Reliability

Critical

The design system sized the button correctly. The call site overrode it.

Link attribution platform

Accessibility

Warning

What is “drift”?

Drift is what happens when a codebase stops matching its own decisions — quietly, without breaking a test or failing a build. It shows up as a color value written out instead of referencing the design token that should hold it, a catch block that logs an error and continues instead of raising it, or a test disabled for being flaky and never re-enabled. Drift occurs in both human-written and AI-written code; the open question isn’t whether AI causes it, but how much faster it now accumulates.

None of the five examples below broke a build, failed a test, or triggered an alert. Each one shipped, and each one reported success. That’s the whole problem this series is about.

1: Logging out does not end the session

The finding, in one line: A logout handler clears the browser cookie but silently fails to delete the server-side session if Redis is unavailable, leaving the session valid and the user unaware.

REPO: Logout handler of an AI knowledge platform
DOMAIN: Reliability
DETECTORS: empty-catch-block, error-masking-catchall
SEVERITY: Critical

6 async function handler(req: NextApiRequest, res: NextApiResponse) {
7 try {
8 const { userId } = await authCert({ req, authToken: true });
9 await delUserAllSession(userId);
10 } catch (error) {}
11
12 clearCookie(res);
13 }

WHAT HAPPENS: delUserAllSession is a Redis call — it opens a connection and deletes the user’s session keys. If Redis is slow, down, or drops the connection, it throws. The catch swallows it, and execution continues straight to clearCookie.

WHAT IT COSTS: The browser cookie is cleared, so the user sees a successful logout. But the server-side sessions are still sitting in Redis, still valid. Anyone still holding that token stays authenticated — and the catch destroyed the only signal that any of this happened.

2: The main feature quietly does nothing

The finding, in one line: The core analysis function fails silently on any error, and the app re-renders a blank form indistinguishable from the page never having been used.

REPO: A haircare ingredient analyzer
DOMAIN: Reliability
DETECTORS: empty-catch-block, error-masking-catchall
SEVERITY: Critical

54 let analysis = null;
55 if (ingredients) {
56 try {
57 const analyzer = new Analyzer();
58 analysis = analyzer.analyze(ingredients);
59 } catch (e) {}
60 }
61
62 return (
63 <IngredientForm

WHAT HAPPENS: analyzer.analyze is the product. If it throws on any input, analysis stays null, and the component renders the empty form as though nothing had been submitted.

WHAT IT COSTS: A user pastes an ingredient list, submits, and gets the blank form back. No result, no error, no retry prompt. It’s indistinguishable from never having pressed the button — and nothing is ever recorded server-side either.

3: The out-of-credit lock that never locks

The finding, in one line: A billing-enforcement step (freezing an over-limit account) is wrapped in a catch block that discards failures, so the enforcement can silently no-op while reporting normal operation.

REPO: LLM knowledge base platform
DOMAIN: Reliability
DETECTORS: empty-catch-block, error-masking-catchall
SEVERITY: Critical

13 } catch (error: any) {
14 if (error === TeamErrEnum.aiPointsNotEnough) {
15 // send inform and lock data
16 try {
17 sendOneInform({
18 level: InformLevelEnum.emergency,
19 templateCode: 'LACK_OF_POINTS',
20 templateParam: {},
21 teamId
22 });
23 logger.info('余额不足,暂停知识库处理');
24 await lockTrainingDataByTeamId(teamId);
25 } catch (error) {}
26 }
27 return false;
28 }

Billing limits only work if the enforcement actually runs. If enforcement is best-effort, the limit is a suggestion. This is the path that runs when a customer exhausts their credits.

WHAT HAPPENS: Notify, then freeze training. If lockTrainingDataByTeamId throws, the inner catch drops it, and the function returns false exactly as it would have anyway.

WHAT IT COSTS: The enforcement step is the thing that failed, and its failure is the thing nobody hears about. Training data keeps processing on an account that should be frozen, and the emergency notification may not have gone out either.

4: A partner is banned and may never be told

The finding, in one line: A partner-ban workflow applies the ban successfully, but a failed notification email is silently discarded, leaving the partner locked out with no explanation on record.

REPO: Link attribution and short-link platform
DOMAIN: Reliability
DETECTORS: empty-catch-block, error-masking-catchall
SEVERITY: Critical

140 // Send email
...
153 try {
154 await sendEmail({
155 to: partner.email,
156 subject: You've been banned from the ${program.name} Partner Program,
...
169 bannedReason: programEnrollment.bannedReason
170 ? BAN_PARTNER_REASONS[programEnrollment.bannedReason!]
171 : "",
172 }),
173 });
174 } catch {}
175 }
176
177 return logAndRespond(

Some actions have two halves: do the thing, then tell the person. If the second half is allowed to fail quietly, you get a system that acts without explaining itself. This is a partner being banned.

WHAT HAPPENS: The ban is applied, then the notification is sent. If the send throws — a provider outage, a rejected address — the catch discards it and execution continues to the success response.

WHAT IT COSTS: A partner loses access with no explanation, and the operator has no way to know it happened: the endpoint reported “Partner banned from the program,” and nothing recorded that the reason never reached them.

5: The design system sized the button correctly. The call site overrode it.

The finding, in one line: A design-system button component defaults to a WCAG-compliant 40px tap target, but a call-site CSS override shrinks it to 16px (two-thirds of the accessibility minimum) on six controls across the app.

REPO: Link attribution and short-link platform
DOMAIN: Accessibility
DETECTORS: hardcoded-size-override
SEVERITY: Warning

297 {values.length > 1 && (
298 <Button
299 variant="outline"
300 className="h-6 w-fit px-1"
301 icon={}
302 onClick={() => handleDelete(id)}
303 />
304 )}
305
306 ))}
307 <Button
308 variant="outline"
309 className="h-4 w-full px-1"
310 icon={}
311 onClick={handleAppend}
312 />

This is a clean example of drift. The system wasn’t wrong, and nobody wrote a bad button. A local height class silently defeated a correct default, one call site at a time. The delete button four lines above sits at h-6: the same file, drifting to two different answers.

WHAT HAPPENS: Button is dub’s own design-system component, and it ships h-10 (packages/ui/src/button.tsx:108) — 40px, comfortably above the WCAG 2.5.8 minimum of 24×24. A className at the call site above replaces that with h-4: 16px, two-thirds of the minimum and 40% of what the system intended.

WHAT IT COSTS: Nothing errors and nothing looks broken on a desktop with a mouse. On a phone, the control is simply hard to hit, and the person who misses it has no way to tell they missed it rather than that the app ignored them. Six controls across the app land below the minimum, at h-4 and h-5.

Frequently asked questions

What is code drift?

Code drift is the gap between what a codebase does and what its own standards, defaults, and prior decisions say it should do. It accumulates silently because it doesn’t fail a build or a test. Examples include hardcoded values that bypass design tokens, errors that are caught and discarded instead of surfaced, and tests disabled for flakiness and never restored.

What is DriftDetector?

DriftDetector is a free tool from ReWeaver AI that scans a GitHub repository across nine production-readiness dimensions — security, accessibility, reliability, architecture, and more — and reports exactly where the code has drifted, with a file, a line, and a severity for each finding.

Is DriftDetector free to use?

Yes. Public repositories can be scanned at no cost and with no signup at drift.reweaver.ai.

Is DriftDetector an AI code reviewer?

No. DriftDetector is deterministic and rule-based, not an LLM guessing at your code. Scanning the same repository twice returns the same findings both times, and every finding links to a specific file and line rather than a generated explanation.

Does AI-written code drift more than human-written code?

Both AI-written and human-written code drift — the examples in this post include both. Part 4 of this series measures the difference directly across all 20 repositories, comparing findings-per-line by who wrote the code.

How many repositories and findings does this series cover?

The study scanned 20 open-source repositories and manually reviewed 125 individual findings, reading the source code for each at a pinned commit.

Try ReWeaver AI DriftDetector yourself

We found all five of these by running DriftDetector, our free, deterministic scanner, across these repos. It’s not an AI reviewer guessing at your code. Instead, it's the same repo in, same findings out, every time, with a file and a line for each one. If you want to see what it finds in yours: drift.reweaver.ai

Next in the series: 12 Ways to Fail a User; the shorter, quieter failures across reliability, security, and accessibility that didn’t need a whole post each, but add up to the same thing.

————————————

JONATHAN GORDON is the Founder & CEO of ReWeaver AI, a platform that detects design-code drift at the point of generation in AI-assisted development. With nearly three decades of experience, he has shaped developer tools and enterprise software at Google, Apple, Microsoft, Oracle, and SAP. He holds two patents and specializes in human-centered design for complex systems, AI/ML integration, and developer tooling.