DEV Community

Cover image for Tres capas de contexto para un agente de código, ordenadas por garantía

Tres capas de contexto para un agente de código, ordenadas por garantía

Tres capas de contexto para un agente de código, ordenadas por garantía

Una migración tiró una columna. Las pruebas estaban en verde, la revisión estaba hecha, el deploy era de rutina. Setenta y ocho segundos después el panel de admin estaba regresando 500s. El agente que revisó esa migración no tenía forma de saber que el deploy rota dos tasks una a la vez, porque nada en el repo lo dice.

TL;DR

Capa Qué va en ella Presente cuando Obedecida
CLAUDE.md Lo que el modelo no puede deducir leyendo el repo Cada sesión, siempre Casi siempre
Archivos de memoria Un archivo por error cometido, con la razón Cuando la recuperación lo juzga relevante Casi siempre
Hooks de git y de herramientas Reglas que no te puedes dar el lujo de que se salten Cada corrida No negociable

El orden es el punto. Cada capa cuesta más de configurar y da una garantía más fuerte. La mayoría de la gente trata de resolver todo en la capa uno, que es por lo que su CLAUDE.md tiene 600 líneas y va empeorando.

Dos números de los cinco meses, los dos medidos, los dos en:

  • CLAUDE.md fue de 180 líneas, llegó a su pico en 249, se podó a 129, y se queda en 145.
  • Los archivos de memoria fueron 12, 26, 36, 76, 91.

Los dos medidos el 21 de agosto de 2026. Corre los scripts en tu propio repo hoy y el conteo de memoria será más alto que el mío, porque esa capa nunca deja de crecer. Ese es el punto de ella.

La capa que escribo a mano se encogió. La capa que sale de los errores creció. Si las dos tuyas están creciendo, estás poniendo todo en la capa uno.

El incidente

21 de julio, 21:14. CloudWatch:

UndefinedColumnError: column listings.public_visibility does not exist
GET /api/v1/admin/marketplace
500 · 500 · 500 · 500 · 500 ...
Enter fullscreen mode Exit fullscreen mode

La migración era correcta. El timing no. El backend corre dos tasks de Fargate y el rollout las reemplaza una a la vez, mientras que la migración corre antes de que las tasks nuevas estén sanas. Por setenta y ocho segundos el código viejo seleccionaba una columna que ya no existía.

El arreglo es el expand y contract de manual: deja de referenciar la columna en un deploy, tírala en uno posterior. Esa no es la parte interesante. La parte interesante es que pedí una revisión de esa migración y nunca le dije a nadie, humano o de otro tipo, cómo se despliega este proyecto. La topología de deploy no está en el código. No hay ningún archivo que diga "esto rota dos a la vez, con traslape".

El reflejo después de un incidente así es escribir un mejor prompt la próxima vez. Eso falla por una razón aburrida: un prompt se muere con la sesión.

Capa 1: lo que el modelo no puede deducir

La prueba de si una línea pertenece a CLAUDE.md es una oración.

Si el modelo lo puede deducir leyendo el repo, córtalo.

Claude lee tu manifiesto. No necesita que le expliques que usas React. El CLAUDE.md típico que encuentras en línea es en su mayoría una descripción del repo, que el agente deriva en dos tool calls y deriva mejor de lo que tú la escribiste, porque el repo es la verdad y tu descripción tiene cuatro meses de vieja.

Lo que sobrevive la prueba cae en cuatro formas, y todas son o una prohibición o una excepción:

## Base de datos

Nunca edites el schema a mano, siempre a través de migraciones.

Las migraciones que crean enums deben usar `op.execute()` crudo, no
`op.create_table` con un Enum nombrado: el paso de autogenerate se rompe.

`score_total` es una columna generada de Postgres. Nunca le asignes.

Las pruebas necesitan un Postgres real y el password no es el que está en el
README, que está viejo. Jálalo del secret manager del equipo.
Enter fullscreen mode Exit fullscreen mode

Nota la última línea. Carga el puntero, no el secreto. Un CLAUDE.md se commitea, así que una credencial nunca va dentro de uno. Nombrar dónde vive el valor es todo el valor de la línea, y no cuesta nada.

Podar es una mejora medible

La parte contraintuitiva: un CLAUDE.md de 600 líneas rinde peor que uno de 100 líneas. Estos no son hechos que se acumulan, son instrucciones compitiendo por la atención. Cada línea irrelevante diluye las que importan.

El mío creció a 249 líneas en once días, que es lo que pasa cuando tratas el archivo como documentación de onboarding. El 5 de abril lo corté a 129. A lo largo de los cuatro meses siguientes se fue de vuelta a 145 y nada se rompió.

El truco práctico es hacer que el agente audite su propio contexto. Abre el archivo y pregunta:

¿Qué en este archivo puedes deducir leyendo el repo?

Luego borra eso. Vas a borrar más de lo que esperas.

Capa 2: un error, un archivo, con la razón

CLAUDE.md es lo que sabías de antemano. La memoria es lo que aprendiste crasheando. Si tu CLAUDE.md crece cada vez que algo se rompe, estás usando la capa equivocada.

Aquí está el archivo que salió de los 78 segundos, textual excepto por el
frontmatter:

Una migración que hace DROP o RENAME de una columna sale en el mismo deploy que el código que deja de usarla, pero el backend corre 2 tasks de Fargate y las rota una a la vez, y la migración corre antes de que las tasks nuevas estén sanas.

