Hooks pre-commit para repositorios heredados: guardarraíles simples
Configura hooks pre-commit en repos heredados con formateo, linting, escaneo de secretos y pruebas rápidas para bloquear commits dañinos antes de que corra CI.

Por qué los repos heredados siguen rompiéndose
Un repositorio heredado es código que no diseñaste tú. Las reglas no están claras, el estilo está mezclado y aparece el “funcionaba en mi máquina” porque nadie sabe qué espera el repositorio.
Los problemas empiezan pequeños y se convierten en fallos reales: un archivo usa tabulaciones, otro espacios; una corrección rápida añade una dependencia sin fijar versiones; un archivo de configuración se edita a mano y se aleja de lo que necesita producción. Incluso los cambios simples se vuelven riesgosos porque el repo está lleno de suposiciones ocultas.
Confiar solo en las comprobaciones de CI suele llegar demasiado tarde. CI corre después de que el commit ya está compartido, las revisiones ya están en marcha y la gente ya ha tirado del estado roto. Cuando CI falla, se pierde tiempo volviendo a ejecutar pipelines, haciendo rebase y adivinando qué cambio desencadenó el problema. Con el tiempo, los revisores dejan de confiar en las señales porque las fallas parecen “normales”.
El verdadero daño es cómo se acumulan los commits pequeños y dañinos:
- Un cambio de formato pequeño crea diffs ruidosos, así que un bug real se cuela.
- Un problema de lint se posterga “para más tarde” y luego se vuelve patrón.
- Una prueba no se ejecuta localmente, así que una build rota bloquea a todos.
- Se comete un secreto y obliga a una carrera para rotar claves.
Los hooks pre-commit ayudan porque trasladan las comprobaciones más comunes al punto más temprano: antes de que un cambio se convierta en un problema compartido. También crean una base consistente sin largas discusiones sobre “la forma correcta”.
Cuando funciona, se ve aburrido (en el buen sentido): menos builds rotos, diffs más pequeños y merges más rápidos porque las revisiones se centran en la lógica en lugar del estilo.
Qué deberían detectar los guardarraíles pre-commit
Los repos heredados fallan de maneras previsibles. El objetivo no es bloquear cada commit. Es parar los pocos errores de alto impacto que crean ruido, rompen builds o filtran datos.
Empieza con la deriva de formato. Los archivos sin formatear generan diffs enormes que ocultan cambios reales. Un arreglo de una línea no debería venir con 400 líneas de cambios por espacios. Formatear automáticamente en el commit mantiene las revisiones legibles y hace que los merges sean menos dolorosos.
Luego detecta problemas de lint que señalen bugs reales, no opiniones de estilo: variables indefinidas, imports sin usar, comparaciones sospechosas, awaits faltantes o patrones peligrosos como SQL construido por concatenación. Si tu linter se queja mayormente de comas, la gente aprenderá a ignorarlo.
Los secretos son el gran problema. Las claves entran cuando alguien copia un .env al repo, pega un token en un archivo de configuración o añade un log de depuración con credenciales. Un escaneo de secretos debe bloquear el commit antes de que llegue a un remoto.
Finalmente, ejecuta pruebas rápidas que cuesten segundos, no minutos. El mejor hook es el que la gente mantiene activado. Una prueba humo pequeña, un subconjunto rápido de unit tests o una verificación básica de tipos atrapan regresiones obvias temprano.
Un conjunto práctico de cosas que vale la pena bloquear:
- Cambios de auto-formato (para que los diffs sigan pequeños)
- Errores de lint de alta señal (probables bugs)
- Secretos detectados (tokens, claves API, claves privadas)
- Pruebas rápidas o checks de tipos (feedback rojo-verde rápido)
- Comprobaciones de cordura (JSON/YAML válidos, consistencia del lockfile)
Elige un conjunto pequeño de herramientas que encaje con tu repo
Al heredar un repo, el objetivo no es “cobertura máxima”. El objetivo es menos commits rotos. Empieza con tres o cuatro checks en los que confíes, y añade más solo después de que el equipo deje de pelear con la herramienta.
Un buen conjunto inicial:
- Un formateador (auto-arregla, sin debate)
- Un linter (atrapa errores reales, no estilo)
- Un escaneo de secretos (evita fugas de claves y tokens)
- Una prueba rápida o un check de build ligero (segundos, no minutos)
Adapta las herramientas al lenguaje que realmente ejecutas. Si tienes varios lenguajes, mantén los checks limitados a sus carpetas para que un lado no ralentice al otro.
La velocidad importa más que la perfección. Si un hook tarda 30 a 60 segundos, la gente lo saltará. Prefiere herramientas predecibles: misma salida en cada máquina, pocos falsos positivos y arreglos claros.
Decide qué bloquea un commit y qué solo avisa. Bloquear debe reservarse para cosas que casi siempre están mal (formato, errores de lint obvios, secretos). Las advertencias son mejores para reglas “quizá” (límites de complejidad, reglas de estilo estrictas) hasta que el repo esté estable.
Si trabajas en un monorepo, una regla sencilla ayuda: ejecuta solo lo que cambió. Si editas la carpeta API, ejecuta lint de API y una prueba rápida de API, no todo el repo.
Paso a paso: añadir pre-commit a un repo heredado
Crea una rama base (o tag) desde la rama main actual. Luego ejecuta los checks una vez en todo el repo para ver el radio de impacto. Esta primera ejecución no es sobre perfección. Muestra qué ya está roto, qué es ruido de estilo y qué bloqueará a todos desde el día uno.
A continuación, elige cómo quieres ejecutar los hooks.
- Un framework de pre-commit es más fácil de compartir entre el equipo y se comporta igual en la mayoría de máquinas.
- Los hooks nativos de git son simples, pero por defecto son locales, así que la gente se olvida de instalarlos o nunca lo hace.
Para la mayoría de equipos, una configuración compartida en el repo funciona mejor porque la configuración puede revisarse como cualquier otro cambio.
Mantén la configuración aburrida: un archivo, nombres claros y solo los checks que realmente quieres imponer. Instala hooks localmente y confirma que se ejecutan haciendo un cambio mínimo y commiteándolo.
Un despliegue que evita drama:
- Crea una rama base y ejecuta todos los hooks una vez para registrar qué falla.
- Añade el archivo de configuración al repo y mantén el primer set de hooks pequeño.
- Instala hooks y verifica que se ejecutan en un commit real.
- Empieza ejecutando solo sobre archivos cambiados (o en modo advertencia), luego cambia a bloqueo.
- Añade un hook nuevo a la vez para que las fallas sean fáciles de entender.
La aplicación gradual importa. Si tu primer commit bloquea por 700 cambios de formato, la gente bypassará los hooks. Arregla primero los problemas de alto riesgo (secretos, errores de lint obvios) y luego vuelve al formateo.
Añade formateo que corte las discusiones sobre estilo
El formateo es la barrera más fácil de añadir y da resultados rápidos. Elige un formateador por lenguaje y trátalo como la única fuente de verdad. Una vez que el repo tiene un estilo consistente, las revisiones pueden centrarse en la lógica, no en si alguien usó tabulaciones o comillas diferentes.
Para velocidad, ejecuta el formateo solo en archivos staged. Eso mantiene los commits ágiles y evita el problema de “toqué una línea y 2.000 líneas cambiaron”.
Una buena base de formateo también evita diffs ruidosos que hacen perder tiempo:
- Normaliza finales de línea (para que Windows y macOS no peleen)
- Elimina espacios finales
- Asegura que los archivos terminen con una nueva línea
- Mantén la codificación consistente (normalmente UTF-8)
Ten cuidado con archivos generados y carpetas vendor. Normalmente no quieres que un formateador reescriba outputs de build, lockfiles que no controlas o código tercero copiado. Exclúyelos explícitamente para que el formateo siga siendo predecible:
# Example idea (not a full config)
exclude: "^(dist|build|vendor|\.next|coverage)/"
Finalmente, pon de acuerdo cuándo ocurre el formateo. Si formateas al guardar en el editor, el formateo en commit debe coincidir, no pelear. Una regla simple funciona bien: formatea al guardar para comodidad diaria y aplica el formateo en commit para que el repo se mantenga limpio aunque alguien use otro editor.
Añade lint que detecte problemas reales
El linting es más útil en repos heredados cuando evita bugs reales, no cuando inicia discusiones sobre comas. Con hooks pre-commit, el objetivo es simple: parar errores obvios antes de que aterricen en una rama y desperdicien tiempo en CI.
Elige un linter que encaje con tu lenguaje principal y configúralo para priorizar señales de bug sobre estilo personal. Reglas que suelen dar resultado rápido incluyen:
- Variables e imports sin usar
- Manejo de errores faltante (promesas sin catch, valores devueltos ignorados)
- Construcción de strings insegura (fuente común de inyección)
- Comparaciones sospechosas y código inalcanzable
- Errores por copiar-pegar y lógica duplicada
Si tu repo está tipado, añade una verificación de tipos ligera donde ayude. No empieces chequeando todo el mundo. Apunta a las partes que más rompen en producción (auth, pagos, acceso a datos) y luego expande.
El código legado es la razón por la que el lint falla en proyectos heredados. No "arregles el mundo" solo para introducir un guardrail. Usa ignores dirigidos o limita el lint a archivos cambiados. Otra opción es una línea base temporal para que el hook bloquee solo nuevas violaciones, y luego reducir las viejas con el tiempo.
Haz la salida amigable. Prefiere configuraciones que muestren una breve explicación y una sugerencia de corrección, y habilita auto-fix donde sea seguro.
Mantén la ejecución predecible. Un hook que a veces tarda 5 segundos y otras 2 minutos será desactivado. Apunta a checks consistentes y rápidos sobre archivos staged, dejando análisis más pesados para CI.
Añade escaneo de secretos antes de que algo llegue al remoto
Los repos heredados a menudo esconden secretos a la vista. Un escaneo rápido de secretos en tus hooks pre-commit atrapa fugas obvias (claves API, tokens de acceso, claves privadas, secretos OAuth) antes de que salgan de un portátil.
Usa un escáner que detecte patrones y bloquee nuevas filtraciones. Muchos equipos eligen herramientas que puedan fallar el commit cuando se introduce un nuevo secreto, mientras permiten una línea base revisada de hallazgos conocidos.
Haz la regla simple: bloquear commits que añadan secretos nuevos. Si el hook salta, el desarrollador elimina el secreto o lo marca como falso positivo revisado mediante un proceso controlado (no desactivando el hook).
Para excepciones, prefiere una allowlist explícita y fácil de revisar. Mantén la base en control de versiones y trata los cambios a ella como significativos.
Los secretos suelen colarse por lugares predecibles:
- Archivos .env y config locales
- Archivos de ejemplo de configuración
- Logs de depuración y volcados de errores
- Fixtures de pruebas y respuestas HTTP grabadas
- Snippets pegados desde paneles de proveedores
Si descubres que un secreto ya fue comiteado, trátalo como comprometido, incluso si el repo es privado:
- Rota o revoca la clave/token
- Elimínalo del código y sustitúyelo por una variable de entorno
- Revisa el historial de git y purga si es necesario
- Busca dónde se usó (logs, despliegues, CI)
- Añade una comprobación de regresión para que no vuelva a pasar
Añade pruebas rápidas que los desarrolladores realmente ejecuten
Las únicas pruebas que ayudan en el momento del commit son las que la gente no se salta. Apunta a un conjunto pequeño y fiable que termine en menos de 60 segundos en un portátil normal. Si tarda más, los desarrolladores lo bypassarán y tu guardrail se convertirá en ruido.
Empieza con pruebas humo que demuestren que el repo sigue construyéndose y que las rutas más importantes siguen funcionando. Buenas candidatas son pruebas que detecten fallos obvios rápido, no cubrir todo.
Qué ejecutar en pre-commit
Manténlo corto y ligado a fallos que hayas visto realmente:
- Check de build (compilar, typecheck o un bundle mínimo)
- Sanity de migraciones DB (validar esquema o aplicar en una DB desechable)
- Una o dos llamadas API (endpoint de estado, flujo básico create-read)
- Prueba rápida de auth (login funciona, ruta protegida sigue protegida)
- Un subconjunto mínimo de unit tests etiquetados como “smoke” o “fast”
Haz que el comando funcione en cualquier máquina con la mínima configuración. Prefiere un único punto de entrada (por ejemplo, una tarea make o un script de package) que falle con salida clara. Evita requerir servicios especiales que solo existan en el portátil de un desarrollador.
Dónde van las pruebas más pesadas
Si una suite tarda minutos, sáquela del pre-commit:
- Pre-commit: pruebas humo rápidas solo
- On push o PR: unit tests completos e integración
- Nightly: end-to-end lentas, pruebas de carga, auditorías de dependencias
Para mantener tests deterministas, reduce dependencias de red frágiles. Stubea APIs externas, congela el tiempo cuando haga falta y usa fixtures locales. Si debes golpear un servicio, hazlo opcional y omítelo por defecto en pre-commit.
Ejemplo: estabilizar un repo prototipo generado por IA
Imagina un repo que empezó como un prototipo rápido de una herramienta de codificación por IA. Funciona bien en demos, pero producción sigue rompiéndose. Auth a veces regresa a la pantalla de login, las carpetas son un desastre y cada cambio corre el riesgo de abrir un nuevo agujero.
Aquí es donde los hooks pre-commit brillan: añade pequeños guardarraíles que detienen los peores errores temprano, sin empezar una reescritura grande.
Un despliegue simple en tres commits mantiene la confianza alta:
- Primer commit: añade un formateador y un linter básico con parámetros seguros.
- Segundo commit: añade escaneo de secretos, elimina cuidadosamente cualquier
.envcomiteado, reemplázalos por un archivo de ejemplo y rota claves filtradas. - Tercer commit: añade una prueba humo que refleje el último fallo (por ejemplo, un flujo de auth que debe producir una sesión válida).
Cuando expliques estos cambios a un fundador o cliente no técnico, céntrate en resultados:
- “Esto bloquea fugas accidentales de contraseñas y claves API antes de que el código salga del portátil.”
- “Esto detecta errores obvios antes de que desperdicien tiempo en CI o durante el deploy.”
- “Esta prueba rápida evita el apagón específico por el que acabas de pagar.”
- “Nada de esto cambia funcionalidades. Solo hace los cambios futuros más seguros.”
Trampas comunes y cómo evitarlas
Los repos heredados ya tienen suficiente fricción. La forma más rápida de hacer que pre-commit falle es que parezca un castigo. Los buenos guardarraíles suelen estar callados la mayor parte del tiempo y suenan solo cuando atrapan algo que te hubiera costado tiempo más adelante.
Mantén los hooks rápidos y predecibles
La trampa principal es hacer hooks tan estrictos que nadie pueda commitear. Si tardan más de un minuto, la gente usará flags para saltarlos y dejarán de confiar en la configuración.
Reglas prácticas:
- Ejecuta solo checks rápidos localmente (formato, lint, secretos y una prueba humo pequeña).
- No ejecutes la suite completa en cada commit.
- Arregla solo archivos staged. Auto-fix en archivos no staged crea diffs sorpresa.
- Empieza con advertencias para reglas ruidosas y apriétalas cuando el repo esté más limpio.
- Haz la salida clara: un mensaje de error debe decir qué hacer a continuación.
Si tu formateador o linter suele tocar archivos que no forman parte del commit, cambia a modo staged-only (o usa una herramienta que lo soporte) para que el hook nunca edite trabajo en progreso.
Haz que saltarse sea una decisión consciente
Otra forma de fallar es permitir que la gente ignore fallos sin plan. A veces saltar es válido (hotfix, herramienta upstream rota, demo urgente), pero debe ser una excepción deliberada.
Establece expectativas en el repo:
- Documenta cuándo está permitido saltar y qué seguimiento se requiere.
- Si alguien salta, CI debe seguir detectando la misma clase de problemas.
Vigila la paridad con CI. Si los desarrolladores usan una versión del linter y CI otra, obtendrás commits “funciona en mi máquina”. Fija versiones de herramientas y mantiene los hooks locales alineados con CI para que las fallas sean consistentes.
Checklist rápido y próximos pasos
Si vas a añadir hooks pre-commit a un repo heredado, mantén la primera pasada pequeña y fiable. Buscas atrapar problemas obvios temprano, sin que los commits parezcan un castigo.
Una base que puedes montar en una tarde:
- Auto-formato en el commit
- Lint solo de los archivos que tocaste (rápido, reglas de alta señal)
- Escaneo de secretos (claves, tokens, claves privadas, .env accidental)
- Ejecutar un comando de prueba rápido (smoke test o un conjunto focalizado de unit tests)
- Fijar versiones de herramientas
Desplíegalo por etapas para que el equipo no se resista:
- Baseline: ejecuta hooks solo sobre archivos nuevos o cambiados
- Warn: haz que los hooks fallen localmente, pero no bloqueen aún en CI
- Enforce: bloquea commits y falla CI cuando los hooks fallan
Tras una semana, deberías ver menos commits de “arregla formato”, menos comentarios en PRs sobre estilo, menos fallos de CI por problemas que se podrían haber atrapado y revisiones más rápidas porque la gente se centra en la lógica en lugar del ruido.
Sabe cuándo dejar de parchear. Si cada hook descubre problemas más profundos (tests frágiles, auth que falla, configs que se filtran, módulos enredados), los guardarraíles no arreglarán la base. Ahí necesitas trabajo de reparación focalizado: reestructurar, endurecer la seguridad y llegar a un punto donde las pruebas rápidas realmente signifiquen algo.
Si el repo empezó como un prototipo generado por IA y sigue rompiéndose en producción, FixMyMess (fixmymess.ai) está diseñado para esa situación exacta: diagnosticar la base de código, arreglar lógica y problemas de seguridad y dejarla lista para deploy. Una auditoría rápida también te ayudará a decidir qué guardarraíles aplicar primero para no ralentizar al equipo.
Preguntas Frecuentes
Why use pre-commit hooks if we already have CI?
CI detecta problemas después de que el commit ya está compartido. Los hooks pre-commit detienen los errores más comunes antes de que lleguen a tu rama, a tus compañeros o a la tubería, lo que reduce builds rotos y el tiempo perdido en reruns de CI.
What are the first guardrails I should add to an inherited repo?
Empieza con poco: un formateador, un linter enfocado en señales reales, un escaneo de secretos y una prueba rápida o una verificación de tipos. Este conjunto evita diffs ruidosos, errores evidentes, credenciales filtradas y regresiones fáciles sin convertir los commits en una tarea lenta.
How fast should pre-commit hooks be?
Apunta a menos de 60 segundos en un portátil normal y prefiere checks que se ejecuten solo sobre archivos staged o cambiados. Si los hooks se sienten lentos o impredecibles, la gente los saltará y perderás el beneficio completo.
How do I avoid a huge “format everything” diff?
Formatea solo lo que vas a commitear, no todo el repositorio. Así los diffs se mantienen pequeños, evitas cambios sorpresa en archivos no relacionados y es más fácil introducir un guardrail en una base de código desordenada sin iniciar una guerra de formato.
How can I enforce linting without breaking the whole repo?
Haz una ejecución base para ver qué falla ahora, y configura los hooks para bloquear solo las nuevas violaciones al principio. Así puedes seguir entregando mientras limpias gradualmente los problemas heredados, en lugar de intentar arreglar todo en un PR masivo.
What should we do when a hook finds a secret?
Haz del escaneo de secretos un bloqueo estricto para nuevas filtraciones y trata cualquier secreto comprometido como tal. Elimínalo del código, rótalo o revócalo, y deja el escáner activado para que el mismo error no vuelva a ocurrir.
What tests belong in pre-commit vs CI?
Empieza con una prueba humo simple que refleje los apagones reales que has visto (por ejemplo, un flujo de auth que debe crear una sesión válida) o una compilación mínima que tenga que pasar. Deja pruebas más pesadas para CI para que los commits locales sigan siendo rápidos.
How do pre-commit hooks work in a monorepo?
Limita las comprobaciones al árbol de carpetas que tocaste para que editar la API no dispare una build completa del front-end y viceversa. La regla por defecto debe ser “ejecuta lo que cambió”, porque en monorepos los hooks se vuelven lentos si no lo haces.
When is it okay to bypass hooks?
Permite saltar solo en emergencias reales y haz que sea una elección deliberada con seguimiento claro. Incluso si alguien salta localmente, CI debería ejecutar las mismas clases de checks para que las excepciones no se conviertan en comportamiento normal.
When should we stop adding guardrails and get outside help?
Si cada cambio sigue rompiendo auth, filtrando configs o mostrando problemas estructurales profundos, los hooks no arreglarán los cimientos: solo sacarán el dolor a la superficie. Si tu repo proviene de herramientas de IA y necesitas ponerlo en producción, FixMyMess puede ejecutar una auditoría de código gratuita, diagnosticar, reparar lógica, reforzar seguridad, refactorizar y preparar el despliegue, con la mayoría de proyectos terminados en 48–72 horas y alta tasa de éxito.