Saltar al contenido principal

El Archivo Más Importante en Nuestro Repositorio dbt Era un Markdown

Qué va en CLAUDE.md cuando usás Claude Code en un codebase financiero de dbt — y por qué los hooks son un tipo diferente de guardrail.

AC
Arturo Cárdenas
Fundador y Chief Data Analytics & AI Officer
19 de marzo de 2026 · 15 min de lectura
El Archivo Más Importante en Nuestro Repositorio dbt Era un Markdown

Punto Clave

Hay un problema real con usar asistentes de AI coding en sistemas financieros: no saben qué no deben tocar. Lo resolvimos con CLAUDE.md — tres categorías de contenido que hicieron confiable a Claude Code en un sistema de facturación con datos reales de ingresos.

Hay un problema real en usar asistentes de código con IA en sistemas financieros: no saben qué no deben tocar.

Pasamos nueve días usando Claude Code para reconstruir los modelos de precios FY27 sobre un codebase de dbt nativo en Snowflake. Sesenta y nueve modelos. Treinta y un macros. Datos reales de revenue. La IA podía escribir cada línea de código que le pedíamos. Exactamente por eso teníamos que ser cuidadosos con lo que le pedíamos.

La solución vivía en un archivo llamado CLAUDE.md.


El problema empieza antes de la primera sesión

El codebase al que entró Claude Code tenía cinco años de historia, y la mayor parte de esa historia era invisible.

La lógica de precios había estado hardcodeada en macros de Jinja desde 2021 — sentencias CASE/WHEN con valores de tarifas literales directamente en el código. Cada cambio de precios era un deploy. Cuando las tarifas cambiaban sin un deploy — lo que pasó — no quedaba registro en git. Durante la validación, encontramos un incremento del 118% sin documentar que solo existía a nivel de base de datos sin cambio en el código.

Esto es lo que Claude Code encuentra sin un CLAUDE.md: un codebase donde la lógica de negocio más importante está sin documentar o directamente perdida. La IA va a generar dbt sintácticamente válido que refleja cómo se supone que funcionan los sistemas de precios, no cómo funciona este en particular.


Una empresa de seguridad en la nube necesitaba modelos de precios actualizados antes del inicio del año fiscal. Nueve días. El equipo de datos llegó con escepticismo — no hacia dbt, sino hacia el desarrollo asistido por IA donde un número equivocado significa una factura equivocada. Ese escepticismo era apropiado.

Incorporamos Claude Code desde el día uno como un pair programmer genuino con contexto completo del repo. Algo que no habíamos contemplado: el codebase corría sobre el ejecutor nativo de dbt de Snowflake, no sobre dbt Cloud. Las subqueries correlacionadas que funcionan en dbt estándar fallan en runtime en Snowflake nativo. Sin eso escrito en algún lugar que Claude pudiera leer al inicio de cada sesión, generaba patrones que parecían correctos y rompían al testearlos.

Primera lección: la IA no conoce tu entorno. Tienes que escribirlo.


Para el día tres, CLAUDE.md se había vuelto más importante que cualquier archivo de modelos.

Claude Code lo lee al inicio de cada sesión. No es documentación — la documentación es para humanos. CLAUDE.md son instrucciones para la IA: qué reglas aplican acá, qué nunca debe hacer, qué significa el contexto de dominio. Cuando no escribes estas cosas, la IA recurre al conocimiento general de programación. En una aplicación web estándar, eso está bien. En un sistema de facturación con 11 multiplicadores regionales, un calendario fiscal que no coincide con los trimestres ISO, y una estructura de tiers de precios que mapea a compromisos contractuales en lugar de bandas de uso, el conocimiento general de programación no alcanza.

También hay un problema de degradación de contexto. Por encima de aproximadamente el 60% del contexto disponible, Claude empieza a perder coherencia — olvida decisiones que tomó antes en la sesión. Las sesiones frescas con traspaso estructurado de archivos resolvieron esto. Ese sistema está descripto en nuestro post sobre el sprint de 9 días.

Acá: qué va realmente en CLAUDE.md.


Tres categorías de contenido pertenecen ahí.

Arquitectura de tres capas de CLAUDE.md: Architecture Rules (capa 1, slate), Hard Boundaries (capa 2, amber), Domain Context (capa 3, teal), con la banda Hooks en la base como enforcement a nivel OS

Reglas de arquitectura. Dónde vive el código y cómo debe encarar el proyecto la IA.

All staging models live in models/staging/ — prefix: stg_
Intermediate joins belong in models/intermediate/ — prefix: int_
Final marts go in models/marts/ — prefix: fct_ or dim_
Never create a mart that sources directly from raw.
Never run `dbt run` without --select. Full-refresh on prod is catastrophic.