**Por qué:** el rolling deploy significa que el código viejo y el nuevo sirven simultáneamente. Cualquier cambio de schema que no sea compatible hacia atrás con la imagen anterior rompe ese traslape. Pegó el 2026-07-21: `/admin/marketplace` tronó por ~78 segundos.

**Cómo aplicar:** los cambios aditivos (ADD COLUMN con un default, CREATE INDEX) son seguros de un jalón. Los destructivos necesitan expand/contract: el deploy A deja de referenciar la columna, el deploy B la tira. Un RENAME es un DROP más un ADD.
Enter fullscreen mode Exit fullscreen mode

Tres partes, todas obligatorias: qué pasa, por qué pasa, qué hacer. Sin la tercera es una queja, no una regla.

La razón no es opcional

Esta es la parte que la gente se salta, y saltársela es lo que hace que estos archivos se vuelvan superstición.

"Nunca hagas DROP COLUMN en una migración."
Enter fullscreen mode Exit fullscreen mode

Esa regla nunca tira una columna. Ni en un proyecto de una sola instancia, ni en desarrollo. No tiene idea de dónde aplica, así que aplica en todos lados y se te atraviesa.

"Nunca hagas DROP COLUMN en un solo deploy, porque el rolling
 deploy tiene dos versiones del código sirviendo a la vez."
Enter fullscreen mode Exit fullscreen mode

Esa sabe que un job de una sola instancia está bien, y se da cuenta por su cuenta de que un RENAME es un DROP disfrazado. La razón convierte una prohibición en un modelo mental, y un modelo mental generaliza a casos que nunca escribiste.

La otra cosa que vale la pena decir: yo no escribí ese archivo. Dije "esto acaba de pasar, guárdalo para que no se repita" y el agente lo escribió. Escribir la memoria es trabajo del modelo. Decidir que algo merece una es tuyo, y la prueba es si te va a morder otra vez. Un typo no. Una propiedad del sistema que no está escrita en ningún lado, sí.

Capa 3: cuando casi siempre no es suficiente

Las capas uno y dos funcionan bien. Pero son instrucciones a un modelo, y un modelo tiene una tasa de olvido distinta de cero. Para la mayoría de las reglas esa tasa es irrelevante. Para unas cuantas no lo es.

Tenía una regla de que cada funcionalidad visible al usuario se documenta en una guía que lee el lado no técnico del equipo. Vivió en CLAUDE.md por seis meses y se seguía casi siempre. Casi siempre resultó significar que la guía iba tres funcionalidades atrasada y nadie lo notó hasta que alguien la necesitó.

USER_VISIBLE_PATHS=(
    '^backend/app/api/v1/admin/'
    '^backend/app/services/billing/'
    '^frontend/src/pages/admin/'
)

# ¿tocó algo visible pero no la guía?
if ! echo "$STAGED" | grep -qx 'docs/team-guide.md'; then
    echo "✗ commit bloqueado" >&2
    # e imprime la salida de emergencia:
    #   git commit --no-verify
    exit 1
fi
Enter fullscreen mode Exit fullscreen mode

El bash no es la parte interesante. Dos decisiones de diseño lo son.

El mensaje de error documenta la salida. Un hook del que no te puedes salir obstruye el primer caso legítimo, y alguien lo desinstala esa semana. El mensaje está escrito para la persona que va a estar aquí a las once de la noche sin paciencia para acordarse de una bandera.

La lista de rutas es deliberadamente conservadora. Los falsos positivos se descartan con una bandera. Una funcionalidad visible saliendo en silencio no se descarta, se descubre meses después.

Elegir la capa

No "cuál es la mejor". Cuánto cuesta la falla.

Si la regla Ponla en Porque
Es una preferencia de estilo CLAUDE.md Un fallo ocasional no le hace daño a nadie
Vino de un incidente específico Memoria Necesita la razón y el alcance
Se descubre semanas después Hook Nadie va a revisar. Se acumula
Cuesta dinero o uptime Hook El fallo ocasional deja de ser aceptable

No tengo una tasa de falla medida para ninguna capa y no la voy a inventar. El argumento es costo esperado, no frecuencia. Es el mismo cálculo que ya usas para decidir si algo se lleva una prueba automatizada.

Una nota sobre los subagentes

Adyacente, y muy malentendido: un subagente cuesta más tokens totales, no menos. Paga por su propio system prompt, su propia copia de CLAUDE.md y sus propios schemas de herramientas, y luego su resumen te regresa como input.

Lo que ahorras es ventana de contexto principal, que es un recurso distinto y a veces el que de verdad te falta. Vale la pena cuando la exploración abarca módulos que no conoces, o cuando las investigaciones pueden correr en paralelo.
No vale la pena para una tarea que toca tres archivos que ya identificaste.

Qué hacer el lunes

Tres cosas, menos de una hora en total.

  1. Abre tu CLAUDE.md y pregúntale al agente qué es redundante. "¿Qué en este archivo puedes deducir leyendo el repo?" Borra eso.
  2. La próxima vez que algo se rompa, archívalo mientras está caliente. Antes de arreglarlo, con la razón. Es cuando lo tienes fresco y cuando menos ganas tienes de escribirlo.
  3. Escoge una regla y conviértela en un hook. La que duele si se resbala. Una. Con su salida documentada en el mensaje de error.

El modelo no recuerda. Tu repositorio sí.

Top comments (0)