YeetCode

Monorepo Architecture

How the YeetCode monorepo is organized and why.

YeetCode uses a Bun workspace monorepo orchestrated by Turborepo. The repo is split into four workspace directories, each with a distinct purpose.

Workspace Layout

yeetcode/
├── packages/*          # Shared libraries consumed by other workspaces
├── apps/*     # User-facing applications (Next.js)
├── apps/*      # Backend services (Convex)
└── tooling/*           # Shared configuration (TypeScript presets)

These are declared in the root package.json:

{
  "workspaces": [
    "packages/*",
    "tooling/*",
    "apps/*",
    "apps/*"
  ]
}

Package Naming

All packages use the @yeetcode/ scope:

PackageLocationPurpose
@yeetcode/emailpackages/emailReact Email templates and render utilities
@yeetcode/typescripttooling/typescriptShared TypeScript config presets
@yeetcode/convex-backendapps/convexConvex backend (private)

Cross-Package Dependencies

Packages reference each other using Bun's workspace:* protocol:

{
  "dependencies": {
    "@yeetcode/email": "workspace:*",
    "@yeetcode/typescript": "workspace:*"
  }
}

This ensures you always use the local workspace version rather than a published one.

Repo-Level Tooling

The root package.json contains repo-wide scripts:

  • bun run dev — Runs turbo dev --parallel across all workspaces
  • bun run build — Builds all packages respecting the dependency graph
  • bun run lint — Runs workspace linting + sherif for monorepo consistency checks
  • bun run format — Formats everything with Biome
  • bun run typecheck — Type-checks all packages
  • bun run clean — Removes all node_modules via git clean -xdf
  • bun run clean:workspaces — Cleans workspace build artifacts (.turbo, .next, dist, etc.)

Sherif

The repo uses sherif (bun lint:repo) to enforce monorepo best practices — consistent dependency versions, correct workspace references, and proper package structure.

On this page