ShipKit docs

Production rules

Eleven hard rules pulled from real production bugs. Don't break them.

These are the rules every change to this codebase must satisfy. They come from real bugs that bit real users — violating one means you've reintroduced a known problem.

1. BetterAuth base URL

Use NEXT_PUBLIC_APP_URL (no /api). Never NEXT_PUBLIC_API_URL — Better Auth appends /api/auth itself, so the API URL would yield /api/api/auth/.

2. Semantic color tokens only

Forbidden in JSX: bg-white, text-slate-*, border-gray-*, hex / rgb in className. Use bg-background, text-foreground, text-muted-foreground, border-border, bg-muted, bg-card, text-primary. They map to CSS variables in packages/ui/src/styles/globals.css and respect both themes.

3. Dark mode

next-themes with defaultTheme="system". Never set className="light" or colorScheme on <html>/<body>. Both themes must work after every change.

4. One header per layout

Header lives in app/layout.tsx. Page components must not render a duplicate <header> with logo / nav. Toolbars and breadcrumbs are fine.

5. Brand color

--primary in globals.css must be set for both light and dark themes before shipping. Default carries a /* CHANGE THIS */ marker.

6. Canonical URLs

Each page sets its own metadata.alternates.canonical. Never hardcode a single canonical in the root layout — search engines will treat every page as a duplicate of the homepage.

7. Payment integration = 3 parts

Every Polar.sh integration needs all three to function: checkout endpoint, HMAC-verified webhook, frontend subscription check. Skip one and money is lost or users are confused.

8. .env URL conventions

NEXT_PUBLIC_APP_URL=https://myapp.com         # no /api
NEXT_PUBLIC_API_URL=https://myapp.com/api      # with /api
BETTER_AUTH_URL=https://myapp.com              # same as APP_URL

9. Turbo race conditions

Parallel turbo builds occasionally race on the filesystem. If you see intermittent failures, build apps individually:

cd apps/web && bun run build

10. Public vs admin

Public-facing UI lives in apps/web. Internal dashboards / admin tooling live in apps/admin. Don't mix.

11. API build for native modules

bun build src/index.ts --outdir dist --target bun --external sharp

On this page