Mono-repositorios: por favor no (parte 2)

Hola a todos!

Entonces, una nueva porción del holivar prometido sobre monorepositorios. En la primera parte, discutimos la traducción de un artículo de un respetado ingeniero de Lyft (y anteriormente en Twitter) sobre cuáles son las desventajas de los mono-repositorios y por qué nivelan casi todas las ventajas de este enfoque. Personalmente, estoy en gran medida de acuerdo con los argumentos presentados en el artículo original. Pero, como prometí, para poner fin a esta discusión, me gustaría expresar algunos puntos más, en mi opinión, aún más importantes y más prácticos.

Te contaré un poco sobre mí: trabajé en proyectos pequeños y relativamente grandes, utilicé polirrepositorios en un proyecto con más de 100 microservicios (y SLA 99.999%). En este momento estoy comprometido en la traducción de un pequeño mono-repositorio (en realidad no, solo el back-end js + java frontal) de maven a bazel. No funcionó en Google, Facebook, Twitter, es decir No tuve el placer de utilizar un mono-repositorio configurado y ajustado correctamente.

Entonces, para empezar, ¿qué es un monorepository? Los comentarios sobre la traducción del artículo original mostraron que muchos creen que un repositorio mono es cuando los 5 desarrolladores de la compañía trabajan en un repositorio y almacenan el frontend y el backend juntos. Por supuesto, esto no es así. Un mono-repositorio es una forma de almacenar todos los proyectos de la compañía, bibliotecas, herramientas de compilación, complementos IDE, scripts de implementación y todo lo demás en un gran repositorio. Los detalles aquí son trunkbaseddevelopment.com .

¿Cómo se llama el enfoque cuando la empresa es pequeña y simplemente no tiene tantos proyectos, módulos, componentes? Este es también un monorepository, solo uno pequeño.
Naturalmente, el artículo original dice que todos los problemas descritos comienzan a aparecer en cierta escala. Por lo tanto, aquellos que escriben que su repositorio mono de 1.5 excavadoras funciona perfectamente tienen toda la razón.

Entonces, el primer hecho que me gustaría arreglar: un monorepository es un gran comienzo para su nuevo proyecto . Al poner todo el código en un montón, al principio solo obtendrá una ventaja, porque admitir múltiples repositorios sin duda agregará un poco de sobrecarga.

¿Cuál es el problema entonces? Y el problema, como se señaló en el artículo original, comienza en cierta escala. Y lo más importante, no se pierda el momento en que tal escala ya ha llegado.

Por lo tanto, me inclino a afirmar que, en esencia, los problemas que surgen no son los problemas del enfoque de "poner todo su código en un montón", sino que son problemas de simplemente grandes repositorios de código fuente. Es decir suponiendo que utilizó polirrepositorios para diferentes servicios / componentes, y uno de estos servicios se hizo tan grande (de qué tamaño, hablaremos un poco más adelante), lo más probable es que tenga exactamente los mismos problemas, pero también sin las ventajas de los mono-repositorios (si Por supuesto que lo hay).

Entonces, ¿qué tan grande debe ser el repositorio para comenzar a ser considerado problemático?
Definitivamente, hay 2 indicadores de los que depende: la cantidad de código y la cantidad de desarrolladores que trabajan con este código. Si su proyecto tiene terabytes de código, pero 1-2 personas trabajan con él, entonces lo más probable es que casi no noten problemas (bueno, o al menos será más fácil no hacer nada, incluso si lo notan :)

¿Cómo determinar que es hora de pensar en cómo mejorar su repositorio? Por supuesto, este es un indicador subjetivo, lo más probable es que sus desarrolladores comiencen a quejarse de que algo no les conviene. Pero el problema es que puede ser demasiado tarde para cambiar algo. Permítame darle algunas cifras personales: si la clonación de su repositorio lleva más de 10 minutos, si la construcción de un proyecto lleva más de 20-30 minutos, si el número de desarrolladores supera los 50, y así sucesivamente.

Un hecho interesante de la práctica personal:
Trabajé en un monolito bastante grande en un equipo de unos 50 desarrolladores, dividido en varios equipos pequeños. El desarrollo se llevó a cabo en brunches de características, y la fusión se produjo justo antes del congelamiento de características. Una vez pasé 3 días en la fusión de la rama de nuestro equipo después de que otros 6 equipos se congelaran frente a mí.

