claude-code - 💡(How to fix) Fix Feature request: TDD + MVC + Component-Based Development as default patterns for web projects [1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#56473Fetched 2026-05-06 06:27:06
View on GitHub
Comments
1
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
labeled ×2commented ×1

Code Example

// 200-line page component that fetches data, manages state,
// renders UI, handles events, and has zero tests
export default function SearchPage() {
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(true);
  // ... 180 more lines mixing everything together
}

---

// Test first — defines what "done" means
test("search returns results matching the query", async () => {
  const res = await fetch("/api/v1/search?q=plumber");
  const data = await res.json();
  expect(data.listings[0].title).toMatch(/plumb/i);
});

// Component — renders one thing, no side effects
export function ListingCard({ listing }: { listing: Listing }) {
  return <Card>...</Card>;
}

// Page — assembles components, reads URL model
export default function SearchPage({ searchParams }) {
  const params = readSearchParams(searchParams);
  const results = await search(params);
  return <Grid>{results.map(r => <ListingCard listing={r} />)}</Grid>;
}
RAW_BUFFERClick to expand / collapse

The Ask

When Claude Code works on web projects, it should default to TDD (Test-Driven Development), MVC (Model-View-Controller), and Component-Based Development patterns. Not as suggestions — as the default operating mode.

This is a companion to issue #56444 but focused specifically on WHY these patterns matter for web development and the measurable quality improvement observed.

The Evidence — One Session

In a single session where TDD + MVC + CBD were explicitly enforced:

  • 43 issues closed in one day
  • 34 features shipped (daring, simple, pretty — all categories)
  • 478 i18n keys in sync across 2 languages
  • 550+ E2E tests passing
  • Zero regressions despite massive surface area changes
  • Code was simpler — fewer lines, fewer bugs, easier to read
  • Every feature had data-oriented tests proving it works, not just exists

Without these patterns enforced, the same codebase had:

  • Tests that checked "did the page load" but not "is the data correct"
  • Components mixing data fetching, presentation, and behavior
  • Features shipping without any tests at all
  • Regressions caught by the user, not the test suite

Why TDD Changes Everything for Web

The problem it solves

Claude's default mode is: build the feature, then maybe add tests. This means:

  1. The feature definition is in Claude's head, not in code
  2. "Done" is subjective — Claude thinks it works, the user finds out it doesn't
  3. Edge cases are discovered in production, not in development

What TDD forces

  1. The test IS the spec. Before writing any feature code, Claude writes a test that defines exactly what "done" means. Red → green → refactor.
  2. Data validation, not existence checking. A TDD test doesn't check "does the button exist" — it checks "does the button link to the correct listing ID." This is the difference between a test that catches bugs and a test that gives false confidence.
  3. Regressions are impossible to ship. If a change breaks something, the test fails before the code is committed. Not after deploy. Not after the user reports it.

Why MVC Changes Everything for Web

The URL is the Model

In a web application, the URL IS the source of truth. Every filter, sort, page number, and view state is a URL parameter. When Claude understands this:

  • State management becomes trivial (read from URL, write to URL)
  • Every view is shareable (copy the URL, paste it, same results)
  • Back/forward navigation works for free
  • Testing is simple (hit the URL, check the response)

Components are Views

Each component renders one thing based on the model (URL params + data). It doesn't fetch its own data, it doesn't manage global state, it doesn't know about other components. This makes components:

  • Independently testable
  • Reusable across pages
  • Simple to reason about

Event handlers are Controllers

User actions emit events. Events update the URL (model). The URL change triggers re-renders (view). This cycle is:

  • Predictable
  • Debuggable
  • Testable at every step

Why Component-Based Development Changes Everything

Convention over Configuration

When every piece of UI is a component with a predictable structure (props in, JSX out, no side effects), Claude:

  • Doesn't reinvent patterns per feature
  • Follows the existing codebase conventions automatically
  • Produces code that looks like it belongs in the project

Composition over inheritance

Small components compose into pages. Pages are containers, not monoliths. When Claude builds a new feature:

  • It creates a component, not a page
  • The component is tested individually
  • The page assembles components — that's it

What This Looks Like in Practice

Without these patterns (Claude's default):

// 200-line page component that fetches data, manages state,
// renders UI, handles events, and has zero tests
export default function SearchPage() {
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(true);
  // ... 180 more lines mixing everything together
}

With TDD + MVC + CBD enforced:

// Test first — defines what "done" means
test("search returns results matching the query", async () => {
  const res = await fetch("/api/v1/search?q=plumber");
  const data = await res.json();
  expect(data.listings[0].title).toMatch(/plumb/i);
});

// Component — renders one thing, no side effects
export function ListingCard({ listing }: { listing: Listing }) {
  return <Card>...</Card>;
}

// Page — assembles components, reads URL model
export default function SearchPage({ searchParams }) {
  const params = readSearchParams(searchParams);
  const results = await search(params);
  return <Grid>{results.map(r => <ListingCard listing={r} />)}</Grid>;
}

The second version is shorter, tested, composable, and maintainable. The first version is a liability.

The Request

  1. Detect web projects (Next.js, React, Vue, Angular, Svelte) and default to these patterns
  2. TDD by default: write test first, show it failing, write code to pass
  3. MVC awareness: URL as model, components as views, events as controllers
  4. Component isolation: every new UI element is a component, not inline JSX in a page
  5. Data-oriented tests: assert on content, not existence. "The price shows $85" not "the price element is visible"

Who Benefits

Every web developer using Claude Code. These aren't advanced patterns — they're fundamentals that produce dramatically better output. The quality difference is not marginal. It is transformative.

extent analysis

TL;DR

To improve code quality, default to Test-Driven Development (TDD), Model-View-Controller (MVC), and Component-Based Development (CBD) patterns in web projects.

Guidance

  • Detect web projects (e.g., Next.js, React, Vue, Angular, Svelte) and enforce TDD, MVC, and CBD patterns by default.
  • Write tests first, demonstrating a failing test, then write code to pass the test.
  • Implement MVC awareness by treating the URL as the model, components as views, and events as controllers.
  • Ensure component isolation by creating new UI elements as separate components, rather than inline JSX in pages.
  • Focus on data-oriented tests that assert on content, not just existence.

Example

// Test first — defines what "done" means
test("search returns results matching the query", async () => {
  const res = await fetch("/api/v1/search?q=plumber");
  const data = await res.json();
  expect(data.listings[0].title).toMatch(/plumb/i);
});

// Component — renders one thing, no side effects
export function ListingCard({ listing }: { listing: Listing }) {
  return <Card>...</Card>;
}

Notes

The provided patterns and examples are specific to web development and may not be directly applicable to other domains. However, the principles of TDD, MVC, and CBD can be beneficial in various contexts.

Recommendation

Apply the suggested patterns and practices to web projects to improve code quality, maintainability, and testability. This approach can lead to fewer bugs, easier debugging, and faster development.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix Feature request: TDD + MVC + Component-Based Development as default patterns for web projects [1 comments, 1 participants]