AI Removed Working Logic During Refactor

AI "simplified" code and quietly removed a branch your users actually rely on.

You asked Claude Code or Cursor to “refactor this handler — make it cleaner.” The diff looked great: 50 lines down to 28. You merged. Two days later support tickets show “old users hit the wrong page after login” or “Safari uploads fail.” The agent decided a branch “looked unused” and removed it — but that branch was the fallback for a real customer scenario.

This guide gives a path to bisect the regression, cross-reference production logs to find the branches actually being hit, restore the deleted logic, and add tests so it can’t be deleted silently again.

Common causes

Ordered by hit rate.

1. AI treats “no tests” as “dead code”

The most common mode. The agent sees if (user.legacyRole === "admin") with no covering test and removes it. But legacy admins are 2% of prod traffic — the day you ship, somebody gets 401’d.

- if (user.legacyRole === "admin" || user.role === "admin") {
-   return grantAdminAccess(user);
- }
+ if (user.role === "admin") {
+   return grantAdminAccess(user);
+ }

How to spot it: The removed branch references legacy, old, v1, fallback, deprecated, or an inactive-but-still-present field.

2. Edge cases weren’t in the prompt

You said “simplify this parser.” You didn’t say “must preserve handling for empty strings, zero-width Unicode, CRLF.” The agent stripped what it saw as “overly defensive” null checks and normalization.

- if (!input || input.trim() === "") return defaultValue;
- input = input.normalize("NFC").replace(/​/g, "");
  return parse(input);

How to spot it: Prod throws TypeError: Cannot read properties of undefined or encoding errors, with stack traces pointing into the refactored function.

3. Refactor scope was vague

“Clean up this file” or “modernize this code” lets the agent freelance. It removes retry, timeout, or circuit-breaker logic that “looked like a hack” — because the comment explaining why it was added wasn’t there.

How to spot it: The deleted code has no surrounding explanatory comments, but git blame shows it was added in a post-incident commit.

4. AI mistook a feature flag for a deprecated branch

- if (isFeatureEnabled("new-checkout", user)) {
-   return newCheckoutFlow(user);
- }
- return oldCheckoutFlow(user);
+ return newCheckoutFlow(user);

The agent assumed the flag was fully rolled out and removed the other side. The flag was still at 50% — half your users suddenly land in an under-tested path.

How to spot it: The deleted code includes isFeatureEnabled / getFlag / LaunchDarkly-style switches.

5. AI simplified error handling and removed an intentional swallow

Some try { ... } catch (e) { /* intentionally swallowed */ } blocks are deliberate (analytics shouldn’t block checkout). The agent sees the empty catch and “fixes” it to throw or logger.error — and now checkout fails when analytics 500s.

How to spot it: A spike of new ERROR-level log entries that didn’t exist before, plus business operations starting to break.

6. AI merged “duplicate” overloads with different signatures

The agent combined getUserById(id: string) and getUserById(id: number) into one, dropping the string-ID fallback parse. Old URLs still send string IDs and immediately 404.

How to spot it: TypeScript overload errors or runtime id is undefined.

Shortest path to fix

Step 1: Run the full test suite and see what breaks

npm test -- --reporter=verbose 2>&1 | tee /tmp/test.log

Don’t trust tests alone — if the deleted branch had no coverage, tests will pass while users break. Also run:

# E2E / integration
npm run test:e2e

# Walk the key business scenarios manually from a checklist

Step 2: Cross-reference prod logs for branches that actually run

Open the last 7 days of prod logs and check the entry points of the changed file:

# e.g. distribution of user agents / roles hitting the checkout handler
grep "POST /api/checkout" /var/log/app.log | awk '{print $7}' | sort | uniq -c

Output reveals “what code paths are alive in prod” — e.g. 8% of traffic is IE/Safari, and the agent removed the branch handling them.

Step 3: git bisect to find the regression commit

If multiple commits landed between known-good and known-bad, bisect is fastest:

git bisect start
git bisect bad HEAD                    # currently broken
git bisect good v1.2.3                 # last known-good tag
git bisect run npm test -- failing-spec
# or step manually: git bisect good/bad each round

In a few rounds you’ll have the exact AI commit that introduced the regression.

Step 4: Diff and restore the removed logic

git show <bad-commit> -- src/checkout/handler.ts

For each deleted block, decide:

Removed codeAction
Truly deadKeep deleted; commit message explains why
Still used but outdatedModernize the syntax, preserve the behavior
Handled an edge caseRevert that hunk as-is
Feature flag branchCheck flag rollout; revert if < 100%

Restore a single hunk:

git checkout <good-commit> -- src/checkout/handler.ts
# or hunk-by-hunk
git checkout -p <good-commit> -- src/checkout/handler.ts

Step 5: Add tests before committing

Logic the AI deleted once will be deleted again. Add a regression test immediately:

// Example: legacy admin must still log in
test("legacy admin role still grants access (regression: removed in refactor 2026-05-22)", () => {
  const user = { id: 1, legacyRole: "admin" };
  expect(authorize(user)).toBe(true);
});

The test name should encode why — not just “test legacy admin,” but “legacy admin role still grants access (regression: removed in refactor 2026-05-22).”

Prevention

  • Only refactor code that has test coverage — if there are no tests, write them before refactoring
  • List preserved behaviors explicitly in the prompt: "keep handling for empty strings, IE11, legacy admin"
  • Comment important edge-case handling with // IMPORTANT: handles X case from incident #1234 — agents respect explained code
  • Add to CLAUDE.md / AGENTS.md: “do not remove branches that have comments, reference feature flags, or contain ‘legacy’ / ‘fallback’ keywords, unless explicitly told to”
  • Large refactors must go through PR; any diff with > 30 deleted lines requires a second reviewer
  • Use the AI pre-commit review workflow — have the agent diff its own output and justify each deletion

Tags: #AI coding #Debug #Troubleshooting