# Routing pattern consistency

- **Pattern:** `ab-000231` (`ai-slop-code-drift.convention-drift.routing-pattern-consistency`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000231
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000231)

## Why it matters

Next.js routing resolution between `app/` and `pages/` follows non-obvious precedence rules: App Router wins for matching segments, but Pages Router catches everything App Router does not claim. Deploying a half-migrated app can change which route renders simply because someone added or removed a file, with no local indication. SEO metadata, layouts, and data-fetching conventions differ completely between the two routers, producing broken canonical URLs and duplicated or missing OG tags.

## Severity rationale

Medium because resolution ambiguity can swap which page renders at a given URL without any code change to that page.

## Remediation

Finish the migration to App Router. Enumerate what remains:

```bash
find pages -type f -name "*.tsx" | grep -vE '_app|_document|_error|api/'
```

Move each remaining page into `app/` (e.g., `pages/about.tsx` becomes `app/about/page.tsx`), port `getServerSideProps` to server components or route handlers, then delete the old file. Keep `pages/api/` until those endpoints are migrated to `app/api/` route handlers. Redeploy and verify every URL resolves through the expected router.

## Detection

- **ID:** `routing-pattern-consistency`
- **Severity:** `medium`
- **What to look for:** When the framework is Next.js, check whether BOTH `app/` AND `pages/` directories exist at the project root or under `src/`. Enumerate every file under each. Pages Router files (`pages/index.tsx`, `pages/about.tsx`, etc.) and App Router files (`app/page.tsx`, `app/about/page.tsx`, etc.) are mutually exclusive routing systems. EXCEPT: a `pages/` directory containing ONLY `_app.{tsx,jsx,ts,js}`, `_document.{tsx,jsx,ts,js}`, `_error.{tsx,jsx,ts,js}`, OR `pages/api/**` files (legacy API routes are allowed alongside App Router) is acceptable. Count all non-allowed files in `pages/` when `app/` also exists. The threshold for failure is at least 1 non-allowlisted file in pages/ when app/ also has page files.
- **Pass criteria:** When framework is Next.js: only one routing system in active use, OR `app/` is the active router AND `pages/` only contains the allowlisted exception files. Report even on pass: "Routing pattern: App Router (or Pages Router). [N] route files."
- **Fail criteria:** Both `app/` and `pages/` contain non-allowlisted route files — mid-migration state with unclear ownership.
- **Skip (N/A) when:** Framework is not Next.js OR only one of `app/`/`pages/` exists.
- **Detail on fail:** `"Both routing patterns active: app/ has 12 page files, pages/ has 5 page files (excluding _app, _document, api/). The two routers conflict — pick one and finish the migration."`
- **Remediation:** Mixing App Router and Pages Router pages means each route's behavior depends on which router resolves it first — and Next.js's resolution rules are not obvious. Pick the destination and finish the migration:

  ```bash
  # Inventory non-allowlisted pages/ files
  find pages -type f -name "*.tsx" | grep -vE '_app|_document|_error|api/'

  # Move each to app/ following the new conventions
  # pages/about.tsx → app/about/page.tsx
  # Then delete the pages/ file
  ```

Taxons: dependency-coherence

HTML version: https://auditbuffet.com/patterns/ab-000231
