¿La mejor estrategia de ramificación al realizar una integración continua?

100

¿Cuál es la mejor estrategia de ramificación para utilizar cuando desee realizar una integración continua?

  1. Release Branching: se desarrolla en el tronco, guarda una rama para cada lanzamiento.
  2. Ramificación de características: desarrolle cada característica en una rama separada, solo fusione una vez estable.

¿Tiene sentido usar ambas estrategias juntas? Como en, ¿se ramifica para cada versión pero también se ramifica para funciones grandes? ¿Alguna de estas estrategias encaja mejor con la integración continua? ¿Tendría sentido usar la integración continua cuando se usa un tronco inestable?

ReyNestor
fuente
2
Nota al margen: algunos dirían que incluso cuando se incorporan nuevas funciones, todo debería ser siempre estable. Por otro lado, eso podría ser algo idealista.
Keith Pinson

Respuestas:

21

Encuentro el tema realmente interesante ya que dependo mucho de las sucursales en mi trabajo diario.

  • Recuerdo a Mark Shuttleworth proponiendo un modelo sobre cómo mantener la rama principal prístina mientras iba más allá de la IC convencional. Lo publiqué aquí .
  • Como estoy familiarizado con el control de crucero, también escribí en un blog sobre las ramas de tareas y la IC aquí . Es un tutorial paso a paso que explica cómo hacerlo con Plastic SCM .
  • Finalmente, encontré algunos de los temas sobre CI (y potencialmente hablando de ramificación) en el libro de Duvall sobre CI también muy interesantes .

Espero que encuentres interesantes los enlaces.

pablo
fuente
Agregamos soporte a Bamboo para hacer rama por tarea codicesoftware.blogspot.com/2012/02/… , y parece que su versión más reciente lo hará de forma nativa con varios controles de versión, incluyendo dvcs.
pablo
20

La respuesta depende del tamaño de su equipo y la calidad de su control de fuente y la capacidad de combinar correctamente conjuntos de cambios complejos. Por ejemplo, en un control de fuente de rama completo como CVS o SVN, la fusión puede ser difícil y es posible que esté mejor con el primer modelo, mientras que si usa un sistema más complejo como IBM ClearCase y con un tamaño de equipo más grande, podría ser mejor con el segundo modelo o una combinación de los dos.

Personalmente, separaría el modelo de rama de características, donde cada característica principal se desarrolla en una rama separada, con subramas de tareas para cada cambio realizado por un desarrollador individual. A medida que las características se estabilizan, se fusionan con el tronco, que se mantiene razonablemente estable y pasa todas las pruebas de regresión en todo momento. A medida que se acerca al final de su ciclo de lanzamiento y todas las ramas de funciones se fusionan, estabiliza y ramifica una rama del sistema de lanzamiento en la que solo realiza las correcciones de errores de estabilidad y los backports necesarios, mientras que el tronco se usa para el desarrollo de la próxima versión y usted nuevamente bifurcarse para nuevas ramas de características. Y así.

De esta manera, el tronco contiene siempre el código más reciente, pero se las arregla para mantenerlo razonablemente estable, creando etiquetas estables (etiquetas) en cambios importantes y fusiones de funciones, las ramas de funciones son de desarrollo rápido con integración continua y las subramas de tareas individuales pueden ser a menudo actualizado desde la rama de funciones para mantener a todos trabajando en la misma función sincronizados, mientras que al mismo tiempo no afecta a otros equipos que trabajan en diferentes funciones.

Al mismo tiempo, tiene a lo largo del historial un conjunto de ramas de lanzamiento, donde puede proporcionar backports, soporte y corrección de errores para sus clientes que, por cualquier motivo, permanecen en versiones anteriores de su producto o incluso en la última versión lanzada. Al igual que con el tronco, no configura la integración continua en las ramas de la versión, se integran cuidadosamente después de pasar todas las pruebas de regresión y otros controles de calidad de la versión.

Si por alguna razón dos características son co-dependientes y necesitan cambios entre sí, puede considerar desarrollar ambas en la misma rama de características o requerir que las características fusionen regularmente partes estables del código en el tronco y luego actualicen los cambios desde tronco para intercambiar código entre ramas troncales. O si necesita aislar esas dos características de otras, puede crear una rama común desde la cual ramifique esas ramas de características y que pueda usar para intercambiar código entre las características.

El modelo anterior no tiene mucho sentido con equipos de menos de 50 desarrolladores y un sistema de control de fuente sin ramas escasas y la capacidad de fusión adecuada como CVS o SVN, lo que haría de todo este modelo una pesadilla para configurar, administrar e integrar.