Ahora repasemos la lista de los problemas que surgen en repositorios grandes (algunos de ellos se mencionaron en el artículo original, otros no).

1) Tiempo de descarga del repositorio


Por un lado, podemos decir que esta es una operación única que el desarrollador realiza durante la configuración inicial de su estación de trabajo. Personalmente, a menudo tengo situaciones en las que quiero clonar un proyecto en una carpeta vecina, profundizar en él y luego eliminarlo. Sin embargo, si la clonación lleva más de 10-20 minutos, esto no será tan conveniente.

Pero además, no olvide que antes de ensamblar el proyecto en el servidor CI, debe clonar el repositorio para cada agente de compilación. Y aquí comienza a descubrir cómo ahorrar este tiempo, porque si cada ensamblaje demora entre 10 y 20 minutos más, y el resultado del ensamblaje aparece entre 10 y 20 minutos más tarde, esto no será adecuado para nadie. Por lo tanto, el repositorio comienza a aparecer en las imágenes de máquinas virtuales desde las que se implementan los agentes, aparecen complejidad adicional y costos adicionales para respaldar esta solución.

2) tiempo de construcción


Este es un punto bastante obvio que se ha discutido muchas veces. De hecho, si tiene muchos códigos fuente, el ensamblaje en cualquier caso llevará un tiempo considerable. Una situación familiar es cuando, después de cambiar una línea de código, tiene que esperar media hora hasta que los cambios se vuelvan a ensamblar y probar. De hecho, solo hay una salida: utilizar un sistema de compilación basado en resultados de almacenamiento en caché y compilaciones incrementales.

No hay muchas opciones aquí: a pesar del hecho de que las características de almacenamiento en caché se agregaron al mismo gradle (desafortunadamente, no las usé en la práctica), no brindan beneficios prácticos debido al hecho de que los sistemas de construcción tradicionales no tienen resultados repetibles (construcciones reproducibles). Es decir Debido a los efectos secundarios de la compilación anterior, de todos modos, en algún momento será necesario llamar a la limpieza de caché (el enfoque estándar de maven clean build ). Por lo tanto, solo queda la opción de usar Bazel / Buck / Pants y otros similares. Por qué esto no es muy bueno, lo discutiremos un poco más tarde.

3) IDE de indexación


Mi proyecto actual está indexado en Intellij IDEA durante 30 a 40 minutos. ¿Qué hay del tuyo? Por supuesto, puede abrir solo una parte del proyecto o excluir todos los módulos innecesarios de la indexación, pero ... El problema es que la reindexación ocurre cada vez que cambia de una rama a otra. Por eso me gusta clonar un proyecto en un directorio vecino. Algunas personas comienzan a almacenar en caché el caché IDE :)
<Imagen de DiCaprio con ojos entrecerrados>

4) Crear registros


¿Qué servidor de CI estás usando? ¿Proporciona una interfaz conveniente para ver y navegar por varios gigabytes de registros de compilación? Lamentablemente el mío no es :(

5) Historia de commits


¿Te gusta ver el historial de confirmaciones? Me encanta, especialmente en una herramienta con una interfaz gráfica (percibo mejor la información visualmente, no me regaña :).
Así es como se ve el historial de confirmaciones en mi repositorio
imagen

Te gusta ¿Es conveniente? ¡Personalmente no!

6) Pruebas rotas


¿Qué sucede si alguien pudo ejecutar pruebas rotas / código no compilado en el maestro? Ciertamente dirá que su CI no le permite hacer esto. ¿Qué pasa con las pruebas inestables que pasa el autor y nadie más? Ahora imagine que este código se extendió a las máquinas de 300 desarrolladores, ¿y ninguno de ellos puede armar un proyecto? ¿Qué hacer en tal situación? ¿Esperar a que el autor se dé cuenta y corrija? ¿Correcto para él? ¿Revertir los cambios? Por supuesto, idealmente, vale la pena cometer solo un buen código y escribir inmediatamente sin errores. Entonces tal problema no surgirá.
(para aquellos que no entendieron las sugerencias en el tanque, la charla es que el efecto negativo si esto sucede en el repositorio con 10 desarrolladores y en el repositorio con 300 será ligeramente diferente)

7) Fusionar bot


