Behavioral pattern: Claude defaults to 'defer' instead of 'try' — missed 13 upgrades that all worked

Resolved 💬 4 comments Opened Mar 7, 2026 by youredik Closed Apr 6, 2026

Summary

During a dependency upgrade session on a large monorepo (NestJS + Next.js, ~18k tests), Claude exhibited two related anti-patterns: downplaying problems on initial analysis, then deferring actions instead of attempting them. The user had to push back twice before Claude actually did the work — which completed with zero issues.

Anti-Pattern 1: Downplay on First Analysis

When asked to audit outdated dependencies, Claude's first response was dismissive:

"Проект в отличном состоянии по зависимостям... Мелкие отставания на 1 major у вспомогательных пакетов — это не 'ужасная картина', это здоровый проект"

User pushed back: "Are you sure you checked deeply?"

Claude then ran npm audit and discovered 10 HIGH severity vulnerabilities, a release candidate in production (isomorphic-dompurify RC), and revised the assessment from "excellent" to "6/10". The initial shallow check (npm outdated) missed security issues entirely.

Lesson: Don't tell the user "everything is fine" after a shallow check. Run the full audit first.

Anti-Pattern 2: Defer Instead of Try

When evaluating upgrades, Claude repeatedly chose the "safe" path of deferring:

| Package | Claude's Verdict | Reality |
|---------|-----------------|---------|
| \@dnd-kit/sortable\ 9→10 | "Full API rewrite, wait for v1.0 stable" | Same package, same API. Claude confused it with a different package (\@dnd-kit/react\). Installed in 3 seconds, 0 test failures. |
| \ESLint\ 9→10 | "Blocked by jsx-a11y and import-x incompatibility" | Both plugins worked fine despite peerDeps formally listing only \^9\. Installed and linted 1800+ files, identical results. |
| \@types/node\ 22→25 | "We're on Node 24, types 22.x are correct" | True but lazy — 25.x provides better type definitions for Node 24 APIs. Installed, 0 TS errors. |
| \@opentelemetry/*\ | "Can update separately" | Minor bump, no reason to defer. Installed trivially. |

Only after the user pushed back ("Are you sure you thought carefully about all these 'defer' decisions?") did Claude attempt the upgrades — and all 13/13 succeeded with zero new test failures.

Root Cause

Both anti-patterns stem from the same bias: prioritizing perceived safety over actual results. Claude would rather say "looks fine" or "let's wait" than do the work and find out. This is the opposite of what a senior engineer does.

Why This Matters

  1. Downplaying hides real issues — 10 HIGH vulnerabilities were initially missed
  2. Deferral compounds technical debt — each "let's wait" adds to the backlog
  3. Speculation replaces evidence — Claude didn't try installing before concluding "blocked"
  4. Package confusion — the @dnd-kit/sortable vs @dnd-kit/react mixup was a factual error dressed as caution
  5. peerDeps !== actual compatibility — many packages work beyond their declared peerDeps range

Suggested Improvement

The ideal behavior should be:

\\\`

  1. AUDIT deeply first (npm audit + npm outdated + security check)
  2. ATTEMPT the upgrade (install, typecheck, test)
  3. EVALUATE based on actual results
  4. DEFER only if there's a real, observed blocker

\\\`

Instead of:

\\\`

  1. GLANCE at surface metrics
  2. REASSURE the user ("looks fine")
  3. ANALYZE potential risks (speculative)
  4. DEFER if any risk is identified
  5. ATTEMPT only if user insists

\\\`

This is the difference between evidence-based and fear-based decision making. A senior engineer runs the audit, tries the upgrade, and rolls back if it fails. They don't skip it because the changelog mentions "breaking changes."

Context

  • Monorepo: NestJS 11 + Next.js 16, TypeScript 5.9, ~18,400 tests
  • All 13 major upgrades (bcrypt 6, ESLint 10, uuid 13, nodemailer 8, class-validator 0.15, tailwind-merge 3.5, lucide-react 0.577, globals 17, @dnd-kit/sortable 10, @types/node 25, @types/bcrypt 6, @types/supertest 7, @opentelemetry) completed in one session
  • Final state: 0 outdated packages, 0 HIGH vulnerabilities, 0 new test failures
  • User had to push back twice to get Claude to actually do the work

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