La regla de --select era una regla dura, no una preferencia blanda — un full-refresh en producción de Snowflake puede tardar horas y costar acorde. Claude sigue las reglas explícitas de forma confiable pero no sabe automáticamente que el billing de tu warehouse escala con el tiempo de cómputo.

Límites duros. Lo que la IA nunca debe hacer. A esta capa la llamamos "Destructive Operation Blockers + Financial Logic Ownership."

Never run `dbt run` without --select.
Never use FLOAT or DOUBLE for financial columns. Always NUMBER(38,9).
Never edit fiscal_quarter macros without detailed comments + human approval.
Never use correlated subqueries — they fail at runtime in Snowflake native.
For any model touching revenue, billing, or pricing:
  pause and request human confirmation before writing the final mart.
The legacy BI repo is read-only. May grep/glob, never edit.

La regla de NUMBER en lugar de FLOAT merece una oración completa. Los errores de punto flotante en modelos de revenue no son un riesgo hipotético — son un cierre trimestral malo y una auditoría que no tenías planificada. Claude Code, operando sobre conocimiento general de programación, va a usar FLOAT. Está bien para la mayoría de las cosas. En un sistema de facturación no lo está, y el error es silencioso hasta que Finance nota un problema de redondeo en sus reportes.

La regla de pausa y confirmación para modelos financieros fue lo más importante del archivo. Claude generaba el modelo, proponía los joins, resolvía el grain — y luego se detenía antes de escribir la capa de output final y pedía confirmación humana. Fricción mínima. Trazabilidad significativa. Exactamente el tipo de cosa que es obvia en retrospectiva y que está ausente por defecto.

También documentamos el flujo de migración: descubrir (solo lectura), proponer plan de modelos, implementar bajo revisión humana, validar con evidencia. Ese último ítem fue ganado — al principio, Claude reportaba reconciliaciones como exitosas antes de que las hubiéramos verificado contra el rango completo de fechas. Una vez que requerimos una query de reconciliación con resultados adjuntos, el comportamiento cambió de inmediato.

Contexto de dominio. Lo que los datos realmente representan.

pricing_tier maps to contractual commitments, not usage bands.
fiscal_quarter uses a 4-4-5 retail calendar. Do not assume ISO quarters.
client_id in billing tables is not the same as account_id in CRM.
Regional multipliers apply to one product line only.
The other uses a global flat rate. Do not apply regional multipliers to it.

El detalle del calendario fiscal importa en la práctica: el output del modelo trimestral tiene un lag de un mes intencionalmente. El modelo excluye el trimestre incompleto actual para que Finance no piense que el sistema está roto cuando los números de Q no se actualizan en el límite del trimestre. Sin esto documentado, Claude construye un modelo que parece correcto pero produce el número "equivocado" desde la perspectiva de Finance — porque Finance aprendió a esperar el lag.

La distinción client_id / account_id es el tipo de cosa que causa filas duplicadas silenciosas en cualquier JOIN entre billing y CRM. Sin error. Sin advertencia. Solo cifras de revenue infladas hasta que alguien corre un grain check.

La regla de multiplicadores regionales detectó un problema real: sin documentación explícita, Claude aplicaría la lógica de precios regionales a la línea de producto con tarifa plana de la misma forma que las aplica a la línea con multiplicadores regionales. Esa línea usa una tarifa global plana. Aplicar un multiplicador regional significa que todas las cuentas de Tokio y São Paulo tienen un precio incorrecto — y el error es lo suficientemente consistente como para parecer intencional.

Un data engineer nuevo tarda dos semanas en aprender esto. De cualquier forma hay que escribirlo — de lo contrario la IA lo reconstruye a partir de nombres de modelos y comentarios de columnas, y a veces lo reconstruye mal.


El catálogo de restricciones de Snowflake

Las plantillas estándar de CLAUDE.md cubren arquitectura, límites y contexto de dominio. Cuando estás corriendo dbt nativamente en Snowflake — no dbt Cloud, no dbt Core standalone — necesitas una quinta categoría: restricciones específicas del entorno que tu IA va a encontrar sin saber que existen.

Estas son las cuatro que documentamos después de la primera semana del sprint.

Las subqueries correlacionadas fallan en runtime. Claude las genera naturalmente cuando construye lógica de lookup. Compilan, se ven correctas, y fallan cuando Snowflake las ejecuta. El fix es JOIN + QUALIFY:

-- What Claude generates by default (fails at runtime in Snowflake native)
SELECT *, (SELECT rate FROM rates WHERE rates.date <= t.date ORDER BY date DESC LIMIT 1) as rate
FROM transactions t

