Para devs AI-native
La base SaaS
que tu agente no puede romper
No estás comprando código que tu agente ya sabe escribir. Estás comprando los guardrails que no puede escribir solo: las paredes que le impiden romperte el login cuando toca pagos — enforzadas por el compilador y CI, no una convención blanda que puede racionalizar.
El agente ve una porción, y edita con confianza igual
En cada sesión tu agente ve solo una parte del proyecto — así que cambia código sin saber qué depende de eso en otro lado. El mes uno se siente eléctrico. Al mes seis la misma herramienta va más lenta que antes, porque la deuda técnica no se paga: se acumula, y en algún momento se cobra. Un repo vacío lo empeora: sin patrones a los que agarrarse, el agente driftea hacia sus propios defaults y erosiona la arquitectura en silencio. Para el prompt 50 es spaghetti.
En las palabras de quienes shippean con agentes
Deuda que se cobra más tarde
"Tech debt isn't paid down, it's being added to, and at some point in the future it will need to be collected." — antihipocrat, Hacker News (item 45405177)
Ningún patrón consistente al que agarrarse
"llm code has none of that [consistent patterns], if yes its by pure chance that won't repeat." — kakacik, Hacker News (item 45405177)
Los edge cases de seguridad que se le escapan
Los agentes IA "generate RLS policies that accidentally expose data to other tenants, forget to validate webhook signatures on payment events, create permission checks that authenticated users can bypass." — blog de MakerKit
Paredes que el agente físicamente no puede cruzar
Apuntá tu agente a la capa domain y decile que importe Prisma ahí nomás. No puede — ESLint rompe el build. Fronteras DDD enforzadas por el compilador y el lint: el código de domain no puede importar Express, Prisma ni infraestructura. Esto no es una sugerencia blanda de un AGENTS.md que el agente razonea y esquiva. Es una pared en CI.
1 modules/billing/domain/subscription.ts
2 import { prisma } from '@prisma/client'
3 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 error no-restricted-imports
5 domain/ debe seguir framework-agnostic.
6 Ver ADR-001.
7
8 ✖ 1 problem — CI bloqueado
Payload drift, atrapado antes de shippear
El agente renombra un campo en el server y se olvida del cliente. El type drift lo atrapa el compilador. Pero el payload drift — el form manda fullName, el server espera name — pasa el typecheck y tira 400 en producción. Acá los schemas Zod del cliente importan desde un paquete compartido @app/contracts, y los tipos de openapi-fetch se regeneran y quedan gateados por el check de CI api-types-fresh. La superficie generada y el server no pueden discrepar sin poner el CI en rojo.
1 $ bun run generate:api
2 $ git diff --exit-code \
3 apps/server/openapi.json \
4 apps/client/lib/api/openapi-types.ts
5
6 openapi.json cambió — los tipos están viejos
7 ✖ api-types-fresh falló — regenerá y commiteá
Los tres bugs que un repo vacío shippea siempre — acá ya cerrados
Sesiones que sobreviven un reset
El bug recurrente que los reviews de seguridad encuentran en apps generadas por IA: sesiones que no se cierran al cambiar la contraseña — resetéas tu password y el login viejo sigue funcionando. Acá las sesiones son filas server-side en Postgres — cualquiera se puede revocar on demand, y cerrar las sesiones concurrentes al cambiar la credencial es un flag documentado, no un rewrite.
Webhooks que cobran dos veces
El otro clásico es el webhook handler sin idempotencia: cuando Stripe manda el mismo mensaje dos veces (y lo hace), la app cobra o acredita dos veces. Acá las compras son idempotentes sobre externalOrderId, entregadas por un event bus con outbox durable.
Datos de tenant que se filtran
Los agentes IA "generate RLS policies that accidentally expose data to other tenants" (makerkit.dev). Acá un tenant-isolation guard scopeado por org — repos org-scoped más un query guard de Prisma — devuelve 404 entre orgs. Sin RLS que olvidar.
Un harness contra el que tu agente puede auto-verificarse
E2E Playwright en CI
Los flujos golden-path corren en cada PR (bun run e2e:golden). El loop code → test → code que el agente necesita para autocorregirse — con un paso de validación que refleja lo que realmente querés, no lo que parece plausible.
Fronteras DDD, lint-enforced
El layering son reglas de ESLint, no documentación: domain y application no pueden importar frameworks, y los imports cross-context en runtime están prohibidos. La pared que el agente choca es la pared que mantiene tu arquitectura intacta.
Un CLAUDE.md de reglas enforzadas
Un CLAUDE.md viene en el repo — layering DDD, la regla de contratos, disciplina de commits y PRs — para que el agente herede tu pensamiento de largo plazo en vez de promediar el internet público. TypeScript estricto de punta a punta.
Resuelto una vez, para que tu agente solo construya lo tuyo
Auth
BetterAuth — 2FA, magic-link, OAuth, verificación de email, password reset. Sesiones y flujos ya cableados y testeados.
Billing
Stripe, MercadoPago y Polar detrás de una interfaz tipada — checkout, customer portal, cambios de plan, webhooks firmados.
Orgs y RBAC
Organizaciones, miembros, invitaciones, permisos por rol y API keys — multi-tenancy real, no una tabla users con una columna role.
Jobs
Colas, processors y schedulers de BullMQ, con un dashboard de Bull Board para mirarlos.
Events
Un event bus con outbox durable para que los domain events sobrevivan un crash y se entreguen confiablemente.
Observabilidad
OpenTelemetry, Sentry (@sentry/bun), Pino, /metrics — cada uno bootea a no-op hasta que lo configures.
Preguntas de quienes shippean con agentes
¿No alcanza con un CLAUDE.md o un AGENTS.md?+
¿Venden velocidad?+
¿Qué stack es?+
¿Con qué agentes anda?+
Apuntá tu agente acá, no a un repo vacío.
Un repo vacío obliga al agente a inventar arquitectura y a repetir los mismos tres bugs de seguridad. Dale paredes que no puede cruzar.