Elegir la estrategia de ramificación adecuada para lanzamientos

11

Comenzando con un nuevo equipo de desarrollo en un nuevo proyecto y tenemos que definir nuestra estrategia de ramificación para nuestro repositorio de origen ( por ejemplo, Microsoft Team Foundation Server 2010 ). Nos hemos topado con una discusión difícil sobre si debemos o no ...

Una . Tener una rama de lanzamiento desde la que hacemos compilaciones de producción y luego etiquetar cuando algo se lanza realmente

O

B . Tener una nueva rama de lanzamiento para cada nuevo lanzamiento en producción ( Ej. Versión 1, 2, 3, etc. )

La opción A parece bastante sencilla, pero no estamos seguros de si esto conducirá a problemas a largo plazo. Parece que la Opción B simplemente crea muchas ramas de larga duración que se acumulan una sola vez con el tiempo.

¿Alguien tiene alguna experiencia que nos pueda ayudar a decidir? Específicamente, estoy buscando saber dónde están los puntos débiles para cualquier opción. Siéntase libre de proporcionar experiencia específica en relación con las implicaciones de TFS y / o gestión de versiones.

JoeGeeky
fuente

Respuestas:

15

Opción A. Simplemente usando la línea principal y etiquetado para el lanzamiento

Pros:

  • Evitas fusionar el infierno.
  • Mantenerse en la línea principal fomenta algunas de las mejores prácticas, como la planificación adecuada de la liberación, no introducir una gran cantidad de WIP, usar la ramificación por abstracción para tratar el trabajo a largo plazo fuera de banda, y usar el sistema cerrado abierto y las características configurables para manejar la gestión de trabajos. en progreso que puede; o no puede; es necesario deshabilitarlo ahora o en el futuro para liberarlo o evitar una reversión completa.

Contras:

  • Tratar con trabajos en progreso se convierte en un problema y se agrega al área potencial de ataque de superficie cuando llega el momento de liberar. Sin embargo, si sus desarrolladores son disciplinados, las nuevas características deben ser configurables y modulares y, por lo tanto, fácilmente deshabilitadas / habilitadas, o no hay WIP y en cada punto de lanzamiento todo el trabajo se completa o aún no se ha iniciado (es decir, Scrum).
  • Los cambios a gran escala / fuera de banda requieren más tiempo de anticipación para su implementación (por ejemplo, ramificación por abstracción).

Personalmente prefiero este enfoque. La cobertura del código y las pruebas unitarias deben identificar el código que no está listo para salir y las personas no deberían estar trabajando en el código que no se lanzará durante la iteración actual. La ramificación por abstracción u otros mecanismos se pueden utilizar para hacer frente a cambios a largo plazo y trabajos en progreso.

Cuando no hace esto, comienza a encontrarse con problemas de fusión, código obsoleto, características que nunca se publican, etc.

Opción B. Sucursal por liberación

Pros:

  • Puede comenzar a trabajar en la próxima iteración mientras la iteración actual finaliza su ronda de pruebas de aceptación.
  • Otras cosas estoy seguro.

Contras:

  • Toneladas de ramas.
  • Todavía necesita etiquetar ramas en los puntos de liberación
  • Todavía necesita lidiar con WIP y fusionar WIP de la rama de la versión anterior a la siguiente rama de la versión si no va a lograrlo y todavía necesita deshabilitarlo o tirarlo de la rama de la versión y volver a ejecutar las pruebas de aceptación
  • Las correcciones urgentes deben aplicarse a más ramas (liberar rama + revisión + nueva etiqueta + fusionar revisión en rama vnext y posiblemente vnextnext dependiendo de dónde caiga la revisión).

No soy un gran admirador de esta solución ^ _ ^.

En general, recomendaría simplemente tratar de seguir la línea principal. Si sus desarrolladores tienen problemas para no escribir WIP que se pueden eliminar fácilmente cuando falla el corte o que se verifica temprano para la próxima versión, entonces puede comenzar a hablar sobre etiquetar el código en el punto donde debería estar completo y bifurcarse a partir de ahí, si es necesario, para abordar defectos y errores pasados ​​por alto que las pruebas de la unidad de desarrolladores no pudieron detectar.

Idealmente, creo que quieres que sea el proceso de excepción, no la regla.

Opción C. Opción de bono loco

Si quieres ponerte elegante, también puedes considerar un modelo de ramificación por historia de usuario / por característica. ( Una idea terrible en TFS o en cualquier DVCS mientras que al mismo tiempo es increíblemente trivial de implementar si se usa un DVCS como git o mercurial ).