-- What actually works
SELECT t.*, r.rate
FROM transactions t
LEFT JOIN rates r ON r.date <= t.date
QUALIFY ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY r.date DESC) = 1

Una vez que esto estuvo en CLAUDE.md, Claude dejó de generar el patrón roto por completo.

Los macros en PARTITION BY fallan silenciosamente. Cuando Claude llama a un macro dentro de la cláusula PARTITION BY de una window function, dbt lo compila sin error. La query corre. Los resultados son incorrectos. Lo descubrimos cuando los datos de Q1–Q3 FY26 desaparecieron del ARR trimestral — el cálculo de trimestre fiscal era una llamada a macro dentro de PARTITION BY, y estaba produciendo particionamiento incorrecto. El fix es inlinear la lógica en lugar de llamar al macro. Esto está documentado ahora como regla dura: nada de llamadas a macros dentro de las cláusulas PARTITION BY u ORDER BY de window functions.

La inferencia de tipos en seeds es incorrecta. El account ID "00123" se convierte en el entero 123. Todo archivo de seeds necesita tipos de columna explícitos en seeds.yml — sin ellos, los ceros a la izquierda desaparecen y la precisión decimal se pierde silenciosamente.

No hay desarrollo local. El flujo es escribir → commitear → pushear → testear en la UI de Snowflake. Claude va a sugerir correr dbt run localmente. En Snowflake nativo, eso no funciona. Documentarlo evitó sugerencias que habrían consumido tiempo de sesión.


CLAUDE.md establece las reglas. Los hooks las hacen cumplir.

Las instrucciones se degradan, los hooks no: dos tracks horizontales que muestran cómo las instrucciones de CLAUDE.md se desvanecen pasado el 60% del contexto mientras la línea de hooks se mantiene perfectamente sólida

Los pre-commit hooks corren antes de que cualquier commit llegue al repo. Los pre-tool-use hooks (específicos de Claude Code, configurados en .claude/settings.json) se disparan antes de que Claude ejecute una tool call. Construimos dos tipos.

Pre-commit hooks que detectan SQL destructivo antes de que pueda llegar al repo:

# Block DROP and TRUNCATE in model files
if grep -rE "^\s*(DROP|TRUNCATE)\s" models/; then
  echo "ERROR: Destructive SQL found in models/"
  exit 1
fi

Pre-tool-use hooks que interceptan a Claude antes de que edite rutas protegidas:

# Prevent edits to macros/fiscal/ without explicit confirmation
if tool == "edit" and "macros/fiscal/" in tool_input.get("path", ""):
    raise Exception("Protected path: macros/fiscal/ requires manual review")

La protección de los macros fiscales no era negociable — esos macros codificaban lógica de negocio de varios años anterior al equipo actual. Claude podía leerlos y referenciarlos pero no editarlos sin que un humano desbloqueara la ruta.

La distinción clave: las instrucciones de CLAUDE.md pueden degradarse pasado el 60% del contexto. Los hooks corren a nivel OS y no olvidan. Nunca tuvimos que usar la salida de emergencia.


¿Dónde encaja el MCP de dbt?

dbt lanzó un servidor MCP (abril 2025) que le da a los asistentes de IA acceso de herramientas a un proyecto de dbt en vivo — consultar el manifest, correr comandos de dbt, ejecutar SQL contra tu warehouse. Es útil. Pero opera en una capa diferente del problema.

Así es como mapean las capas:

Modelo de capas dbt MCP: cuatro bandas horizontales mostrando dbt MCP (acceso a herramientas), Agent Skills (flujo de trabajo), CLAUDE.md (contexto de dominio), y Hooks (enforcement a nivel OS) — cada capa resuelve un problema distinto

  • dbt MCP: acceso a herramientas — Claude puede consultar el linaje, correr dbt test, ejecutar SQL contra tu warehouse en runtime
  • Agent Skills (los archivos de skill markdown de dbt): codificación de flujo de trabajo — cómo usar comandos de dbt en secuencia
  • CLAUDE.md: contexto de dominio + reglas — calendarios fiscales, definiciones de métricas no estándar, restricciones específicas del engagement, cualquier cosa que no esté en el manifest — persistente entre sesiones
  • Hooks: enforcement — reglas que deben cumplirse incluso cuando el contexto está lleno

dbt Labs ha declarado explícitamente que proveer "conocimiento específico del usuario y del dominio" es un ítem futuro en el roadmap del MCP, no una capacidad actual. El MCP puede decirle a Claude qué modelos existen y cómo se conectan. No puede decirle que client_id y account_id son claves diferentes que producen duplicados silenciosos en un JOIN, o que los trimestres fiscales tienen un lag de un mes por diseño, o que una línea de producto usa una tarifa global plana mientras otra usa multiplicadores regionales.

