GitFlow vs GitHub Flow vs Trunk-Based: cuál elegir
Comparativa senior de las tres estrategias de branching: tradeoffs, cuándo usar cada una y cómo migrar sin romper el equipo.
La mayoría de los equipos con los que he trabajado no eligió GitFlow. Lo heredó. Era el diagrama de colores que circulaba en 2010, alguien lo puso en el wiki, y diez años después el equipo sigue creando ramas develop y release/* sin que nadie recuerde por qué. La estrategia de branching es una decisión de arquitectura con consecuencias diarias en velocidad, riesgo y moral del equipo. Aquí comparo GitFlow, GitHub Flow y trunk-based development por lo que cuestan de verdad, y te doy un eje para decidir cuál encaja con tu contexto. Si quieres el marco completo de trabajo en equipo con Git, lo tienes en la guía de trabajo en equipo con Git y GitHub; esto es la decisión concreta que cuelga de ahí.
¿Qué problema resuelve una estrategia de branching?
Una estrategia de branching es un acuerdo sobre cuánto tiempo vive una rama antes de integrarse en la línea principal. Ese es el eje real, y todo lo demás son consecuencias.
El conflicto de fondo es viejo: cuanto más tiempo vive una rama separada de main, más diverge, y más caro es el merge final. A esa deuda acumulada se la llama merge hell, y no es teórica. Una rama que lleva tres semanas sin tocar main ha visto pasar decenas de commits ajenos por debajo. Cuando por fin la integras, reconcilias cambios que ni siquiera recuerdas.
Las tres estrategias que vamos a ver son tres respuestas distintas a la misma pregunta: ¿cuándo integro? GitFlow dice “cuando la release esté lista”. GitHub Flow dice “cuando la PR pase review”. Trunk-based dice “hoy, y si el código no está listo lo escondo detrás de un flag”. El resto de sus diferencias, número de ramas, nombres, ceremonia, sale de esa respuesta.
¿Cómo funciona GitFlow y qué cuesta?
GitFlow es el modelo de Vincent Driessen[1]: dos ramas permanentes (main y develop) más tres tipos de ramas temporales (feature/*, release/* y hotfix/*). Las features salen de develop, las releases se estabilizan en su propia rama antes de fusionarse a main, y los hotfixes salen directos de main para producción.
En comandos, arrancar una feature y prepararla para release se ve así:
# GitFlow: una feature vive en develop, luego pasa por una rama de release
git switch develop
git switch -c feature/pago-stripe
# ...commits de la feature...
git switch develop
git merge --no-ff feature/pago-stripe
# cuando toca versionar, la release se estabiliza aparte
git switch -c release/1.4.0 develop
# ...solo fixes de estabilización aquí...
git switch main
git merge --no-ff release/1.4.0
git tag -a v1.4.0 -m "release 1.4.0"
El --no-ff no es decorativo: fuerza un commit de merge aunque el fast-forward fuera posible, y así conserva la topología de cada rama en el historial. En GitFlow eso importa, porque el diagrama de ramas ES la documentación del proceso.
¿Qué cuesta? Ramas de larga vida. Una release/* que se estabiliza durante dos semanas mientras develop sigue avanzando genera dos líneas de trabajo que hay que reconciliar con cherry-picks. Multiplícalo por varias features simultáneas y tienes un equipo que dedica tiempo real a gestionar el árbol en vez de a escribir producto. El propio Driessen añadió años después una nota a su artículo original recomendando modelos más simples para equipos que despliegan software de forma continua[1]. El autor de GitFlow te avisa de que probablemente no lo necesitas.
Donde GitFlow todavía brilla: software versionado con varias versiones en soporte simultáneo. Si mantienes la 2.3, la 2.4 y la 3.0 a la vez para clientes distintos, esas ramas de larga vida dejan de ser deuda: son el mapa de tu realidad.
¿Qué es GitHub Flow y por qué ganó?
GitHub Flow tiene una sola regla que recordar: main siempre es desplegable, y todo lo demás son ramas cortas que nacen de main y vuelven a main por una PR[2]. No hay develop, no hay release/*, no hay ceremonia de versionado.
El ciclo completo cabe en pocos comandos:
# GitHub Flow: rama corta desde main, PR, merge, deploy
git switch main
git switch -c fix/timeout-checkout
# ...commits pequeños...
git push -u origin fix/timeout-checkout
# abres la PR, pasa CI + review, merge a main
# main desplegable => se despliega
Ganó por dos razones que no tienen nada que ver con la moda. La primera es que encaja con el deploy continuo: si despliegas cuando quieres y no en fechas fijas, no necesitas una rama que represente “la próxima versión”. La segunda es que la PR se convirtió en la unidad de trabajo, review y CI a la vez, y GitHub la puso en el centro de su producto. El modelo mental se reduce a “una rama, una PR, un merge”.
El coste está escondido en su simplicidad: GitHub Flow asume que tu CI es fiable. Si main se despliega y tu suite de tests no es de confianza, cada merge es una tirada de dados en producción. GitHub Flow no te protege de eso; te obliga a resolverlo.
¿Qué es trunk-based development?
Trunk-based development lleva la idea al extremo: todo el mundo integra en main varias veces al día, y las ramas, cuando existen, viven menos de un día[3]. El objetivo explícito es eliminar la divergencia antes de que se acumule.
Aquí aparece la pregunta obvia. Si integro código a main a diario, ¿qué pasa con las features que tardan dos semanas? La respuesta son los feature flags: el código se fusiona a main apagado, invisible para el usuario, y se enciende cuando está completo y probado.
# Trunk-based: commits pequeños a main, la feature incompleta vive apagada
git switch main
git pull --rebase
# el código nuevo entra detrás de un flag apagado
# (en el código de la app, no en el shell):
# if (flags.checkoutV2) { renderCheckoutV2(); }
git commit -am "checkout v2 tras flag (off)"
git push
Trunk-based tiene la menor deuda de merge de las tres porque nunca deja que la deuda crezca. Pero es el modelo que más exige de la infraestructura. Sin CI rápida y de confianza, integrar a main a diario significa romper a los demás a diario. Sin feature flags, no tienes forma de fusionar trabajo incompleto sin exponerlo. Es un modelo de alta cadencia que paga su velocidad con disciplina de plataforma.
Vale la pena separar dos variantes que la gente mezcla. Está el trunk-based con commits directos a main, viable en equipos pequeños y muy senior, y está el trunk-based con ramas de vida ultracorta y PR ligera, que es lo que practican la mayoría de equipos grandes que dicen hacer trunk-based. La diferencia práctica es si la PR es un gate obligatorio o no.
¿Cómo decidir?
La decisión sale de tres variables: cada cuánto liberas, cuánta gente toca el mismo repo, y cuánto confías en tu CI. No de qué modelo tenga el diagrama más bonito.
| Dimensión | GitFlow | GitHub Flow | Trunk-based |
|---|---|---|---|
| Ramas de larga vida | Sí (develop, release) | No | No |
| Cadencia de release | Fechas fijas, versionado | Continua, on-demand | Continua, varias veces al día |
| Tamaño de equipo ideal | Medio-grande con versiones en soporte | Pequeño a grande | Requiere madurez, no tamaño |
| Madurez de CI requerida | Media | Alta | Muy alta |
| Complejidad mental | Alta (5 tipos de rama) | Baja (una rama, una PR) | Baja de reglas, alta de infra |
| Riesgo de merge hell | Alto | Bajo | Mínimo |
Léelo como un eje, no como tres cajas. En un extremo tienes releases empaquetadas y calendario fijo: GitFlow paga su complejidad. En el otro extremo tienes deploy continuo y CI de confianza: trunk-based te da la máxima velocidad. GitHub Flow es el punto medio sensato para la mayoría, y es donde yo empezaría cualquier equipo nuevo que despliega servicios web.
Un atajo honesto: si despliegas continuo, empieza por GitHub Flow y evoluciona hacia trunk-based cuando tu CI y tus feature flags aguanten el ritmo. Si tienes que mantener versiones antiguas en producción para clientes, GitFlow no es legacy, es la herramienta correcta.
¿Cómo migrar sin romper el equipo?
No migres de golpe un lunes por la mañana. La forma que me ha funcionado es reducir la vida de las ramas primero y cambiar la nomenclatura después.
Empieza recortando el tamaño de las PR. Antes de tocar el modelo, pon un límite blando: ninguna PR debería vivir más de dos o tres días. Ese solo cambio te acerca a GitHub Flow sin renombrar nada, y expone si tu CI aguanta merges frecuentes. Si tu suite de tests se cae cuando aumentas la frecuencia de merge, ese es tu verdadero bloqueo, y no lo arregla ninguna estrategia de branching.
Después, retira develop. En GitFlow, develop es la rama de integración. Si ya despliegas desde main de forma continua, develop es una capa intermedia que no aporta y sí genera merges dobles. Fusiona develop a main, congela la creación de nuevas release/*, y declara main como la única fuente de verdad desplegable.
El paso a trunk-based es el último y el más caro, porque no es un cambio de Git sino de infraestructura: necesitas feature flags de verdad y una CI en la que confíes para desplegar sin mirar. El día a día concreto de PRs, commits y review una vez elegido el modelo lo tienes en el flujo de trabajo con Git en equipo.
Errores comunes
Adoptar GitFlow porque sí en un equipo de deploy continuo. Este es el más caro y el más frecuente. Si despliegas a producción varias veces por semana pero mantienes develop y release/*, estás pagando la complejidad de GitFlow sin cobrar su beneficio, que es coordinar releases versionadas. Los feature branches se acumulan, los merges se hacen bola, y nadie sabe qué hay realmente en producción. Detéctalo con una pregunta: ¿cuántas versiones tenéis en soporte simultáneo? Si la respuesta es “una, la de producción”, GitFlow te sobra.
Trunk-based sin CI ni feature flags. Trunk-based sin la infraestructura que lo sostiene rompe main a diario. Es adoptar la regla (“integra a menudo”) sin la red de seguridad que la hace viable. Si no tienes tests fiables que corran en cada push ni una forma de esconder trabajo incompleto, integrar a main varias veces al día es garantizar que alguien encuentra main roto cada mañana.
Ramas de release eternas que acumulan cherry-picks. Una release/1.4.0 que lleva un mes abierta ha dejado de ser una rama de estabilización. Ahora es una segunda línea de desarrollo paralela, y cada fix que entra en main hay que replicarlo a mano con git cherry-pick. Cada cherry-pick es una oportunidad de olvidarse uno. Si tus ramas de release viven semanas, las ramas no son el problema; estás estabilizando demasiado tarde.
GitHub Flow con PRs gigantes de dos semanas. GitHub Flow con una PR que lleva quince días abierta es GitFlow disfrazado. La rama es de larga vida aunque no se llame develop, la deuda de merge crece igual, y encima has perdido la ceremonia de GitFlow que al menos te daba un sitio para estabilizar. Si tus PRs no se cierran en días, en la práctica estás manteniendo ramas de larga vida con otro nombre.
Checklist de decisión
- Has contado cuántas versiones mantienes en producción simultáneamente (una sola apunta a GitHub Flow o trunk-based)
- Sabes cada cuánto despliegas de verdad (fechas fijas apuntan a GitFlow; continuo, a los otros dos)
- Tu CI corre en cada push y confías en su resultado antes de plantearte trunk-based
- Tienes feature flags reales antes de integrar trabajo incompleto a main
- Ninguna rama del equipo vive más de unos pocos días sin integrarse
- La estrategia elegida está escrita y acordada, no heredada de un diagrama de 2010
Elegir estrategia de branching es un ejercicio de leer tu propio contexto con honestidad: cadencia de release, número de versiones vivas y confianza en tu CI. Si quieres bajar de la estrategia a las prácticas concretas de commits y PRs que aplican con cualquiera de los tres modelos, las tienes en las mejores prácticas de Git. Y si estás construyendo esa base desde cero, el curso Git de cero a profesional recorre este mismo camino con ejercicios sobre repos reales.
Fuentes
- A successful Git branching model, de Vincent Driessen. Artículo original de GitFlow y la nota posterior del autor recomendando modelos más simples para despliegue continuo.
- GitHub Flow, de Scott Chacon. Post original que define GitHub Flow y la regla de main siempre desplegable. La documentación de GitHub recoge hoy el flujo de rama, PR, review y merge.
- Trunk-Based Development, de Paul Hammant. Referencia canónica de trunk-based, ramas de vida corta y feature flags.
Preguntas Frecuentes
¿GitFlow está muerto?
No. Está mal aplicado en la mayoría de los sitios donde sigue vivo. GitFlow tiene sentido cuando mantienes varias versiones en soporte a la vez o liberas en fechas fijas con un proceso de estabilización real. Para un equipo que despliega servicios web de forma continua, es complejidad que no cobra su beneficio.
¿Trunk-based necesita feature flags obligatoriamente?
En la práctica, sí, en cuanto una feature tarda más de un día en estar lista. Trunk-based integra a main constantemente, así que necesitas una forma de fusionar código incompleto sin exponerlo al usuario. Sin feature flags, tus únicas opciones son ramas más largas (y ya no es trunk-based) o desplegar features a medias.
¿Puedo mezclar estrategias?
Sí, y la mayoría lo hace sin darse cuenta. Un caso común es GitHub Flow con una rama de release puntual para un lanzamiento grande que necesita estabilización. El riesgo es que la mezcla se convierta en incoherencia: si cada desarrollador sigue reglas distintas, dejas de tener una estrategia coherente. Mézclalas por decisión explícita, no por deriva.
¿GitHub Flow y trunk-based son lo mismo?
Se parecen mucho y se confunden con razón, pero no son idénticos. GitHub Flow pone la PR como gate obligatorio: nada entra a main sin review. Trunk-based en su forma pura permite commits directos a main, y su énfasis está en la frecuencia de integración (varias veces al día) más que en el mecanismo de review. En equipos grandes la línea se difumina, porque el trunk-based realista usa ramas ultracortas con PR ligera, que es casi GitHub Flow acelerado.
¿Qué usan los equipos modernos que despliegan continuo?
La combinación más habitual es GitHub Flow como punto de partida y trunk-based como destino cuando la infraestructura lo permite. Ambos comparten lo importante: ramas cortas, PRs pequeñas y CI de confianza. GitFlow queda para el software versionado con soporte de múltiples versiones, que es un caso legítimo pero cada vez menos común en producto web.