En el pasado, implementé lo siguiente para un equipo de mantenimiento de empleadores anterior que trabajó con una base de código heredada que no podía ser fácilmente transferida a mercurial desde svn. Se requirió mucho trabajo innecesario para cumplir con un requisito comercial de una línea principal siempre liberable en lugar de simplemente coordinar mejor los lanzamientos. . .

  1. Las características fueron desarrolladas por desarrolladores en la rama de desarrollo de sus equipos.
  2. Cuando una característica está lista para ser revisada por pares, los desarrolladores la agrupan en una sola fusión de la rama Dev en la rama CR e incluyen el id de la característica / historia del usuario en el título. * Aplicado por gancho previo al compromiso *
  3. Después de pasar CR, se utiliza una herramienta de administración para promover la función en la rama de QA. (Escribí una pequeña aplicación de terminal que enumeraba las historias de usuarios presentes en las diversas etapas de aceptación y permitía al operador promoverla o degradarla entre esas etapas de aceptación)
  4. QA ejecuta pruebas de automatización y usabilidad manual. Si la característica es buena, se inserta en la rama de lanzamiento (línea principal). Si la función se rechaza, se degrada / se retira de la rama de control de calidad hasta que los desarrolladores puedan abordar los problemas que surgieron durante la prueba y agregar enviar un parche a la rama CR.
  5. Si se revierte el código de la rama QA y se aplica una solución, la herramienta de terminal volverá a aplicar los cambios necesarios para que la característica vuelva a la rama QA desde la rama CR para que QA pueda volver a revisar el código y promoverlo o degradarlo de nuevo.
  6. En cualquier momento, la rama de liberación debe estar en un estado liberable estable.
  7. Después de un lanzamiento, los nuevos Dev, QA y CR se hacen girar desde la línea principal.
Keith trae
fuente
@Keith_Brings Este es un resumen muy agradable, gracias. Como ya indicó, la opción C no es realmente una opción ya que estoy usando TFS, pero no obstante es interesante.
JoeGeeky
No veo cómo puede funcionar la opción A. En mi empresa, tenemos diferentes versiones para diferentes clientes. Si todavía estamos desarrollando funciones / revisiones en la versión 1.0, y también estamos trabajando activamente en la versión 2.0, y tal vez incluso en la 3.0, no podemos hacer todo esto en una sola rama. Quizás tenga el lujo de disfrutar de la Opción A debido a su modelo de lanzamiento. Pero eso no es modelo de liberación de todos, y para aquellos de nosotros pegado con exceso de prestaciones o varias versiones paralelas, tenemos que utilizar la opción B.
void.pointer
6

Tenemos sucursales separadas para cada versión que publicamos (aproximadamente 4 al año). Es muy conveniente cuando necesita extraer una versión específica.

Si necesita mantener un par de versiones anteriores, no creo que el etiquetado sirva. Con ramas de lanzamiento específicas, puede aplicar correcciones urgentes a cada rama por separado (o a una selección de ellas) sin preocuparse por ninguna de las otras versiones.

También hace que la comparación de versiones sea mucho más fácil cuando estás buscando cuando se introdujo un error o una característica.

No se preocupe por la cantidad de sucursales o por el tiempo que pasan sin cambios. Su sistema de versiones es para darle control y proporcionar un historial del desarrollo de su proyecto. La historia tiene una tendencia a no cambiar ... Y no te preocupes por que tus cvs no puedan hacer frente. Utilizamos Perforce, más de 9000 archivos en una rama de desarrollo, hasta 50 ramas de desarrollo para las versiones en las que estamos trabajando y, como ya se dijo, una sola rama por versión que publicamos. Perforce ni siquiera respira más fuerte.

En resumen: haga su vida como desarrollador / mantenedor / solucionador de errores / cazador de problemas más fácil y no se preocupe por la cantidad de ramas o la cantidad de archivos. Cualquier cvs respetuoso hará frente.

Editar:

No sufrimos ninguna confusión con respecto al número de sucursales que tenemos. Nuestro esquema de nombres para las sucursales de lanzamiento y nuestra política de sucursal 1 número 1 para las sucursales de desarrollo (o trabajo) pueden tener algo que ver con eso.