Jiri Klouda
fuente
5
No estoy seguro de estar de acuerdo en que lo que describe no tiene sentido para equipos de menos de 50 desarrolladores. También puedo ver beneficios para equipos mucho más pequeños. +1
Aardvark
2
Por supuesto, existen beneficios para equipos de cualquier tamaño. La pregunta es en qué tamaño del equipo los beneficios superan los costos asociados con un proceso pesado.
Jiri Klouda
Esto es similar al modelo de GitFlow y / o GitHubFlow. No creo que estos modelos faciliten la Integración Continua (CI). En mi opinión, el desarrollo basado en troncales es una mejora significativa en estos modelos.
Yani
Puede ver que este comentario en realidad es anterior al lanzamiento original de git flow. No estoy muy seguro de lo que quiere decir con "mejor". He apoyado a equipos de 1, 5, 25, 150, 1,000 y 20,000 desarrolladores que trabajan en proyectos que estaban integrados hasta cierto punto. Los requisitos varían y "mejor" es un término muy relativo. ¿Necesitas hacer backport de código alguna vez? Arreglos de seguridad? Si no, entonces tu vida es simple. SaaS es un resultado directo de las restricciones impuestas por el desarrollo basado en troncales. Los indicadores de características son tan complejos como las ramas de características. Excepto que solo se entera de los clientes cuando se rompe una permutación de ellos.
Jiri Klouda
9

Personalmente, me parece mucho más limpio tener un tronco estable y tener ramificaciones. De esa manera, los probadores y similares pueden permanecer en una única "versión" y actualizar desde el tronco para probar cualquier característica que tenga código completo.

Además, si varios desarrolladores están trabajando en diferentes funciones, todos pueden tener sus propias ramas separadas, luego fusionarse con el tronco cuando terminen y enviar una función para que se pruebe sin que el evaluador tenga que cambiar a varias ramas para probar diferentes funciones.

Como beneficio adicional, hay cierto nivel de pruebas de integración que se realizan automáticamente.

Adnan
fuente
Además, ¿todavía se ramifican y etiquetan para cada versión importante? ¿O simplemente etiqueta?
KingNestor
1
Funciona bien con CI siempre que las ramas de funciones se fusionen en el tronco con cierta disciplina para no tener compilaciones rotas. Hago bifurcaciones y etiquetas para cada versión de producción que se utilizará solo para corregir errores. Eso se puede fusionar en el baúl del establo de inmediato.
Adnan
@king Diría que probablemente depende de lo que llames versión principal, pero en cualquier caso puedes etiquetar y bifurcar más tarde cuando lo necesites (según la etiqueta :))
eglasius
5

Creo que cualquiera de las estrategias se puede utilizar con un desarrollo continuo siempre que recuerde uno de los principios clave que cada desarrollador se compromete con el tronco / línea principal todos los días.

http://martinfowler.com/articles/continuousIntegration.html#EveryoneCommitsToTheMainlineEveryDay

EDITAR

He estado leyendo este libro sobre CI y los autores sugieren que la ramificación por lanzamiento es su estrategia de ramificación preferida. Tengo que estar de acuerdo. La ramificación por función no tiene sentido para mí cuando utilizo CI.

Intentaré explicar por qué estoy pensando de esta manera. Digamos que tres desarrolladores toman cada uno una rama para trabajar en una función. Cada función tardará varios días o semanas en completarse. Para garantizar que el equipo se integre continuamente, deben comprometerse con la rama principal al menos una vez al día. Tan pronto como comienzan a hacer esto, pierden el beneficio de crear una rama de características. Sus cambios ya no están separados de todos los cambios de los demás desarrolladores. Siendo ese el caso, ¿por qué molestarse en crear ramas de características en primer lugar?

El uso de la ramificación por lanzamiento requiere mucha menos fusión entre las ramas (siempre es algo bueno), asegura que todos los cambios se integren lo antes posible y (si se hace correctamente) asegura que su base de código esté siempre lista para su lanzamiento. La desventaja de la ramificación por lanzamiento es que debe tener mucho más cuidado con los cambios. Por ejemplo, la refactorización grande debe realizarse de forma incremental y si ya ha integrado una nueva función que no desea en la próxima versión, entonces debe ocultarse utilizando algún tipo de mecanismo de alternancia de funciones .

OTRA EDICIÓN

Hay más de una opinión sobre este tema. Aquí hay una publicación de blog que es una función profesional que se ramifica con CI

http://jamesmckay.net/2011/07/why-does-martin-fowler-not-understand-feature-branches/

Phil Hale
fuente
interesante, ya no puedo encontrar esta publicación.
Jirong Hu
5

Las ramas de lanzamiento son muy útiles, e incluso absolutamente necesarias, si necesita mantener varias versiones de su aplicación.

Las ramas de características también son muy convenientes, especialmente si un desarrollador necesita trabajar en un cambio enorme, mientras que otros aún lanzan nuevas versiones.