¿Has oído hablar de tal cosa? ¿Sabes por qué lo necesitas? Te reirás, pero esta es otra herramienta que no debería haber existido :) Solo imagina que el tiempo de construcción de tu proyecto es de 30 minutos. Y 100 desarrolladores están trabajando en su proyecto. Supongamos que cada uno empuja 1 commit por día. Ahora imagine un CI honesto, que le permite fusionar cambios en el maestro solo después de que se hayan aplicado al último commit del maestro (rebase).

Atención, la pregunta es: ¿cuántas horas debería haber en un día para que un servidor de CI tan honesto estrangule los cambios de todos los desarrolladores? La respuesta correcta es 50. Aquellos que respondieron correctamente pueden tomar una zanahoria de un estante. Bueno, o imagínese cómo simplemente cortó su compromiso hasta el último compromiso con el maestro, comenzó la asamblea y, cuando terminó, el maestro ya tenía 20 compromisos por delante. ¿Todo de nuevo?

Por lo tanto, merge bot o merge queue es un servicio que automatiza el proceso de cambio de nombre de todas las solicitudes de fusión para un maestro nuevo, ejecutando pruebas y la fusión en sí, y también puede combinar confirmaciones en lotes y probarlas juntas. Muy práctico. Ver mergify.io , k8s test-infra Prow de Google, bors-ng , etc. (Prometo escribir más sobre esto en el futuro)

Ahora por menos problemas técnicos:

8) Usando una sola herramienta de construcción


Honestamente, todavía es un misterio para mí por qué armar todo el mono-repositorio usando un sistema de construcción común. ¿Por qué no construir javascript con Yarn, java con gradle, Scala con sbt, etc.? Si alguien sabe la respuesta a esta pregunta (no adivina ni sugiere, es decir, sabe), escriba los comentarios.

Por supuesto, parece obvio que usar un sistema de compilación es mejor que varios diferentes. Pero todavía entienden que cualquier cosa universal es obviamente peor que una especializada, porque lo más probable es que solo tenga un subconjunto de las funciones de todos los especializados. Pero aún peor, diferentes lenguajes de programación pueden tener diferentes paradigmas en términos de ensamblaje, gestión de dependencias, etc., lo que será muy difícil de incluir en un contenedor común. No quiero entrar en detalles, daré un ejemplo sobre bazel (vea los detalles en un artículo separado): encontramos 5 implementaciones independientes de reglas de ensamblaje de JavaScript para bazel de 5 compañías diferentes en GitHub, junto con la oficial de Google. Vale la pena considerarlo.

9) enfoques generales


En respuesta al artículo original, CTO de Chef escribió su respuesta Monorepo: ¡por favor! . En su respuesta, argumenta que "lo principal en el monorepo es que te hace hablar y hace visibles los defectos". Significa que cuando desee cambiar su API, tendrá que encontrar todos sus usos y discutir sus cambios con los encargados del mantenimiento de estos códigos.

Entonces mi experiencia es exactamente lo contrario. Está claro que esto depende mucho de la cultura de ingeniería en el equipo, pero veo desventajas sólidas en este enfoque. Imagine que está utilizando un enfoque específico que le ha servido fielmente durante algún tiempo. Entonces, por alguna razón, decidió resolver un problema similar, utilizar un método ligeramente diferente, posiblemente más moderno. ¿Cuál es la probabilidad de que agregar un nuevo enfoque pase por una revisión?

En mi pasado reciente, recibí comentarios varias veces como "ya tenemos una ruta probada, úsela" y "si desea implementar un nuevo enfoque, actualice el código en los 120 lugares donde se usa el enfoque anterior y obtenga la actualización de todos los equipos responsables de estas piezas de código ". Por lo general, el entusiasmo del "innovador" termina aquí.

¿Y cuánto, en su opinión, costará escribir un nuevo servicio en un nuevo lenguaje de programación? En el repositorio, en absoluto. Crea un nuevo repositorio y escribe, e incluso toma el sistema de compilación más adecuado. ¿Y ahora lo mismo en el monorepository?

Entiendo perfectamente que "estandarización, reutilización, código compartido", pero el proyecto debe desarrollarse. En mi opinión subjetiva, un monorepositivo más bien evita esto.

10) código abierto


Recientemente me preguntaron: " ¿existen herramientas de código abierto para mono-repositorios? ", Respondí: "El problema es que las herramientas para mono-repositorios, por extraño que parezca, se desarrollan dentro del mono-repositorio. ¡Por lo tanto, ponerlos en código abierto es bastante difícil!

Por ejemplo, mire un proyecto en Github con un complemento bazel para Intellij IDEA . Google lo desarrolla en su repositorio interno y luego "salpica" partes de él en Github con una pérdida de historial de confirmación, sin la capacidad de enviar una solicitud de extracción, y así sucesivamente. No creo que sea de código abierto (aquí hay un ejemplo de mi pequeño PR , que se cerró, en lugar de una fusión, y luego los cambios aparecieron en la próxima versión). Por cierto, este hecho se mencionó en el artículo original de que los mono-repositorios les impiden publicar en código abierto y crear una comunidad en torno al proyecto. Creo que muchos no le dieron mucha importancia a este argumento.

Alternativas


Bueno, si hablamos sobre qué hacer para evitar todos estos problemas. Hay exactamente un consejo: esforzarse por tener un repositorio lo más pequeño posible.
Pero, ¿qué tiene que ver el monorepository con él? Y a pesar de que este enfoque le priva de la oportunidad de tener repositorios pequeños, ligeros e independientes.

¿Cuáles son las desventajas del enfoque de polirrepositorio? Veo exactamente 1: la incapacidad de realizar un seguimiento de quién es el consumidor de su API. Esto es especialmente cierto en el enfoque de los microservicios de "no compartir nada" , en el que el código no está buscando entre microservicios. (Por cierto, ¿crees que alguien usa este enfoque en mono-repositorios?) Desafortunadamente, este problema debe resolverse ya sea por medios organizativos o intentar usar herramientas de exploración de código que admitan repositorios independientes (por ejemplo, https://sourcegraph.com / ).

¿Qué pasa con comentarios como "probamos los polirrepositorios, pero luego tuvimos que implementar constantemente características en varios repositorios a la vez, lo cual fue agotador, y fusionamos todo en una sola caldera" ? La respuesta a esto es muy simple: "no confunda los problemas del enfoque con una descomposición inadecuada" . Nadie afirma que el repositorio debe contener exactamente un microservicio y eso es todo. Cuando estaba usando polirrepositorios, reunimos perfectamente una familia de microservicios estrechamente relacionados en un solo repositorio. Sin embargo, teniendo en cuenta que había más de 100 servicios, había más de 20 repositorios de este tipo. Lo más importante a tener en cuenta en términos de descomposición es cómo se implementarán estos servicios.

Pero, ¿qué pasa con el argumento sobre la versión? ¡Después de todo, los mono-repositorios le permiten no tener versiones y desplegar todo desde una confirmación! En primer lugar, el control de versiones es el más simple de todos los problemas expresados ​​aquí. Incluso en una cosa antigua como maven hay un complemento de versión de maven que le permite degradar la versión con solo un clic. Y en segundo lugar, y lo más importante, ¿tiene su empresa aplicaciones móviles? Si es así, entonces ya tienes versiones, ¡y no obtendrás nada de esto!

Bueno, todavía existe el argumento principal a favor de los mono-repositorios: ¡le permite refactorizar toda la base de código en una confirmación! De hecho, no. Como se menciona en el artículo original, debido a las limitaciones que impone la implementación. Siempre debe tener en cuenta que durante mucho tiempo (la duración depende de cómo se construya su proceso) tendrá 2 versiones del mismo servicio en paralelo. Por ejemplo, en mi último proyecto, nuestro sistema estuvo en este estado durante varias horas en cada implementación. Esto lleva al hecho de que es imposible realizar refactorizaciones globales que afecten a las interfaces de interacción en un solo commit, incluso en un mono-repositorio.

En lugar de una conclusión:


Entonces, esos colegas respetados y pocos que trabajan en Google, Facebook, etc. y venga aquí para defender sus mono-repositorios, quiero decir: "No se preocupe, está haciendo todo bien, disfrute de su afinación, que pasó cientos de miles o millones de horas humanas. Ya se han gastado, así que si no lo usas, nadie lo hará ".

Y para todos los demás: "¡No eres Google, no uses mono-repositorios!"

P.S. Como señaló el respetado Bobuk en el podcast de radio-T al discutir el artículo original: “Hay ~ 20 compañías en el mundo que pueden usar un solo repositorio. El resto ni siquiera debería intentarlo .

Source: https://habr.com/ru/post/es436264/


All Articles