Las ramas de lanzamiento reciben el nombre de la versión que tienen, es decir: R2011SP1 para el Service Pack 1. Las ramas de trabajo tienen nombres menos inteligentes: sub01, sub02, sub03, etc. El "sub" proviene del hecho de que todas las ramas de trabajo son sub rama de la rama de aceptación. La rama de aceptación es aquella donde se recopilan todos los problemas que están listos para ser lanzados.

Nuestra política de sucursal de trabajo de 1 problema 1, combinada con el hecho de que nuestro sistema de seguimiento de problemas ha sido personalizado con un campo de "rama" asegura que siempre sepamos qué problema se desarrolla en cada rama. Cuando se integra un problema en la rama de aceptación, este campo se actualiza. Esto significa que siempre sabemos qué problemas están listos para su lanzamiento (una vez que se realiza la prueba de aceptación). Del mismo modo, actualizamos este campo cuando se crea una rama de lanzamiento y de esta manera siempre podemos rastrear en qué lanzamiento se publica un problema.

Marjan Venema
fuente
1
Creo que puede ramificarse de las etiquetas en TFS. Por lo tanto, debería estar de acuerdo con las correcciones urgentes en las versiones actuales del producto siempre que no haya olvidado la etiqueta.
Keith trae el
@KeithBrings Eso es correcto, acabo de probar eso y de hecho puedes ramificarte desde una etiqueta.
JoeGeeky
@MarjanVenema No estoy tan preocupado por la carga en el sistema como por la confusión que puede causar una gran cantidad de ramas. También estoy un poco preocupado de que los cambios realizados en la pila de ramas de lanzamiento no se fusionen con otras ramas de lanzamiento que deberían obtenerlos, no importa la línea principal. ¿Te has encontrado con este tipo de problemas?
JoeGeeky
@JoeGeeky: no, no hay confusión alguna. Ver actualización de mi respuesta.
Marjan Venema
2

Se trata de contexto: con qué frecuencia publicas y qué contiene una publicación.

Aquí hay un poco de un caso de estudio que tuve con mi antiguo trabajo, usando el método B (lo llamamos rama por propósito ).

Para poner la historia en contexto,

  • Un lanzamiento consistió en nuevas características en nuestro software: nuevos modos de juego, nuevas funcionalidades, nuevas opciones de configuración.
  • El ciclo de lanzamiento fue bastante largo: nuestros clientes eran universidades que se quedarían con un conjunto de características por lo general durante un año.

El desarrollo principal se realizó en el tronco hasta que alcanzamos un estado de función completa para una determinada versión. En ese momento, crearíamos una rama, por ejemplo, nombre del proyecto-enero de 2012 y realizaríamos pruebas de calidad y correcciones de errores en esa misma rama. Una vez que estuviéramos listos para un lanzamiento público, etiquetaríamos el código en esa rama y lo liberaríamos.

Sin embargo, el desarrollo en el lanzamiento no terminó en esa etiqueta. Inevitablemente, tuvimos clientes que encontraron errores o pequeños problemas con el lanzamiento. Entonces, en ese caso, todo lo que tenemos que hacer es volver a esa rama, parchear el código y crear una nueva versión etiquetada de la rama de enero de 2012 que se lanzará, y fusionar las correcciones nuevamente en el tronco.

En nuestro caso, este enfoque fue favorable porque algunos usuarios prefirieron quedarse con versiones anteriores con un conjunto limitado de características, o simplemente porque el costo de implementar en su infraestructura una versión completamente nueva en lugar de una revisión causó algunos problemas.

Entonces las preguntas que tienes que hacerte son:

  • ¿Con qué frecuencia libero?
  • ¿Mis lanzamientos serán 100% compatibles con versiones anteriores?
  • ¿Mis clientes estarán de acuerdo con la actualización completa para corregir errores?

Si liberas a menudo, entonces quizás no valga la pena tener ramas para cada uno de ellos. Sin embargo, si su ciclo de lanzamiento es bastante largo como mi caso de uso anterior, y esa implementación, la compatibilidad con versiones anteriores y los clientes que se aferran a lanzamientos antiguos pueden ser riesgosos, la opción B ciertamente le ahorrará mucho dolor, hará que las cosas sean mucho más fáciles de soportar sus clientes al costo mínimo de lidiar con el desorden de sucursales.

