Growth: charge for it.
- Users
- Several clients, some you do not control
- Team
- Two to five engineers, each with agents
You are here if
- A breaking response change would break a client
- Background work, emails or webhooks exist
- Someone asks for an API reference
- You have been paged at least once
Growth is where a backend earns its structure. It is still one deployable, but organised into feature modules with their own schema, handlers and tests. Side effects go through a queue. The OpenAPI document is generated from the code and clients are generated from it, so the agent changes a field once and every consumer sees the type error.
What changes when you leave this tier: teams, regions or compliance that make implicit coupling an outage. Until then, one service with strong modules is faster to change and easier for an agent to verify than several small ones.
Product API · serves web & mobile
NestJS + Postgres + Redis
One service with strong module conventions, a queue for side effects, and an OpenAPI document clients are generated from.
The stack
- Framework
- NestJS
- Runtime
- Node.js 22 in a container
- Data
- Managed Postgres via Prisma or Drizzle, Redis for cache and queues
- Auth
- Passport with a hosted OIDC provider
- Hosting
- Fly, Railway, or ECS
- Also
- OpenAPI from decorators, clients generated for web and mobileBullMQ for jobsJest with testcontainers for Postgres
Why agents do well here
5/5- NestJS's module, controller, service and DTO convention is rigid enough that the agent knows exactly where new code goes.
- Generated OpenAPI plus generated clients turn a schema change into a compile error in every consumer, which is the feedback loop that keeps consumers safe.
- Decorators make validation and documentation live next to the handler, so the agent reads one file to understand a route.
Avoid at this tier
- Splitting into microservices; split into modules with clear imports.
- Hand-written clients in the frontend; generate them.
- Synchronous webhooks or emails; everything goes through BullMQ.
At growth the API has consumers it does not control. NestJS gives the structure, OpenAPI gives the contract, and BullMQ takes every side effect out of the request path.
src/
modules/<domain>/ controller.ts, service.ts, dto/, entities/, *.spec.ts
jobs/ processors for BullMQ queues
common/ guards, interceptors, filters
prisma/ or drizzle/ schema + migrations
openapi.json generated in CI, consumed by packages/api-client
First five decisions: modules by domain, OpenAPI generated and versioned, generated clients only, BullMQ for side effects, and testcontainers so tests hit a real Postgres.
Data & AI · pipelines & jobs
LangGraph + FastAPI + Postgres
Pipelines and agent workflows as explicit graphs, each step typed and retried, served through the same FastAPI you started with.
The stack
- Framework
- FastAPI + LangGraph
- Runtime
- Python 3.12 with uv
- Data
- Postgres with pgvector, object storage for raw files
- Hosting
- Fly or Modal for the API, Modal or a worker pool for jobs
- Also
- Pydantic models as the graph stateA queue (Redis or Postgres-backed) with retries and dead lettersLangfuse or OpenTelemetry for tracesEvals in CI for the LLM steps
Why agents do well here
4/5- A graph with typed state makes every step's input and output explicit, which is what lets an agent modify one node without breaking the rest.
- Idempotent steps with retries turn flaky external calls into a visible queue instead of silent data loss.
- Traces and evals give the agent something to check against, which matters more here than in any other profile.
Avoid at this tier
- Hiding LLM calls inside helper functions; each is a node with a schema.
- A separate vector database while pgvector still fits.
- Shipping prompt changes without an eval run.
Growth-tier data work is pipelines that fail partway and workflows that call models. Make both explicit graphs with typed state so the agent can see each step, and put retries and traces around every external call.
app/
api/ FastAPI routes
graphs/ <pipeline>/ {graph.py, nodes.py, state.py, test_graph.py}
models/ Pydantic state and schemas
jobs/ queue workers running graphs
evals/ datasets and scoring for LLM nodes
First five decisions: typed graph state, one queue with retries, pgvector in Postgres, traces on every node, and evals in CI for prompt changes.
Internal · tools & automation
Django + Postgres + Redis
Batteries included: the admin, the ORM, auth and background tasks are one framework the agent has read more of than any other.
The stack
- Framework
- Django with the built-in admin
- Runtime
- Python 3.12 with uv
- Data
- Postgres, Redis for cache and Celery
- Auth
- django-allauth with SSO
- Hosting
- Fly, Railway, or a container platform
- Also
- Celery beat for schedulesHTMX for the few interactive screenspytest-django
Why agents do well here
5/5- Django's conventions are two decades old and everywhere in training data; agent-written models, views and admin classes look like the docs.
- The admin gives a screen per model for free, so most internal tool requests become a model change the agent makes in one file.
- Celery beat keeps the automation next to the models it acts on, in the same tests.
Avoid at this tier
- A React admin panel; Django's admin covers the first two years.
- Raw SQL in views; the ORM is the contract the agent can reason about.
- Multiple Django projects; use apps inside one project.
Internal tools at growth need permissions, audit trails, schedules and a lot of screens quickly. Django’s admin, ORM and Celery cover all of it in one process with one set of conventions.
project/
settings/ base.py, prod.py
apps/<domain>/ models.py, admin.py, views.py, tasks.py, tests/
templates/ server-rendered, HTMX where needed
First five decisions: Django admin as the UI, apps per domain, Celery beat for schedules, SSO through allauth, and pytest-django with a real database.