Fullstack/Monorepo vs Polyrepo

Monorepo vs Polyrepo

Two different ways to organize a project with multiple apps or services. The tradeoff is between shared code convenience (monorepo) and deployment independence (polyrepo).


What is a Monorepo

One Git repository, multiple apps or packages inside.

my-project/                   ← single repo
├── apps/
│   ├── web/                  → Next.js frontend (marketing)
│   ├── app/                  → Next.js app (dashboard)
│   └── api/                  → Express backend
├── packages/
│   ├── ui/                   → shared component library
│   ├── types/                → shared TypeScript types
│   └── config/               → shared ESLint, Tailwind config
├── package.json
└── turbo.json

One git push can trigger builds for all affected apps. Shared code lives in packages/ and is imported directly without publishing to npm.

Tools: Turborepo, Nx, pnpm workspaces


What is a Polyrepo

Each app or service lives in its own separate Git repository.

Real example: Neko Singa AI

github.com/nekosinga/web    → marketing site
github.com/nekosinga/app    → main application (AI tools)
github.com/nekosinga/api    → backend API
github.com/nekosinga/docs   → documentation

Each repo deploys independently to its own Vercel project:

RepoVercel ProjectLive URL
webnekosinga-webnekosinga.vercel.app
appnekosinga-appapp-nekosinga.vercel.app
apinekosinga-apiapi-nekosinga.vercel.app
docsnekosinga-docsdocs-nekosinga.vercel.app

Each repo moves at its own pace. The docs can be updated without touching the app. The API can be redeployed without rebuilding the marketing site.


When to Use Which

SituationUse
Solo project, everything tightly coupledMonorepo
Small team, shared components everywhereMonorepo
Multiple apps with independent deploy cadencesPolyrepo
Different teams owning different servicesPolyrepo
Public-facing site + internal dashboard + APIPolyrepo
AI product with web, app, API, docsPolyrepo (like Neko Singa)

For Neko Singa I chose polyrepo because:

  • The marketing site (web) and the AI dashboard (app) have completely different update cycles
  • The API can be redeployed independently when endpoints change
  • Docs can be written and published without triggering a full app rebuild
  • Each Vercel project has its own environment variables

Polyrepo: How the Services Talk to Each Other

Each service exposes a URL that others call via environment variables.

# nekosinga/app .env.local
NEXT_PUBLIC_API_URL="https://api-nekosinga.vercel.app"

# nekosinga/api .env
ALLOWED_ORIGINS="https://app-nekosinga.vercel.app,https://nekosinga.vercel.app"
DATABASE_URL="postgresql://..."

The app frontend never hardcodes the API URL. It reads from NEXT_PUBLIC_API_URL. Switching from staging to production is just an env variable change in Vercel.


Turborepo Setup (if you go monorepo instead)

npx create-turbo@latest
// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
// root package.json
{
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo build"
  }
}

Turbo only rebuilds what changed. If you edit only packages/ui, only apps that import from ui get rebuilt.


Shared Packages Pattern (monorepo)

packages/ui/
├── src/
│   ├── Button.tsx
│   └── index.ts
└── package.json
// packages/ui/package.json
{
  "name": "@nekosinga/ui",
  "main": "./src/index.ts",
  "exports": { ".": "./src/index.ts" }
}
// apps/web/app/page.tsx
import { Button } from "@nekosinga/ui";

No npm publish needed. Turborepo resolves the package locally at build time.


Last updated: September 2026.

Last updated · September 2026