Bushibitos
fuente
Me gusta cómo llamas esa opción. En este caso, somos nuestros propios clientes ( por así decirlo), por lo que la implementación permanecerá en gran medida bajo nuestro control. También somos una tienda Scrum y esperamos tener ciclos de lanzamiento bastante frecuentes ( por ejemplo, cada 2-4 semanas ). Si bien esperamos admitir actualizaciones continuas, la compatibilidad con versiones anteriores solo será un problema durante el tiempo que sea necesario para implementar las actualizaciones, así que ... minutos tal vez. De ese sonido de eso; en tu experiencia; La opción B puede no ser la mejor opción para mí. Gracias por la información, muy interesante.
JoeGeeky
Ah sí, en ese caso, la opción B suena como desorden con poco retorno. Solo quería resaltar que ambas opciones son viables y tienen cada una sus ventajas. Olvidé mencionar explícitamente: ¿cómo manejas las correcciones de errores? ¿Se colocan exclusivamente en nuevas versiones o están en parches / versiones antiguas parcheadas?
Bushibytes
1

Prefiero la opción A. Desarrollar en las versiones de troncal y rama cuando son estables. Esto limita significativamente el trabajo en la integración de los hotfix aplicados a la versión de producción.

Me contrataron para ayudar a un equipo que intentó la opción B a volver a la normalidad.

Algunas cosas a considerar.

  • Migre las revisiones hacia adelante a través de todas las ramas de código activas. Esto se puede hacer fusionando, parcheando y / o reconstruyendo. Estos deben administrarse por completo para garantizar que se aplique una corrección a todas las versiones apropiadas, luego a la troncal.
  • Considere las ramas de características para permitir el desarrollo de características en forma aislada del flujo de código principal. Estos se recomiendan para cambios experimentales. Siéntase libre de abandonar las ramas de características si la característica no funciona.
  • Etiqueta y rastrea tus puntos de fusión.
  • Ramifica tus comunicados cuando sea necesario. Creo que esto es normalmente cuando la versión está lista para las versiones candidatas de versión. En algunos casos, la introducción de cambios incompatibles en el tronco puede forzar una ramificación temprana. Considere una rama característica.
BillThor
fuente
0

He trabajado durante varios años en un sistema que utiliza algo entre los dos esquemas que usted describe. La clave es que hay un esquema de numeración multinivel en uso. El nivel externo es básicamente la versión API, y se administra en ramas (con combinaciones apropiadas cuando algo necesita ser reparado en varias ramas) y el nivel interno son las versiones exactas, que se administran con etiquetas.

En particular, si sabemos qué versión exacta tiene un cliente, sabemos exactamente de qué fuente se creó el código y podemos hacer un duplicado exacto para que podamos ver exactamente lo que está sucediendo. ¡Esto es muy importante para el apoyo! Sin embargo, el nivel externo de las ramas, las versiones de API que lanzamos actualmente, evolucionan con el tiempo (con el tronco principal del desarrollo obteniendo la mayoría de las nuevas características). Además, cuando hacemos una nueva versión importante de la API, hacemos una nueva rama para soportar eso (para que el troncal siempre pueda estar orientado al desarrollo de núcleo duro) y consideramos si deberíamos finalizar el ciclo de vida del soporte más antiguo actual rama.

Por lo tanto, recomiendo algo que sea realmente una mezcla de A y B ; ambos tienen buenos aspectos, pero ninguno está completo en sí mismo. Usa lo mejor de ambos mundos.

Compañeros de Donal
fuente
0

He usado TFS para implementar efectivamente la opción (B) en el pasado.

La ramificación / fusión es una gran herramienta cuando se hace en piezas pequeñas. La dificultad no radica en hacer una rama (eso es estúpidamente fácil), ni en empujar el trabajo de una semana de vuelta al árbol (generalmente también es fácil) ... es hacer que el sistema de CI detrás de su control de fuente funcione automáticamente para tú.

Porque la ramificación es discutible si el sistema no está creando y ejecutando automáticamente pruebas para su sucursal.

Habíamos personalizado el flujo de trabajo de compilación predeterminado de TFS para reconocer las rutas relativas de los conjuntos de cambios, y establecimos una convención mediante la cual la personalización podría reconocer una nueva rama (en lugar de simplemente una nueva subcarpeta bajo alguna raíz de desarrollo). Fue suave, fácil de bifurcar, fácil de matar una bifurcación, y recibimos comentarios continuos de nuestro sistema para compilaciones y pruebas.

Veo a muchas personas declarando cuán imposibles son estas estrategias bajo TFS, y creo que se debe a la falta de familiaridad con las posibilidades de un motor de compilación basado en XAML. TFS no es solo control de fuente, es una solución completa y debe usarse como tal.

Craig Brunetti
fuente