Scale: run it for many.
- Users
- Tens of thousands and up
- Team
- Six or more engineers across teams
You are here if
- More than one team owns part of the product
- You serve users from more than one region or under data-residency rules
- An auditor will ask how a change got to production
- A single deploy touching everything is no longer acceptable
At scale the goal flips: instead of keeping everything in one place so an agent can read it all, you draw boundaries so an agent never needs to. Each service or package owns its schema, publishes a typed contract, and can be tested in isolation. The frontend becomes one consumer among several.
Boring choices matter more than ever here. Agents have read far more Next.js, NestJS and Go than any bespoke framework, and at this size the cost of an agent misunderstanding a clever abstraction is measured in incidents.
What does not change: feature-shaped modules, schema-first types and a per-package test command remain the unit of work. Scale just adds a contract between them.
SEO-driven · content & search
Next.js + Postgres + edge caching
Millions of indexable pages with incremental regeneration, a CDN in front, and a content pipeline separate from the app.
The stack
- Framework
- Next.js App Router with ISR
- Data
- Postgres (read replicas) via Prisma or Drizzle, plus a search index
- Auth
- Only on the parts that are not public
- Hosting
- Vercel, or self-hosted behind a CDN
- Also
- Headless CMS or a content pipeline in its own packageMeilisearch or Typesense for on-site searchLighthouse CI and a crawl budget dashboard
Why agents do well here
4/5- Incremental regeneration is a per-page setting the agent can read, so caching behaviour is visible in the route file instead of in infrastructure.
- Separating the content pipeline into its own package lets the agent change how pages are built without touching how they are rendered, and vice versa.
- Next.js at this size has the most public examples of large-scale SEO sites, so the agent's patterns for sitemaps, canonicals and structured data are well rehearsed.
Avoid at this tier
- Rendering everything on demand; decide per route what is static, revalidated or dynamic.
- Letting search and rendering share a database connection pool.
- A rewrite to a newer framework for performance you have not measured.
At this scale the site is two systems: a content pipeline that produces rows, and a rendering app that turns rows into cached pages. Keep them in one monorepo with a shared schema package so the agent can still trace a field end to end.
apps/web/ Next.js: app/, features/, lib/seo/
packages/schema/ Prisma or Drizzle schema, generated types
packages/content/ ingestion, enrichment, search indexing jobs
packages/ui/ the component kit
First five decisions: ISR with explicit revalidation per route, read replicas for rendering, a search index that is rebuilt not patched, a Lighthouse budget in CI, and a shared schema package so both systems agree.
SaaS · logged-in product
Next.js front, NestJS + Postgres + Redis back
A typed API the frontend is only one consumer of, with modules, queues and an OpenAPI contract that generates the clients.
The stack
- Framework
- Next.js (frontend) + NestJS (API)
- Runtime
- Node.js in containers
- Data
- Postgres with read replicas, Redis for cache and queues
- Auth
- OIDC provider (Auth0, WorkOS) with sessions on the API
- Hosting
- Kubernetes or a managed container platform, CDN in front
- Also
- OpenAPI generated from NestJS decorators, clients generated from OpenAPIBullMQ for jobsOpenTelemetry traces from day one
Why agents do well here
4/5- NestJS modules are a strong, well-documented convention; the agent knows where a controller, service and DTO go without being told.
- The generated OpenAPI contract means a schema change becomes a type error in every client, which is the feedback loop an agent needs at this size.
- Separate frontend and API repos or packages let the agent work inside one at a time with a contract it can trust for the other.
Avoid at this tier
- Microservices per feature; split by team boundary, not by noun.
- Sharing a database between services; share a contract instead.
- Frontend code that reaches around the API to the database.
The frontend and the API become separate deployables with a generated contract between them. Each NestJS module owns its entities, DTOs, service and tests; the frontend consumes the generated client and nothing else.
apps/web/ Next.js consuming packages/api-client
apps/api/ NestJS: src/modules/<domain>/ {controller, service, dto, spec}
packages/api-client/ generated from apps/api's OpenAPI document
packages/schema/ shared enums and validation
First five decisions: OpenAPI as the contract, generated clients only, modules by domain, BullMQ for every side effect, and tracing before the first incident rather than after.
Tool · utility & internal
Go + Postgres + React
A compiled backend with one binary to deploy, a React frontend built to static assets, and a contract generated from the Go handlers.
The stack
- Framework
- React (Vite) frontend
- Runtime
- Go HTTP service (chi or Echo)
- Data
- Postgres via sqlc, Redis if you need it
- Auth
- OIDC, sessions in Postgres
- Hosting
- Single binary in a container, static assets on a CDN
- Also
- OpenAPI from the Go handlers, TypeScript client generated from itsqlc for typed queries from SQLStructured logging and pprof endpoints
Why agents do well here
4/5- Go's small language and strong formatting conventions mean agent-written code looks like the code around it, which matters when many people review it.
- sqlc turns SQL files into typed functions, so the agent writes plain SQL and the compiler checks the result.
- The compiler is a second reviewer; most of the mistakes an agent makes in dynamic languages do not build here.
Avoid at this tier
- Generics-heavy frameworks and dependency-injection containers; keep Go boring.
- ORMs that hide the SQL; sqlc keeps it visible.
- Serving the frontend from the Go binary in production; use a CDN.
A tool at scale is often an internal platform with many users and strict uptime. Go gives one binary, predictable performance and a compiler that catches what tests miss. The React frontend stays a static SPA that talks to the generated client.
cmd/server/ main.go
internal/ <domain>/ {handler.go, service.go, queries.sql, handler_test.go}
db/ migrations/, sqlc.yaml
web/ Vite + React, src/api/ generated from OpenAPI
First five decisions: chi or Echo with no framework magic, sqlc for every query, OpenAPI generated from handlers, migrations with a single tool, and integration tests against a real Postgres in CI.