Entonces, para mí, usar ambos mecanismos es una muy buena estrategia.

Interesante enlace del Libro de SVN .

SirFabel
fuente
4

Recientemente me ha gustado este modelo al usar git. Aunque su pregunta está etiquetada como "svn", es posible que aún pueda hacer algún uso de ella.

La Integración Continua puede ocurrir hasta cierto punto en la rama de "desarrollo" (o como se llame) en este modelo, aunque tener ramas de características de larga ejecución para futuras versiones no lo haría tan rígido como para considerar cada cambio que ocurre en el código en alguna parte. La pregunta sigue siendo si realmente lo querrías. Martin Fowler lo hace.

hermannloose
fuente
2

La integración continua no debe ser un factor de ningún tipo para determinar su estrategia de ramificación. Su enfoque de ramificación debe seleccionarse en función de su equipo, el sistema en desarrollo y las herramientas disponibles para usted.

Una vez dicho esto ...

  • no hay ninguna razón por la que la CI no se pueda utilizar en los dos enfoques que describe
  • esos enfoques funcionan bastante bien en combinación
  • ninguno de los dos funciona "mejor" que el otro
  • CI tiene mucho sentido con un maletero inestable

Todo esto fue respondido en la cuarta pregunta en la página de la que tomó los diagramas: http://blogs.collab.net/subversion/2007/11/branching-strat/

Zac Thompson
fuente
2

Siempre que comprenda los principios, siempre podrá reinventar las mejores prácticas. Si no comprende los principios, las mejores prácticas lo llevarán tan lejos antes de desmoronarse debido a algún requisito externo conflictivo.

Para obtener la mejor introducción al modelo de línea principal, lea esto: https://web.archive.org/web/20120304070315/http://oreilly.com/catalog/practicalperforce/chapter/ch07.pdf

Lea el enlace. Una vez que tenga lo básico, lea el siguiente artículo del venerable Henrik Kniberg. Le ayudará a relacionar Mainline Model con la integración continua.

http://www.infoq.com/articles/agile-version-control

zvolkov
fuente
Ya no se puede acceder al capítulo de O'Reilly
Jason S
1

Cuando comenzamos nuestro equipo, heredamos una estrategia basada en versiones del proveedor que desarrolló originalmente el sistema del que estábamos a punto de ponernos a cargo. Funcionó hasta el momento en que nuestros clientes solicitaron que varias características desarrolladas no deberían incluirse en una versión (fyi ~ 250k líneas de código, ~ 2500 archivos, Scrum con XP SDLC).

Luego comenzamos a buscar ramas basadas en características. Esto también funcionó durante un tiempo, como 2 meses hasta que nos dimos cuenta de que nuestro proceso de prueba de regresión demoraría más de 2 semanas, lo que combinado con la incertidumbre de lo que se lanzaría creó un gran inconveniente.

El "clavo en el ataúd" final de las estrategias de SC puras llegó cuando decidimos que deberíamos tener 1. tronco estable y 2. La producción debería contener ST, UAT y BINARIOS probados por regresión (no solo fuente, piense en CC).

Esto nos llevó a diseñar una estrategia que es un híbrido entre las estrategias de SC basadas en funciones y versiones.

Entonces tenemos un baúl. En cada sprint, ramificamos la rama de sprint (para las personas no ágiles, un sprint es solo un esfuerzo de desarrollo encuadrado en el tiempo con una salida variable basada en la complejidad). Desde la rama de sprint creamos las ramas de características y el desarrollo paralelo comienza en ellas. Una vez que las características están completas y el sistema probado, y recibimos la intención de implementarlas, se fusionan con la rama del sprint; algunas pueden flotar en varios sprints, generalmente los más complejos. Una vez que el sprint está cerca de su final y las funciones están completas ... "cambiamos el nombre" de la rama del sprint a "regresión" (esto permite que CruiseControl lo recupere sin ninguna reconfiguración) y luego comienzan las pruebas de regresión / integración en el cc-built OÍDO. Cuando todo está hecho, entra en producción.

En resumen, las ramas basadas en características se utilizan para desarrollar, probar el sistema y la funcionalidad UAT. La rama Sprint (en realidad, la rama de lanzamiento) se utiliza para fusionar de forma selectiva características bajo demanda y prueba de integración.

Ahora, aquí hay una pregunta para la comunidad: obviamente, estamos teniendo problemas para realizar una integración continua debido al hecho de que el desarrollo ocurre en muchas sucursales y la sobrecarga de reconfiguración de CruiseControl. ¿Alguien puede sugerir y aconsejar?

XAvatar
fuente
No estoy necesariamente de acuerdo con las conclusiones, pero gracias por la discusión de su proceso. No existe una solución única para todos.
RaoulRubin
0

