Scale: run it for many.
- Users
- Many clients across products
- Team
- Six or more engineers across teams
You are here if
- More than one team deploys backend code independently
- Data lives in more than one region or under residency rules
- An auditor will ask who changed what
- Latency and throughput are contractual, not aspirational
At scale the backend becomes a set of services with explicit contracts, and the language choice starts to matter for reasons beyond taste. Compiled, statically typed languages give the agent a compiler as a second reviewer, and their conventions are strong enough that generated code tends to look like the code around it.
Every service owns its schema, exposes a generated contract, ships with structured logs and traces, and has a test command that runs in isolation. The agent works inside one service at a time and trusts the contract for the rest.
What does not change: small handlers, side effects as jobs, and tests before changes. Scale adds boundaries; it does not change the unit of work.
Product API · serves web & mobile
Go services + Postgres, Rust where latency is the product
Compiled, typed services with a generated contract each, boring infrastructure, and a compiler as the second reviewer.
The stack
- Framework
- Go with chi or Echo; Rust with Axum for the hot path
- Runtime
- Static binaries in containers
- Data
- Postgres per service via sqlc (Go) or sqlx (Rust), Redis or NATS between services
- Auth
- OIDC at the gateway, service-to-service mTLS or signed tokens
- Hosting
- Kubernetes or Nomad, a gateway in front
- Also
- OpenAPI or protobuf per service, generated clientsOpenTelemetry everywhereContract tests between services
Why agents do well here
4/5- Go's formatting and small language make agent-written code indistinguishable from the team's, which is what code review at this size depends on.
- Generated contracts per service let the agent work inside one service and trust the rest.
- The compiler and sqlc's typed queries catch the class of errors an agent introduces most in dynamic languages.
Avoid at this tier
- A service per noun; a service per team boundary.
- Rust everywhere for consistency; use it only where p99 latency is the product.
- Sharing databases between services; share contracts and events.
At scale the product API is several services owned by several teams. Go is the default for its conventions and compile-time checks; Rust earns its place on the one or two paths where latency is the product.
services/<name>/
cmd/ main.go
internal/ <domain>/ {handler, service, queries.sql, tests}
api/ openapi.yaml or .proto, generated clients published
platform/ gateway, otel collector, shared CI
First five decisions: a contract per service, sqlc or sqlx for typed SQL, events over shared tables, OpenTelemetry from the first commit, and contract tests in CI.
Data & AI · pipelines & jobs
ClickHouse + a typed ingestion layer + Postgres
Analytical storage separated from transactional, with typed ingestion and a query layer the product reads through one client.
The stack
- Framework
- Ingestion in TypeScript or Python, query API in Next.js route handlers or FastAPI
- Runtime
- Workers or containers for ingestion, an API for reads
- Data
- ClickHouse for events and analytics, Postgres for entities, object storage for raw
- Hosting
- ClickHouse Cloud or self-hosted, Kafka or a managed stream in front
- Also
- Schema registry for eventsdbt or SQL models for derived tablesData quality checks in CI
Why agents do well here
3/5- Typed event schemas in a registry give the agent one place to learn what flows through the system.
- SQL models in the repo are explicit and testable, so the agent can change a derived table and run its checks.
- Separating analytical from transactional storage keeps each query path simple enough to reason about.
Avoid at this tier
- Analytical queries on the transactional Postgres.
- Untyped event payloads; every event has a versioned schema.
- A new pipeline framework per team; one ingestion layer, many models.
At scale, data work splits into ingestion, storage and modelling. Events get a versioned schema, land in ClickHouse, and are shaped by SQL models the agent can read and test. The product reads through one query API.
ingest/ typed event handlers, schema registry client
models/ SQL models (dbt or plain), tests/
api/ query endpoints with typed responses
schemas/ versioned event definitions
First five decisions: a schema registry, ClickHouse for events only, SQL models under test, one read API, and data-quality checks in CI.
Internal · tools & automation
Rails + Postgres + Redis
The most convention-heavy full-stack framework there is, which at scale is exactly what keeps many teams' internal tools consistent.
The stack
- Framework
- Ruby on Rails 8 with Hotwire
- Runtime
- Ruby in containers
- Data
- Postgres, Redis for Sidekiq and cache
- Auth
- SSO through OmniAuth, roles in the database
- Hosting
- Kamal on your own hosts, or a container platform
- Also
- Sidekiq for jobs and schedulesAdministrate or a home-grown admin per domainRSpec with a real database
Why agents do well here
5/5- Rails conventions are the strongest in the industry; agent-generated models, controllers and migrations follow them almost without prompting.
- Hotwire keeps screens server-rendered, so an internal tool with hundreds of screens has no client build for the agent to fight.
- The framework's opinion about where everything goes is what keeps a large internal platform consistent across teams.
Avoid at this tier
- A JavaScript frontend for internal screens; Hotwire covers it.
- Service objects for everything; models and jobs are enough until they are not.
- Multiple Rails apps; use engines or namespaces in one.
Internal platforms at scale have hundreds of screens, many owners, and constant change. Rails’s conventions are the tool that keeps that consistent, and Hotwire keeps it all server-rendered.
app/
models/ one file per table, validations and scopes
controllers/ namespaced per domain
jobs/ Sidekiq jobs and schedules
views/ Hotwire; Turbo frames for the interactive parts
db/ schema.rb, migrate/
spec/ RSpec with a real database
First five decisions: Rails conventions unmodified, Hotwire over a SPA, Sidekiq for every job, SSO with database roles, and RSpec on models and requests.