Feature request: TDD + MVC + Component-Based Development as default patterns for web projects

Resolved 💬 2 comments Opened May 6, 2026 by jodybrownell Closed Jun 4, 2026

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.

View original on GitHub ↗

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