Por Qué Claude Code Falla al 60% — y el Sistema Con el Que Entregamos de Todas Formas
Cómo estructuramos 28 sesiones de Claude Code en 9 días para entregar un rebuild completo de precios FY27 — 5 días antes de lo planeado.

Punto Clave
Teníamos 14 días. Terminamos en 9. No por horas extra. Porque encontramos una forma de usar Claude Code que no se degrada — un sistema de cinco archivos y un protocolo de fresh start que mantuvo cada sesión coherente del día 1 al día 9.
Teníamos 14 días. Terminamos en 9.
No por trabajar más horas. Porque encontramos una forma de usar Claude Code que no se degrada. Eso nos llevó la mayor parte del día 3 descubrirlo, y cambió cómo corrimos todas las sesiones que siguieron.
El 1 de febrero era la fecha límite inamovible. Una empresa de seguridad cloud necesitaba los precios FY27 en producción antes de que abriera el año fiscal — nuevas tarifas por tier, 11 multiplicadores regionales, una jerarquía de descuentos completamente reestructurada. Seis modelos nuevos, cientos de miles de filas cada uno, todos validando a 0,00% de varianza contra un año completo de facturas históricas.
El equipo de Finanzas contaba con esos números para los cierres de Q1. El equipo de ingeniería tenía una v1 limpia de la cual migrar, dbt en Snowflake, y Claude Code. Nueve días para hacerlo funcionar.
El entorno imponía una restricción dura: este codebase corría en el ejecutor nativo de dbt de Snowflake, no en dbt Cloud. No hay flujo de desarrollo local. El ciclo era: escribir código → hacer commit → hacer push → testear en la UI de Snowflake → registrar resultados → empezar la próxima sesión. Cada paso de validación requería ojos humanos en Snowflake. No puedes correr queries desde la sesión de IA. Esa estructura — código en la sesión de IA, validación afuera — es lo que hace que el sistema de cinco archivos sea esencial, no opcional.
Los días 1 y 2 fueron bien. El día 3 fue cuando chocamos con el problema.
Si hiciste una sesión larga con Claude Code, conoces ese momento. Por encima del 60% del contexto disponible — y según reportes de practitioners usando modelos de contexto más grande, la degradación puede empezar desde el 40–48% — algo cambia. El código se vuelve un poco más descuidado. Le pides que aplique un patrón que estableció dos horas antes y te da una respuesta diferente. Lo corriges, sigues. Una hora después vuelve a pasar.
Esto no es un bug. Así funcionan las arquitecturas transformer bajo carga. El modelo no guarda tu conversación en memoria — razona sobre ella, y a medida que el contexto se llena, las decisiones anteriores se comprimen y empiezan a desvanecerse.
En un sprint corto, empujas hasta el final. Nuestro primer instinto fue exactamente ese. "Ya casi terminamos este componente." No estábamos casi. Estábamos degradando.
La solución no fue un prompt mejor. Fue organizacional.
Construimos cinco archivos que vivían en el repo junto al código. Cada sesión empezaba leyéndolos. Cada sesión terminaba actualizándolos.
| Archivo | Propósito |
|---|---|
PLAN.md | Plan maestro, estado de cada batch, qué está hecho y qué sigue |
PATTERNS.md | Patrones probados — JOIN+QUALIFY, temporal lookups, estrategia de validación v2 |
BATCH.md | Alcance solo del batch actual. Se resetea al inicio de cada batch. |
RESULTS.md | Resultados de los tests en Snowflake para este batch |
SESSION.md | Lo que esta sesión específica necesita lograr |
La idea central era simple: Claude Code no necesita recordar el historial del proyecto. Necesita acceder al historial del proyecto en un formato que pueda leer en cinco minutos.
PATTERNS.md — cómo se ve una entrada en la práctica:
Este es el archivo más importante. Una entrada se ve así:
## JOIN + QUALIFY over Correlated Subqueries
Snowflake's native dbt doesn't support correlated subqueries.
Switch all temporal lookups to JOIN + QUALIFY pattern.
BAD (fails at runtime in Snowflake native):
SELECT *, (SELECT rate FROM pricing_rates WHERE effective_date <= billing_date ORDER BY effective_date DESC LIMIT 1)
FROM billing
GOOD:
SELECT billing.*, rates.rate
FROM billing
LEFT JOIN pricing_rates rates
ON rates.effective_date <= billing.billing_date
QUALIFY ROW_NUMBER() OVER (PARTITION BY billing.billing_id ORDER BY rates.effective_date DESC) = 1
Después del descubrimiento en el Batch 4, esta entrada fue al PATTERNS.md. Todas las sesiones que empezaron después del Batch 4 lo leyeron primero. Nunca volvimos a tener el problema de subqueries correlacionadas en las 20+ sesiones siguientes.
SESSION.md — cómo se ve al inicio de una sesión matutina:
## Session: Morning Day 6 — Batch 5 start
Context: Batch 4 complete and committed. JOIN+QUALIFY pattern confirmed working.
Finance review scheduled for this afternoon.
This session:
1. Build fct_revenue_v2 using seed-driven approach
2. Build fct_arr_v2 using same pattern
3. Validate grain: one row per (customer_id, product_line, billing_period)
Do NOT:
- Touch macros/fiscal/ without explicit request
- Use correlated subqueries (see PATTERNS.md)
- Push to prod target
Stop and update RESULTS.md before session ends.
El punto de estos ejemplos: Claude Code no necesita recordar el proyecto. Necesita leer cinco archivos y tener suficiente conocimiento institucional para empezar a trabajar de forma útil de inmediato.
El sistema de cinco archivos se convirtió en la base de un starter kit reutilizable que dejamos al equipo — lee cómo construimos controles que sobreviven al consultor.
Los reinicios no eran fracasos. Eran el protocolo.
Corrimos 28 sesiones en 9 días. La mayoría de los días tenía tres bloques bien diferenciados.
Mañana (4–5 horas): Instancia fresca. Cargar los archivos de contexto. Escribir 2–3 componentes. Hacer commit y push antes de que la sesión se degrade. Sin heroísmos — si no está en un commit, no es real.
Tarde (2–3 horas): Sin Claude Code. Testing en la UI de Snowflake — correr los modelos, revisar los resultados, documentar todo en RESULTS.md. Este es trabajo humano. La IA no puede correr tus queries ni leer tus datos reales. Puede escribir SQL; tú tienes que verificarlo. Esto no es una limitación — es donde el equipo de Finanzas detectó edge cases en el Batch 6 que los tests automatizados habían pasado por alto completamente.
Noche (3–4 horas): Instancia fresca de nuevo. Leer RESULTS.md. Arreglar lo que se rompió. Preparar el SESSION.md del día siguiente para que la sesión matutina sepa exactamente dónde arrancar.
Iniciábamos un reinicio fresco cada vez que empezaba un día nuevo (siempre — sin excepciones), cada vez que terminaban los tests en Snowflake y teníamos resultados para procesar, cada vez que pasábamos a un batch nuevo, cada vez que el uso del contexto superaba el 60%, y cada vez que Claude se contradecía o la calidad del código caía notablemente.
Los dos últimos son decisiones de criterio. Después de unos días desarrollas el instinto. Las respuestas se vuelven levemente más largas. Las explicaciones se ponen más charlatanas. El código pierde un poco de precisión en los bordes. Esa es tu señal — no un conteo de tokens específico, sino la sensación de que la sesión empezó a responder preguntas que no hiciste.
El trabajo se dividió en nueve batches, cada uno validado de forma independiente antes de tocar el siguiente.
El Batch 1 fue la base: seeds, macros de lookup. La infraestructura aburrida de la que todo lo demás depende — exactamente por eso fue primero. El Batch 2 fue la lógica central de precios: reglas de floor híbridas, estructuras de descuento Observability y External. Las piezas que tenían que funcionar antes de que la complejidad regional pudiera siquiera intentarse.
El Batch 3 es donde el alcance empezó a sentirse real: 11 multiplicadores regionales más nuevas tarifas por tier. No complicado por separado, complicado en combinación.
El Batch 4 es donde tuvimos nuestra mayor sorpresa.
El adaptador nativo de dbt para Snowflake no soporta subqueries correlacionadas. Si llevas años escribiendo SQL en Postgres o BigQuery, las usas sin pensar. No funcionan en Snowflake nativo, y los mensajes de error no siempre son claros sobre el porqué. La solución fue JOIN + QUALIFY — cada patrón de subquery correlacionada reescrito como un join con una cláusula QUALIFY para deduplicación. En el momento en que lo pusimos en PATTERNS.md, todas las sesiones siguientes ya lo sabían de entrada. Nunca más lo volvimos a pisar.
Un principio guió la validación de cada batch: paridad primero, corrección después. Cuando descubrimos que el sistema BI legacy tenía un bug en los cuartos fiscales — un trimestre que abarcaba 15 meses en lugar de 3 — replicamos el bug intencionalmente, validamos paridad contra él, y después lo corregimos en un PR separado. Esto es contraintuitivo. La movida correcta era escribir el código incorrecto primero. Si corriges y validas al mismo tiempo, no puedes distinguir si una varianza es un error de migración o una corrección intencional. Sepáralos. Valida paridad. Después mejora.
Los Batches 5 al 7 fueron el trabajo más difícil: modelos de revenue v2 basados en seeds, feedback de Finanzas sobre escenarios de descuentos de clientes específicos, lógica de descuentos de cuentas basada en prioridad que tenía que resolverse en exactamente el orden correcto. El Batch 7 fue el más difícil de lograr — el tipo de lógica donde el output parece correcto hasta que lo comparas con un caso donde dos reglas de descuento aplican simultáneamente y una debería ganar.
El Batch 8 quedó bloqueado. Esperando datos de un socio tecnológico importante. Pasamos al Batch 9 y volvimos. Cero scrambling — la estructura en batches hacía que el progreso parcial fuera seguro. No puedes hacer eso con un sprint monolítico.
El Batch 9 fue la línea de llegada: modelos ARR basados en seeds con descuentos FY27 aplicados. Validación final: cientos de miles de filas a 0,00% de varianza.
Nueve batches. Nueve días. Cinco días antes.
Cómo fue la validación en la práctica
El 0,00% por batch es real, pero vale la pena mostrar la metodología que lo produjo. La cifra de 0,002% de varianza general se cita seguido; el patrón de query que la generó, raramente.
El patrón central fue una query de varianza con FULL OUTER JOIN que corríamos al final de cada batch:
with v1 as (
select period_month, sum(net_revenue) as v1_value
from fct_revenue_v1
group by period_month
),
v2 as (
select period_month, sum(net_revenue) as v2_value
from fct_revenue_v2
group by period_month
)
select
coalesce(v1.period_month, v2.period_month) as period_month,
v1.v1_value,
v2.v2_value,
v2.v2_value - v1.v1_value as absolute_diff,
case
when v1.v1_value = 0 then 0
else round((v2.v2_value - v1.v1_value) / v1.v1_value * 100, 4)
end as pct_diff
from v1
full outer join v2 on v1.period_month = v2.period_month
order by period_month
Por qué FULL OUTER JOIN: un INNER JOIN dejaría pasar meses donde un sistema tiene datos y el otro no. El error de migración más común — un grain faltante — sería invisible. FULL OUTER JOIN lo hace aparecer de inmediato como un NULL en uno de los lados.
ROUND(..., 4) rastrea hasta 0,0001% de precisión. Nuestro umbral era 0,01%. Con esa precisión, una discrepancia de $100 en un dataset de el conjunto completo de datos de ingresos es visible.
Esta query corría tres veces por batch con distintos granos: total por mes, luego por cliente, luego por línea de producto. Una varianza que desaparecía al hacer drill-down era un problema de grain. Una varianza que persistía en todos los granos era un error de lógica. El debugging jerárquico redujo significativamente el tiempo promedio para aislar la causa raíz.
La disciplina de paridad-primero importó también acá. Una varianza entre v1 y v2 podía significar una de tres cosas: un error de migración, una corrección intencional, o un problema de calidad de datos que ya existía en v1. Solo puedes distinguirlos si separaste el trabajo de "replicar v1 exactamente" del trabajo de "corregir los bugs conocidos". Cuando ambos están pasando en el mismo batch, cada varianza es ambigua.
Algunas cosas nos sorprendieron de verdad.
El equipo de Finanzas fue mejor como QA de lo que esperábamos. Su revisión en el Batch 6 detectó edge cases en la jerarquía de descuentos que los tests automatizados habían perdido — escenarios con configuraciones de clientes específicas que nuestra cobertura de tests no había anticipado. La revisión humana de modelos financieros no es opcional. Esto no es una crítica a Claude Code. Es un recordatorio de que las personas que construyeron las reglas del negocio saben cosas que los datos no dicen en voz alta.
Claude Code es mejor en greenfield que en migración. Escribir componentes nuevos desde cero fue rápido. Migrar lógica existente preservando el comportamiento existente requirió mucho más contexto explícito sobre qué estaba haciendo la v1 y, más importante, por qué. A veces ese "por qué" es conocimiento tribal no documentado. De cualquier manera tiene que venir de ti. El caso clásico: la v1 tenía una definición de cuarto fiscal que era técnicamente incorrecta pero había sido incorrecta de forma consistente por dos años. La replicamos para validar paridad, después la corregimos. Intentar corregir y migrar al mismo tiempo es donde la mayoría de las migraciones asistidas por IA se tuercen.
La trampa del "casi terminamos" es real y tiene un momento específico. Las sesiones que empujamos demasiado eran casi siempre las nocturnas, no las matutinas. Al llegar la noche llevas horas trabajando y "un componente más" suena razonable. No lo era. Cada vez que cruzamos el 60% sin un reinicio, lo pagamos en la sesión siguiente limpiando el drift.
Empieza PATTERNS.md el día uno, no después del primer bug.
Lo fuimos llenando de forma reactiva: nos topábamos con un problema, lo resolvíamos, lo documentábamos. Una auditoría de las limitaciones conocidas del entorno antes del día uno vale unas horas — para Snowflake nativo con dbt, eso significa saber de antemano sobre el soporte de subqueries correlacionadas, la inferencia de tipos en seeds, y el comportamiento de los macros en window functions. El descubrimiento de JOIN+QUALIFY en el Batch 4 podría haber estado en PATTERNS.md desde el día uno. Ese conocimiento existe; solo hay que buscarlo antes de que arranque el sprint, no durante.
Cada equipo que usa Claude Code sobre un codebase existente tiene gotchas específicos de su entorno. Algunos son quirks de Snowflake. Algunos son quirks del dominio. Algunos son cálculos de calendario fiscal que solo tienen sentido si entiendes el motivo de negocio por el que fueron construidos así. Escríbelos antes de que la sesión tenga que aprenderlos a las malas.
La IA no es el cuello de botella. Tu capacidad de darle contexto consistente y estructurado a través de las sesiones sí lo es. Resuelve eso, y un sprint de 14 días se convierte en 9.
Sobre la configuración técnica detrás de las sesiones — CLAUDE.md, hooks, el sistema de guardrails — escribimos en nuestro post sobre ingeniería analítica asistida por IA con Claude Code y dbt.
Temas
Arturo Cárdenas
Fundador y Chief Data Analytics & AI Officer
Arturo es un consultor senior en analítica e IA que ayuda a empresas medianas y grandes a eliminar el caos de datos para desbloquear claridad, velocidad y ROI medible.


