¿Cómo refactorizar cuando todo su desarrollo está en ramas?

24

En mi empresa, todo nuestro desarrollo (corrección de errores y nuevas características) se realiza en sucursales separadas. Cuando está completo, lo enviamos a QA que lo prueba en esa rama, y ​​cuando nos dan luz verde, lo fusionamos en nuestra rama principal. Esto podría tomar entre un día y un año.

Si tratamos de exprimir cualquier refactorización en una rama, no sabemos por cuánto tiempo estará "fuera", por lo que puede causar muchos conflictos cuando se fusiona nuevamente.

Por ejemplo, supongamos que quiero cambiar el nombre de una función porque la función en la que estoy trabajando está haciendo un uso intensivo de esta función, y descubrí que su nombre realmente no se ajusta a su propósito (de nuevo, esto es solo un ejemplo). Así que voy y encuentro cada uso de esta función, y les cambio el nombre a todos por su nuevo nombre, y todo funciona perfectamente, así que lo envío a QA.

Mientras tanto, está ocurriendo un nuevo desarrollo, y mi función renombrada no existe en ninguna de las ramas que se están bifurcando en main. Cuando mi problema vuelva a fusionarse, todos se romperán.

¿Hay alguna forma de lidiar con esto?

No es que la gerencia apruebe un problema de refactorización, por lo que debe ser aplicado con otro trabajo. No se puede desarrollar directamente en main porque todos los cambios tienen que pasar por QA y nadie quiere ser el imbécil que rompió main para poder hacer un poco de refactorización no esencial.

mpen
fuente
¿Qué control de versión estás usando? Existen diferentes enfoques para DVCS y un modelo de servidor centralizado. Además, ¿de qué se están quitando las ramas de desarrollo? Si se acepta una rama de características, ¿cómo otras ramas de desarrollo recogen los cambios?
2
Por otro lado, un diagrama de la estructura de ramificación actual podría ser realmente útil. Es muy posible que la raíz del problema con la dificultad con la refactorización sea en parte causada por algunas ... políticas de ramificación no convencionales (vea programmers.stackexchange.com/questions/210360 para un ejemplo de este tipo). También sugeriría leer vance.com/steve/perforce/Branching_Strategies.html para obtener algunas ideas y antecedentes (si puedo responder a esta pregunta, será un punto de referencia importante).
1
El último párrafo lo resume: si la empresa no percibe el valor, no hay forma de que un refactor importante pueda seguir adelante. Debe trabajar con su equipo de prueba para resolver sus plazos. (Sospecho que el control de calidad es realmente la prueba de arrastre (Ponen una peluca y lápiz labial en y pretender ser algo que no son) Un equipo de control de calidad real sería que le dice qué refactor, no en su camino..)
mattnz
1
@mattnz: Tienes toda la razón. No son un verdadero equipo de control de calidad. En su mayoría son atención al cliente. Creo que muchas de sus responsabilidades deberían transferirse nuevamente al equipo de desarrollo porque simplemente no pueden manejar todo lo que les dejamos, pero ese es un problema de gestión y una batalla que aún tengo que ganar.
mpen
3
Te perdiste mi excavación. Prueba! = QA. El control de calidad supervisa la calidad y tiene como objetivo mejorar los resultados comerciales. Prueba intenta demostrar la ausencia de defectos al encontrarlos.
mattnz

Respuestas:

12

Hay varios problemas que se mezclan para hacer que la refactorización sea un desafío en este entorno. Mezclado con esto hay algunos problemas no técnicos ("pero eso es un problema de gestión y una batalla que aún no he ganado").

El primer problema a tener en cuenta es la rama de larga ejecución. Estas ramas tienen dificultades para rastrear los cambios fuera de la vista del desarrollador. Para abordar esto:

  • Cuando el código esté completo, revísela (deje que el servicio de atención al cliente lo vea si lo desea), pero combínelo rápidamente para que se desarrolle de modo que otros cambios que dependan de él puedan detectarse y los conflictos que se identifiquen temprano en el proceso.
  • Si, por alguna razón, un brach se vuelve de larga duración mientras se realiza la refactorización, tiende a ser una buena práctica fusionarse de estable en la rama para recoger los cambios y la refactorización. A menudo, esto minimiza los conflictos y las sorpresas al fusionarse desde la rama de características a la rama estable.
  • Todas las pruebas de integración deben realizarse en versiones , no en características . En este entorno, las características pueden o no estar completamente integradas con el sistema. Si bien es posible hacer una verificación de la cordura de la función de forma aislada, no identifica problemas en el lanzamiento.
  • Desde el momento en que se completa el código hasta que se fusiona (llamémoslo desarrollo: la ramificación desde maestro / estable / lanzamiento tiene sus propios problemas de no detectar los últimos cambios de desarrollo) no debería ser demasiado largo. Cuanto más espere, más conocimiento se perderá y más difícil será que el código se integre con otras líneas de código.

Otro problema que se está mezclando con esto es que aludí con los puntos anteriores es el papel cambiante de la rama con el tiempo. Comienza como una rama de desarrollo donde los desarrolladores se comprometen, y luego se convierte en un área de prueba (¿qué pruebas se están haciendo aquí que pueden ser significativas en toda la aplicación?), Que luego se fusiona en estable (y presumiblemente lanzado) probado de nuevo?).

Con una función más corta de inicio a fin, es más fácil que la refactorización pueda ser recogida por otras ramas.

Anime a los desarrolladores a obtener todo el entorno. Solo los cambios de selección de cereza pueden conducir a ... digamos entornos de desarrollo interesantes. Si bien la selección de cerezas tiene sus usos, para que ese sea el modo predeterminado de llevar los cambios a una rama puede ser preocupante.

La refactorización es algo que idealmente se hace constantemente, o si no constantemente, siempre que haya un mínimo de tiempo de inactividad. Branch, realice una refactorización simple, ejecute las pruebas unitarias para verificar que todo sigue funcionando (su unidad ha sido probada, ¿ verdad? ) Y luego vuelva a fusionarse en estable. Pase la información para que otros desarrolladores puedan extraer esos cambios que refactorizó en sus propias sucursales.

Es importante que los desarrolladores posean la calidad del código. Si bien la dirección de las funciones proviene del exterior, y las asignaciones de tiempo a menudo no son nuestras, la calidad del código es algo de lo que es necesario enorgullecerse y dedicar tiempo.

Puede encontrar las siguientes preguntas útiles en la búsqueda de asignar tiempo para lidiar con la deuda técnica:

También es posible que desee ver herramientas como el sonar que pueden ayudar a identificar las áreas del código que necesitan más trabajo para la refactorización. El complemento de deuda técnica es algo que puede usarse para ayudar a señalar la acumulación de deuda con el tiempo en la base del código.

A menudo es necesario señalar que el ROI para tratar con la deuda técnica es un tiempo de respuesta más rápido para las funciones y las correcciones de errores del equipo de desarrollo.

Comunidad
fuente
Las pruebas se realizan esencialmente en tres puntos en el tiempo. Una vez, cuando se reclama el problema solucionado (para asegurarse de que cumple con todos los requisitos y no hay problemas importantes), nuevamente cuando se fusiona nuevamente con el valor predeterminado (prueba de integración), y nuevamente cuando hacemos una compilación (integración con todos los cerezas seleccionados problemas / revisión final). Creo que la selección de cerezas es necesaria en nuestro entorno, ya que operamos un SaaS con clientes muy particulares. Echaré un vistazo a estos enlaces, ¡gracias por los consejos! Editar: En realidad, hay una revisión más de la producción para asegurarse de que funcionó bien.
mpen
3

Por lo general, estoy desarrollando una versión refactorizada en "paralelo" con la actual, es decir, en la misma base de código, pero no haciendo referencia a ella desde la aplicación principal. Y cuando se hace y se prueba una nueva solución, comienzo a refactorizar.

Ejemplo 1. Supongamos que tengo Thing, que sea función, interfaz, módulo o lo que sea. Y quiero refactorizarlo. Estoy creando Thing2 en la misma base de código, es una versión refactorizada de Thing. Cuando está hecho y probado, estoy refactorizando todo lo que hace referencia a Thing, para reemplazarlo con Thing2. Por lo general, este paso lleva una cantidad de tiempo relativamente pequeña.

