Unit 1 · Module 5

Reading A Real Codebase

PetKarma, Opsette, and the scar tissue of real projects

The scar tissue of real projects

Every textbook shows a clean, neat file structure. Every real project has scar tissue. Layers of history. Decisions made before a migration, after a framework change, during a refactor that never quite finished.

That's not a flaw — that's what a real codebase looks like. And learning to read that history is part of navigating any project you didn't build yourself.

PetKarma: a clean migration

PetKarma started on Express. At some point it got migrated to Next.js. Today its top level looks like this:

pet-karma/

.claude/        ← Claude Code config
.vscode/        ← VS Code settings
app/            ← routes and layouts (Next.js App Router)
components/     ← reusable UI
docs/           ← documentation
hooks/          ← reusable logic
inspo/          ← design references (builder-only)
lib/            ← general utilities
migrations/     ← database migrations
public/         ← static assets
services/       ← external integrations

Almost every folder you can name from what we've already covered. migrations holds your database change history. inspo is a builder convention, not a framework one — it's a folder for keeping design references. Projects can have folders that exist just for the humans building them.

Inside app/:

pet-karma/app/

(app)/          ← authenticated routes, grouped
(public)/       ← unauthenticated routes, grouped
api/            ← route handlers
error.tsx       ← shown on errors
globals.css     ← app-wide styles
layout.tsx      ← root wrapper
loading.tsx     ← shown during loading
not-found.tsx   ← shown for 404s
page.tsx        ← homepage

Inside (app)/:

pet-karma/app/(app)/

dashboard/
friend-groups/
friend-profile/
history/
my-pets/
profile/
schedule/
settings/
layout.tsx
loading.tsx

Every folder name matches a feature. Every feature has its own home. You don't have to hunt — if something breaks with my-pets, you know exactly where to look.

And inside app/api/:

pet-karma/app/api/

achievements/
activities/
auth/
contacts/
friend-groups/
friend-profiles/
health/
notifications/
pet-history/
pets/
push-tokens/

Same pattern — every API folder maps to a feature or data domain. This didn't happen by accident. This is the result of modularization: starting with one giant server file and, over time, breaking it into pieces where each piece has one clear responsibility.

Opsette: history you can read in the folders

Opsette is bigger and more complicated. It's been through multiple migrations, a couple of abandoned monorepo attempts, and months of active development. Its top level tells that story:

opsette/

.claude/
.vscode/
admin-app/
api/             ← separate root-level API folder
app/             ← main Next.js App Router
apps/            ← legacy monorepo attempt
components/
dev-dist/
docs/
email-templates/
hooks/
inspo/
lib/
packages/        ← monorepo leftover
public/
sanity/          ← CMS integration
scripts/
shared/
src/
supabase/
types/           ← generated Supabase types

That's a lot — and that's normal. Production apps accumulate folders because they accumulate features.

Inside Opsette's app/ folder:

opsette/app/

(auth)/         ← authentication pages, grouped
(dashboard)/    ← authenticated admin pages, grouped
(marketing)/    ← public marketing pages, grouped
(pos)/          ← point-of-sale flow, grouped
(storefront)/   ← storefront pages, grouped
api/            ← route handlers
auth/           ← auth utilities
b/              ← shortened booking URL
booking/        ← admin booking routes
embed/          ← embeddable widgets
f/              ← shortened form URL
hub/            ← hub routes
portal/         ← portal routes
products/       ← product routes
providers/      ← public provider pages
sign/           ← sign-in flow
GoogleAnalytics.tsx
globals.css
layout.tsx
page.tsx
sitemap.ts

Five route groups. One sitemap. The full set of special filenames at the bottom. Every bracket convention you've learned appears in one screen.

How to orient in any codebase

When you open an unfamiliar project, here's the map-reading order:

  1. Start at the root. What's at the top level? Any immediate red flags or obvious patterns?
  2. Find the routing folder. app/, pages/, or equivalent. That tells you the framework and what URLs the app exposes.
  3. Find the API layer. Inside app/api/, at the root, or in a server/ folder somewhere.
  4. Follow a feature all the way through. Pick something small — say, "pets." Find the pet page. Find the pet API. Find the pet hook. Find the pet types. Watch how data flows across the layers.
  5. Read the package.json. The dependencies tell you what the app uses. The scripts tell you how to run it.
  6. Skim the top-level config files. You don't have to master them. Just know what's there.

Do that once in a project you didn't build, and you'll have a working map within an hour. Do it a few times, and every codebase starts to rhyme.

What you now know

You came into this module knowing your projects well enough to ship them, but not always sure why each folder was named what it was named. Now you can:

The folder tree used to look like a foreign language. Now it reads like a map.