Choosing a stack that agents handle well
Conventional frameworks, typed languages and well-documented libraries cost far fewer attempts, and that difference compounds across a project.
Why conventions reduce cost
A model has seen an enormous amount of conventional code and very little of your bespoke framework. When you ask for something it has seen a thousand times, it produces the idiomatic version on the first attempt. When you ask for something novel, it produces a plausible invention that looks right and is not.
- Training-data volume is a real engineering property of a stack, not a preference.
- Established conventions mean the agent's default guess is usually your team's choice.
- Typed languages let the compiler reject wrong code before you review it.
- Popular libraries have current documentation that the model has likely read.
| Choice | Cheap for an agent | Expensive |
|---|---|---|
| Framework | React, Next, Django, Rails, Express | An in-house meta-framework |
| Language | TypeScript, Python, Go | A DSL you invented |
| Styling | Tailwind, CSS Modules | A bespoke class system |
| State | Any popular, documented library | Custom event bus with implicit rules |
| Build | Vite, the framework default | Hand-rolled webpack config |
Types turn a guess into an error
// Untyped: a wrong property name ships silently
function total(cart) {
return cart.items.reduce((n, i) => n + i.price * i.qty, 0);
}
// Typed: the compiler names the mistake for the agent
type Line = { price: number; quantity: number };
type Cart = { lines: Line[] };
function total(cart: Cart): number {
return cart.lines.reduce((n, l) => n + l.price * l.quantity, 0);
}
// Property 'items' does not exist on type 'Cart'.
// Property 'qty' does not exist on type 'Line'.That compiler output is the cheapest correction loop available: it is specific, it is immediate, and the agent can read it without any ambiguity. A typed codebase is one where the machine, not the reviewer, catches the first category of mistake.
Constraints that help
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"exactOptionalPropertyTypes": true
}
}- Turn strictness up and fix the fallout before the agent arrives; a lenient compiler lets generated code pass review with real errors.
- Validate at the boundary with a schema library, so the shape of external data is checked rather than asserted.
- Keep one way to do each thing. Two HTTP clients, two state managers and two date libraries each give the agent a chance to pick the wrong one.
- Pin versions. An unpinned dependency means the docs the agent remembers and the code you run can disagree.
- When you must use something unusual, write the pattern once yourself and point the agent at it as the example to follow.
"Follow the pattern in src/features/orders. New features must have
the same shape: route, schema, repository, test. Do not invent a
different structure."That one sentence often does more for consistency than any amount of linting, because it gives the agent something concrete to copy rather than something abstract to satisfy.
FAQ
Should I rewrite my project in a more popular stack because of agents?
Do agents work badly with older frameworks?
Related
Prompting for intent, not syntax Types, linters and static checks as the first filter
Last refreshed 2026-09-18.