El MCP y CLAUDE.md no son alternativas. El MCP maneja el acceso a herramientas; CLAUDE.md maneja el significado. Necesitas ambos cuando el proyecto tiene reglas de negocio que viven fuera del manifest.


De específico del engagement a reutilizable por el equipo

Al final del engagement, destilamos el setup en tres versiones: el CLAUDE.md específico del engagement con todo el conocimiento de dominio, un playbook generalizado de Clarivant reutilizable entre engagements, y un starter kit simplificado que el equipo de datos pudiera adoptar de forma independiente — cloná, seguí cinco pasos, arrancá una sesión.

El valor de construir esto con cuidado: sobrevive al engagement. El enfoque completo de handoff está documentado en Barreras de IA Que Sobreviven al Consultor.


Preguntas frecuentes

¿Claude Code realmente entiende dbt, o simplemente escribe SQL?

Entiende bien los patrones de dbt — refs, sources, materializaciones, macros, el modelo del DAG. Lo que no conoce son las convenciones específicas de tu proyecto, la semántica de dominio de tu modelo de datos, ni las limitaciones específicas del entorno como el comportamiento de las subqueries correlacionadas en Snowflake nativo. CLAUDE.md cierra esa brecha. Sin él, obtienes dbt sintácticamente válido que es arquitectónicamente incorrecto — lo cual es peor que un error de sintaxis porque no falla, simplemente se desvía hasta que alguien lo nota.

¿Cómo evitas que CLAUDE.md quede desactualizado a medida que el proyecto evoluciona?

Tratalo como dbt_project.yml — cambia cuando el proyecto cambia. Agregamos una regla: cualquier decisión tomada en una sesión que deba mantenerse se agrega a PATTERNS.md o CLAUDE.md antes de cerrar la sesión. Dos minutos. La brecha entre lo que dice CLAUDE.md y lo que el proyecto realmente hace es cómo se acumulan las inconsistencias.

¿Esto no es simplemente prompt engineering?

El contenido de CLAUDE.md, sí. El sistema de hooks, no. Los hooks corren a nivel OS y se disparan antes de que Claude pueda commitear código o modificar un archivo protegido. Las instrucciones se degradan por los límites de contexto. Los hooks no. Necesitas ambos: las instrucciones moldean el comportamiento que quieres, los hooks previenen el comportamiento que no puedes permitir que ocurra por accidente. Más adelante en una sesión larga — pasado el 60% del contexto disponible — Claude puede perder coherencia en reglas que venía siguiendo. Un hook que era verdad en el minuto uno de la sesión sigue siendo verdad en el minuto noventa.

¿Y el servidor MCP de dbt? ¿No hace innecesario CLAUDE.md?

Operan en capas distintas. El MCP le da a Claude acceso a herramientas sobre tu proyecto de dbt en vivo — linaje, manifest, la posibilidad de correr tests y ejecutar SQL. CLAUDE.md lleva el contexto de dominio: las reglas de negocio, las convenciones fiscales y las restricciones del proyecto que no están en el manifest. dbt reconoció que el contexto de dominio es un ítem futuro en el roadmap del MCP. Hasta entonces, CLAUDE.md es el puente. Quieres ambos: el MCP para el acceso a herramientas, CLAUDE.md para el significado.

¿Todo proyecto de dbt debería tener este setup?

No. Si estás explorando un dataset, prototipando, o trabajando solo en un proyecto que no es de producción, el overhead no se justifica. Este setup vale la pena cuando tres cosas son verdad al mismo tiempo: los modelos tocan dinero, el codebase tiene lógica de dominio que no es obvia desde el schema, y múltiples sesiones van a construir unas sobre otras durante días. Las tres eran verdad acá. Ese es el setup para el que fue construido.


El Sr. Director de Analytics pasó de escéptico a defensor a lo largo del engagement. No porque le dijéramos que la IA era genial. Sino porque las restricciones que pusimos en lugar significaban que la IA era confiable de verdad sobre datos financieros.

No construimos CLAUDE.md para limitar lo que Claude podía hacer. Lo construimos para que Claude pudiera ser confiable haciendo más.

Configurar workflows asistidos por IA que los equipos puedan mantener es parte de nuestra práctica de AI Strategy — los controles son tan importantes como las herramientas.


Si tu equipo usa IA en datos financieros sin controles, o la evita porque no los tiene, podemos ayudarte a definir los límites correctos. Empieza con una revisión de controles.

Temas

claude codeCLAUDE.mddbtsnowflakehooksguardrailsseguridad aidatos financierosanalytics engineeringdegradación de contexto
Compartir este artículo:
AC

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.

¿Listo para convertir datos en decisiones?

Hablemos de cómo lograr ROI medible en meses.