A mi modo de ver, querrás tener un conjunto limitado de ramas donde puedas concentrarte. Dado que desea pruebas, métricas de calidad del código y muchas cosas interesantes para ejecutar con las compilaciones, tener demasiados informes probablemente hará que se pierda información.

Cuándo y qué bifurcar, generalmente depende del tamaño del equipo y del tamaño de las funciones que se están desarrollando. No creo que exista una regla de oro. Asegúrese de utilizar una estrategia en la que pueda obtener retroalimentación temprano / con frecuencia, y eso incluye involucrar la calidad desde el principio de las funciones. El bit de calidad significa que a medida que se automatiza a medida que se desarrolla el equipo, si se ramifica para un conjunto grande de funciones que está construyendo un equipo, también debe tener calidad involucrada en el equipo.

ps ¿De dónde sacaste esas referencias de enfoque? - no siente que esos gráficos representan todas las opciones

Actualización 1: Ampliando por qué dije que no es una regla de oro. Básicamente, para equipos relativamente pequeños, lo he encontrado mejor usando un enfoque que es una mezcla. Se crean ramas de características si es algo largo y parte del equipo continuará agregando características más pequeñas.

eglasius
fuente
También tiene más. Pero siento que Feature Branching y Release Branching son los 2 más comunes.
KingNestor
0

Dave Farley , autor de Continuous Delivery , se refirió al Trunk Based Development (TBD) como la piedra angular de la Integración continua (CI) y la Entrega continua (CD). Él dice:

Cualquier forma de ramificación es la antítesis de la integración continua.

También dice,

La ramificación de funciones es muy agradable desde la perspectiva de un desarrollador individual, pero subóptima desde la perspectiva de un equipo. A todos nos gustaría poder ignorar lo que están haciendo los demás y seguir con nuestro trabajo. Desafortunadamente, el código no es así. Incluso en bases de código muy bien factorizadas con una hermosa separación de preocupaciones y componentes maravillosamente acoplados libremente, algunos cambios afectan otras partes del sistema.

El Desarrollo Basado en Troncales (TBD) es la práctica de integrar cambios de código en el tronco (también conocido como principal, línea principal) al menos una vez al día, preferiblemente varias veces al día. La Integración Continua (CI) es una práctica similar excepto que también implica verificar los cambios de código mediante pruebas automatizadas. La mejor estrategia de bifurcación para esto es trabajar directamente desde el tronco y realizar revisiones de código a través de la programación por pares . Si por alguna razón no puede emparejarse, o simplemente quiere ramificar, asegúrese de que sus ramificaciones sean de corta duración (menos de un día).

Trabajo en Trunk, "maestro" en mis repositorios GIT. Me comprometo a dominar localmente y enviar inmediatamente, cuando estoy conectado a la red, a mi repositorio principal central donde se ejecuta CI. ¡Eso es!

Para funciones grandes (es decir, aquellas que toman más de un día), intente dividirlas en pequeños trozos de lógica que puedan integrarse en el tronco sin romper el software. También puede utilizar técnicas como el marcado de características y la ramificación por abstracción que le permiten implementar el trabajo incompleto sin afectar a los usuarios finales.

Utilizo la rama por abstracción, la liberación de oscuridad y, a veces, las banderas de características. Lo que obtengo a cambio es una retroalimentación rápida y definitiva (al menos para la calidad de mis pruebas).

Yani
fuente
Dave Farley y Jez Humble simplemente se equivocan en su postura sobre la ramificación. La razón de esto es que codifica una suposición importante "nunca tendrá que manipular el código a nivel de función y si, entonces está bien que sea una operación costosa" y basan su evaluación en otra suposición "fusionar es demasiado costoso con automatizado se fusiona siendo casi imposible a escala ". Si esas dos suposiciones no son ciertas, si vive en un mundo donde la fusión es barata, pero necesita manipular el código a nivel de función para puertos posteriores y correcciones de seguridad, entonces sus declaraciones se descomponen. Aunque es un caso raro.
Jiri Klouda
Algunas empresas también necesitan hacer avanzar las funciones a versiones futuras, después de que esas funciones encuentren obstáculos en la implementación y estén retrasando una versión. A veces existe la opción de dejar el código, como en los productos SaaS, pero si el código se entrega a los clientes, es posible que no sea una opción, ya que la competencia puede analizarlo. Mucho código en estos días no está compilado e incluso si lo está, los indicadores de definición / característica en el código están en el mismo nivel de complejidad que las ramas.
Jiri Klouda
-3

Creo que las herramientas que usas son un factor importante aquí.

  • Si está utilizando subversión, siga con la opción 1 y libere de las ramas.
  • Si está utilizando GIT, la opción 2 funcionará bien para usted.
Tony Zampogna
fuente
2
La ramificación de
funciones