Monorepositorios: por favor, es necesario

Monorepositorios: por favor, es necesario

La traducción del artículo se ha preparado para los estudiantes del curso «Prácticas y herramientas de DevOps» en el proyecto educativo OTUS.

Deberías optar por un monorepo, porque el comportamiento que promueve dentro de tus equipos es la transparencia y la responsabilidad colectiva, especialmente a medida que los equipos crecen. En cualquier caso, tendrás que invertir en herramientas, pero siempre es mejor cuando el comportamiento predeterminado es el que deseas ver en tus equipos.

¿Por qué hablamos de esto?

Matt Klein escribió un artículo «Monorepos: ¡Por favor, no lo hagan!»  (nota del traductor: traducción en Habr «Monorepos: por favor no lo hagan»). Me gusta Matt, creo que es muy inteligente, y deberías leer su perspectiva. Inicialmente publicó una encuesta en Twitter:

Monorepositorios: por favor, es necesario

Traducción:
En este día de Año Nuevo, discutiré cuán absurdo es el uso de monorepos. El 2019 comenzó de manera bastante silenciosa. En ese espíritu, te propongo una encuesta. ¿Quiénes son los grandes fanáticos? Favorables:
— Monorepo
— Rust
— Encuesta errónea / ambos

Mi respuesta fue: «Literalmente soy ambas personas». En lugar de hablar sobre qué tan adictivo es Rust, exploremos por qué creo que está equivocado acerca de los monorepos. Un poco sobre mí. Soy el director técnico de Chef Software. Tenemos alrededor de 100 ingenieros, una base de código que tiene entre 11 y 12 años, y 4 productos principales. Parte de este código se encuentra en un polirepo (mi posición inicial), y parte en un monorepo (mi posición actual).

Antes de comenzar: cada argumento que presento aquí se aplicará a ambos tipos de repositorios. En mi opinión, no hay razones técnicas por las cuales debas elegir un tipo de repositorio sobre el otro. Puedes hacer que cualquier enfoque funcione. Estoy feliz de hablar sobre esto, pero no me interesan razones técnicas artificiales que sugieran que uno es superior al otro.

Estoy de acuerdo con la primera parte de la perspectiva de Matt:

Porque a gran escala, un monorepo afrontará los mismos problemas que resuelve un polirepo, pero además te provocará a una fuerte cohesión de tu código y requerirá esfuerzos extraordinarios para escalar tu sistema de control de versiones.

Te enfrentas a los mismos problemas sin importar si eliges un monorepositorio o un polirepositorio. ¿Cómo lanzas tus versiones? ¿Cuál es tu enfoque hacia las actualizaciones? ¿Compatibilidad retroactiva? ¿Dependencias cruzadas entre proyectos? ¿Qué estilos arquitectónicos son aceptables? ¿Cómo gestionas tu infraestructura de construcción y pruebas? La lista es interminable. Y los resolverás a medida que crezcas. No hay tal cosa como el queso gratis.

Creo que el argumento de Matt se asemeja a las opiniones compartidas por muchos ingenieros (y gerentes) que respeto. Proviene de la perspectiva de un ingeniero que trabaja en un componente, o de un equipo que trabaja en un componente. Escuchas cosas como:

  • La base de código es voluminosa; no necesito toda esta basura.
  • Es más complicado de probar porque tengo que verificar toda esta chatarra que no necesito.
  • Es más difícil trabajar con dependencias externas.
  • Necesito mis propios sistemas virtuales de control de versiones.

Sin duda, todos estos puntos son válidos. Esto ocurre en ambos casos: en el polirepositorio tengo mi propio desorden, además del que necesito para la construcción... Puede que necesite más desorden. Por eso 'simplemente' creo herramientas que realizan el checkout de todo el proyecto. O creo un monorepositorio falso con submódulos. Podríamos estar discutiendo sobre esto todo el día. Pero creo que el argumento de Matt omite la razón fundamental que he resaltado a favor del monorepositorio:

Fomenta la comunicación y revela problemas.

Cuando separamos los repositorios, de facto creamos un problema de coordinación y transparencia. Esto se alinea con cómo pensamos sobre los equipos (especialmente cómo piensan sus miembros individuales): somos responsables de un componente específico. Trabajamos en relativa aislamiento. Los límites se fijan en mi equipo y en el/los componente(s) en los que estamos trabajando.

A medida que la arquitectura se complica, un solo equipo ya no puede gestionarla por sí solo. Muy pocos ingenieros mantienen todo el sistema en su cabeza. Supongamos que gestionas un componente común A, que es utilizado por los equipos B, C y D. El equipo A realiza una refactorización, mejora la API y también cambia la implementación interna. Como resultado, los cambios son incompatible hacia atrás. ¿Qué consejo darías?

  • Encuentra todos los lugares donde se usa la antigua API.
  • ¿Hay lugares donde no se puede usar la nueva API?
  • ¿Puedes corregir y probar otros componentes para asegurarte de que no se rompan?
  • ¿Pueden estos equipos verificar tus cambios ahora mismo?

Ten en cuenta que estas preguntas no dependen del tipo de repositorio. Tendrás que encontrar a los equipos B, C y D. Necesitarás hablar con ellos, averiguar su tiempo, comprender sus prioridades. Al menos, esperamos que lo hagas.

De hecho, a nadie le gusta hacer esto. Es mucho menos emocionante que simplemente arreglar maldita sea la API. Todo esto es humano y complicado. En un polirepositorio, puedes simplemente realizar cambios, someterlo a revisión a quienes trabajan en ese componente (probablemente no a B, C o D), y seguir adelante. Los equipos B, C y D pueden mantenerse en su versión actual por ahora. Actualizarán cuando se den cuenta de tu genialidad.