Si la refactorización real toma demasiado tiempo para mantenerse sincronizado sin molestar al equipo, estoy tomando todas las características relevantes y refactorizándolas en paralelo también.

Ejemplo 2. Tengo un nuevo backend de representación, que es una versión refactorizada de la anterior. Pero no es compatible con la interfaz de renderizado anterior. Por lo tanto, necesito refactorizar la interfaz. Y de nuevo: en la misma base de código. Cuando todo esté hecho, solo estoy cambiando la clase de instancia frontend, idealmente tomará una breve confirmación.

Sí, recursivamente uno puede llegar a la conclusión de que todo debe hacerse en paralelo. Pero esto generalmente ocurre cuando hay demasiado acoplamiento en la base de código, o está cambiando demasiado rápido.

Finalmente, cuando el nuevo código se integra y funciona bien, las características antiguas pueden eliminarse de la base de código, y las nuevas características pueden renombrarse para obtener nombres antiguos.

En general, la idea es preparar nuevas funciones en paralelo y cambiar a usarlas en un pequeño paso.

John Carmack utiliza este enfoque (o al menos similar), tal vez su publicación de blog lo explica mejor: (enlace)

Sombras en la lluvia
fuente
Este es un buen enfoque. Estoy tratando de recordar lo que realmente provocó esta pregunta ahora ... No creo que fuera algo muy susceptible a la paralelización. O si lo fuera, creo que mi preocupación es que este enfoque causa mucha fragmentación en la base de código. Tenemos "viejas formas" de hacer las cosas y "nuevas formas" de hacer las cosas, y las cosas viejas se están reemplazando a un ritmo glacial, pero está causando dolor de cabeza a los desarrolladores porque ahora esencialmente tienen que conocer dos (o más) sistemas.
mpen
1

Puede parecer una dificultad en el lado técnico cuando en realidad está en el lado de los requisitos.

Donde el desarrollo está orientado hacia diferentes requisitos en diferentes ramas es la verdadera dificultad. Los gerentes y arquitectos del equipo deben tomar decisiones que permitan que las diferentes necesidades comerciales coexistan.

El proceso ZBB y los "compromisos" de Co-Dev cuando se realizan después de tomar las decisiones correctas con los aportes relevantes de todos los desarrolladores deberían permitirle implementar lo que necesita sin tener que pensar: ¿Cómo fusionaré mi código?

ZBB significa presupuesto basado en cero . Al decir Co-Dev me refería a pocas personas que trabajan en programación paralela.

Yosi Dahari
fuente
2
¿Qué son "ZBB" y "Co-Dev"?
mosquito
ZBB - en.wikipedia.org/wiki/Zero-based_budgeting . Al decir Co-Dev me refería a pocas personas que trabajan en programación paralela.
Yosi Dahari
1

El problema me parece que estás trabajando demasiado tiempo en las ramas. El costo de los conflictos aumenta exponencialmente con el tiempo que todos permanecen en una rama, por lo que con conflictos muy largos tiene pocas posibilidades de refactorizar.

gnasher729
fuente
0

Su problema es el modelo de sucursal que está utilizando. Puede desarrollarse en una rama, y ​​cuando se completa y está lista para el control de calidad, la rama se fusiona con un 'tronco intermedio', a veces llamado integración o prueba. Cuando desarrolle la siguiente característica, puede ramificarse desde este tronco intermedio en su lugar.

Este modelo le permite desarrollar múltiples funciones en paralelo en diferentes ramas, fusionándolas todas en la rama de integración para enviar a QA, y también mantener una única troncal de lanzamientos (fusiona la QA de base de código recibida en la troncal principal cuando la certifican )

Sin embargo, está asumiendo que los cambios entregados a QA se aprobarán sin modificaciones importantes: si el código de QA regresa con instrucciones para eliminar la mitad de los cambios, tendrá que revertirlo, pero si eso no sucede, se realizará Su desarrollo mucho más suave. Entonces, básicamente, está tomando ramas para nuevas características de cuál será su código de línea principal (es decir, troncal después de fusionar el código pasado al control de calidad), en lugar de lo que es hoy (es decir, troncal actual) y, por lo tanto, ya no se desarrolla contra la base de código de la versión anterior .

gbjbaanb
fuente