En un monorepositorio, la responsabilidad se desplaza por defecto. El equipo A cambia su componente y, si no tiene cuidado, inmediatamente rompe B, C y D. Esto lleva a que B, C y D aparezcan en la puerta de A, preguntándose por qué el equipo A rompió la compilación. Esto enseña a A que no pueden ignorar mi lista anterior. Deben hablar sobre lo que planean hacer. ¿Pueden B, C y D avanzar? ¿Qué pasaría si B y C pueden, pero D estaba estrechamente relacionado con un efecto secundario del comportamiento del antiguo algoritmo?

Entonces, debemos hablar sobre cómo saldremos de esta situación:

  1. Soporte para múltiples API internas, mientras que el antiguo algoritmo se marcará como obsoleto, hasta que D pueda dejar de usarlo.
  2. Soporte para múltiples versiones de lanzamientos, una con la antigua interfaz, otra con la nueva.
  3. Retrasar el lanzamiento de los cambios A hasta que simultáneamente B, C y D puedan aceptarlo.

Supongamos que hemos seleccionado 1, varias API. En este caso, tenemos dos fragmentos de código. Uno antiguo y uno nuevo. Es bastante conveniente en ciertas situaciones. Revertimos el código antiguo, lo marcamos como obsoleto (deprecated) y coordinamos el calendario para su eliminación con el equipo D. Es esencialmente idéntico para el polirepositorio y el monorepositorio.

Para lanzar varias versiones necesitamos una rama. Ahora tenemos dos componentes: A1 y A2. Los equipos B y C utilizan A2, mientras que D utiliza A1. Necesitamos que cada componente esté listo para su lanzamiento porque, antes de que D pueda avanzar, pueden requerirse actualizaciones de seguridad y correcciones de otros errores. En el polirepositorio, podemos ocultar esto en una rama de larga duración que se siente bien. En el monorepositorio, forzamos el código en un nuevo módulo. El equipo D aún tendrá que hacer cambios en el componente "antiguo". Todos pueden ver el costo que estamos pagando aquí: ahora tenemos el doble de código, y cualquier corrección de errores que se aplique a A1 y A2 debe aplicarse a ambos. Con el enfoque de uso de ramas en el polirepositorio, esto se oculta detrás de cherry-pick. Consideramos el costo como menor porque no hay duplicación. Desde un punto de vista práctico, el costo es el mismo: estarás creando, lanzando y manteniendo dos bases de código, en su mayoría idénticas, hasta que puedas eliminar una de ellas. La diferencia es que con el monorepositorio, ese dolor es directo y está a la vista. Esto es aún peor, y eso es bueno.

Finalmente, hemos llegado al tercer punto. Retraso en el lanzamiento. Es posible que los cambios realizados por A mejoren la situación del equipo A. Importante, pero no urgente. ¿Podemos simplemente retrasarlo? En el monorepositorio, estamos impulsando esto hacia la consolidación del artefacto. Por supuesto, estamos comunicándolo al equipo D. ¡Simplemente quédense en la versión anterior hasta que se pongan al día! Esto establece un ambiente de temor. El equipo A continúa trabajando en su componente, ignorando el hecho de que el equipo D está usando una versión cada vez más obsoleta (ese es un problema del equipo D, son incompetentes). Mientras tanto, el equipo D habla mal de la actitud descuidada del equipo A hacia la estabilidad del código, si es que hablan de ello. Pasan los meses. Finalmente, el equipo D decide considerar la posibilidad de actualizar, pero los cambios en A solo han aumentado. El equipo A apenas recuerda cuándo y cómo dañaron a D. La actualización resulta ser más dolorosa y tomará más tiempo. Lo que lo envía aún más abajo en la pila de prioridades. Hasta que un día enfrentemos un problema de seguridad en A, lo que nos obliga a hacer una bifurcación. El equipo A tiene que retroceder en el tiempo, encontrar el momento en que D era estable, arreglar allí el problema y prepararlo para el lanzamiento. Es, de facto, una decisión que toman las personas, y sin duda, es la peor. Parece que esto es bueno tanto para el equipo A como para D, siempre que podamos ignorarnos mutuamente.

En un monorepositorio, el tercero realmente no es una opción. Te ves obligado a manejar la situación de una de dos maneras. Necesitas comprender los costos de tener dos ramas de lanzamiento. Aprender a protegerte de las actualizaciones que rompen la compatibilidad hacia atrás. Pero lo más importante: no puedes evitar una conversación difícil.

Por mi experiencia, cuando los equipos crecen, ya no hay forma de mantener toda la sistema en la mente, y esa es la parte más importante. Debes mejorar la visibilidad de las discrepancias en el sistema. Debes trabajar activamente para hacer que los equipos aparten la vista de sus componentes y miren el trabajo de otros equipos y consumidores.

Sí, puedes crear herramientas que intenten resolver el problema de los polirepositorios. Pero mi experiencia en entrega continua y automatización en grandes empresas me dice lo siguiente: el comportamiento por defecto sin el uso de herramientas adicionales es el comportamiento que esperas ver. El comportamiento por defecto de un polirepositorio es la aislamiento, ese es todo el sentido. El comportamiento por defecto de un monorepositorio es la responsabilidad compartida y la transparencia, ese es todo el sentido. En ambos casos, voy a crear una herramienta que permita suavizar los bordes. Como líder, elegiré un monorepositorio cada vez, porque las herramientas deben fortalecer la cultura que deseo, y la cultura proviene de pequeñas decisiones y del trabajo diario del equipo.

Solo los usuarios registrados pueden participar en la encuesta. Inicie sesión, por favor.

¿Quiénes son los mayores fanáticos? Defensores:

  • Monorepo

  • Rust

  • Encuesta errónea / ambos

33 usuarios votaron. 13 usuarios se abstuvieron.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster