¿Cómo organizar múltiples proyectos en una base de código con lógica compartida, en Python? Si tiene una aplicación web y algunos servicios de fondo que hablan con las mismas bases de datos y comparten la lógica, ¿es malo una sola base de código? ¿Deberían dividirse en bases de código separadas?

DESCOUPLE! DECOUPLE, ¡Maldita sea!

En Spotify, hemos aprendido, a través de dolores de mantenimiento equivalentes al calor o mil soles ardientes, que el desacoplamiento es el camino a seguir. Básicamente, desacoplar hasta que duela .

Descubrimos que mientras más cosas construyes en una cosa, más frágil es. En parte solo porque más código inevitablemente significa más errores y un mantenimiento más complicado, y en parte porque cuantas más responsabilidades tenga un sistema, más sistemas dependerán de él. Esto, a su vez, significa que el servicio fallará más porque está bajo un mayor estrés por parte de muchas otras aplicaciones (que, como beneficio adicional, fallará con él).

El backend de Spotify (que es Python en gran medida, por cierto) consta de unos 100 servicios diferentes. Todos son muy, muy simples y están estrictamente enfocados en hacer una cosa bien. La mayoría de ellos son bastante autónomos, lo que significa que dependen de unos pocos otros servicios, y cuando lo hacen, suponen que los otros servicios se ralentizan y / o disminuyen de vez en cuando.

Tenga en cuenta que estos son servicios separados . Tienen su propia base de código y sus propias bases de datos. Por ejemplo, el servicio de lista de reproducción está completamente separado del servicio de usuario y el almacenamiento de música. Compartir una sola base de datos NO es una buena idea: rápidamente se convierte en una cosa horrible masiva e imposible de mantener que no debe fallar, pero lo hace, todo el tiempo. 🙂 He observado este fenómeno de “base de datos maestra” de primera mano en otras compañías, y NO es divertido y sucede REALMENTE rápido.

Estos servicios se comunican entre sí mediante un protocolo propietario, pero solía hacerse en HTTP. HTTP funcionará bien para la mayoría de los servicios, la razón por la que ya no usamos HTTP en Spotify es porque nuestra situación de carga es extrema: 20 millones de usuarios que transmiten música es una locura.

Spotify hace un uso increíblemente extenso de submódulos git, que definitivamente es una solución viable. Sin embargo, personalmente no soy fanático. Los submódulos de Git no están diseñados para ser un administrador de paquetes, y si puede, use administradores de paquetes reales en su lugar, como pip o easy_install para Python, npm para node.js o gem para Ruby. Le ahorrará mucho dolor tener paquetes con versiones semánticas en lugar de meterse con submódulos git. Para configurar un servidor de paquetes privado, recomiendo Gemfury.

Lo hacemos, en realidad utilizamos un único repositorio para todo esto, y git parece manejar esto muy bien. Si usa Github, también podría usar repositorios separados, pero es muy difícil configurarlo sin él.

Editar: se puede encontrar una lectura más divertida sobre el backend de Spotify en esta respuesta: ¿Cuál es la arquitectura de Spotify?

Creo firmemente que cada base de datos debe tener un servicio “propietario” que sea responsable de su esquema, migraciones, lógica del modelo central, etc. Nunca debería haber ninguna duda de dónde está.

Más allá de eso, depende del tamaño, la complejidad y la escala de sus sistemas. Según la respuesta de Mattias, tener una arquitectura orientada al servicio con muchos módulos bien definidos es fundamental en la escala en la que están trabajando. Pero también hay un costo para esa arquitectura. Debe desarrollar API para la comunicación entre servicios, es probable que las API tengan que ser compatibles con versiones anteriores, la implementación y la supervisión se vuelvan más complejas, etc.

Lo mismo se aplica a los repositorios. Mientras tenga un repositorio, sabrá que cada vez que realiza una confirmación, toda su base de código se encuentra en un estado particular. Si tiene múltiples repositorios, puede enviar un cambio a un repositorio que divide las cosas en un repositorio diferente. Esto puede ser un desafío si tiene un equipo de personas trabajando en diferentes partes del sistema, y ​​cada uno puede tener versiones diferentes de todos los repos desprotegidos.

Yo diría que mientras funcione, use un único repositorio, pero intente dividir su código en módulos independientes en ese repositorio. De esa forma, si desea extraer una parte del código en su propio repositorio, se requiere una cantidad mínima de reescritura.

Cada proyecto tiene sus propias peculiaridades, pero una forma en que podría considerar hacer esto es tener cada unidad principal estructurada como su propia base de código / repositorio y unirlas usando submódulos git . Los submódulos pueden ser difíciles de usar y, si no se usan correctamente, pueden convertirse rápidamente en una pesadilla de mantenimiento; así que lee y practica bien antes de seguir este camino.

Por código base, supongo que quiere decir: “¿debería ser un único repositorio en el scm que utiliza”.
Comenzaría simple y tendría un solo repositorio. Esto le permitirá bifurcar y refactorizar el código que cruza el frontend y el backend sin tener que crear revisiones rotas.
Si prevé tener cantidades extremadamente grandes de código, debería considerar dividir el código en componentes que tienen menos probabilidades de requerir cambios transversales.
Recuerde que, al menos en el caso de git, no puede extraer una copia escasa (es decir, solo un subárbol), de modo que cuando las personas frontend comprueben su código, también obtendrán el código de fondo. Esto puede ser importante para usted, pero generalmente no lo es.

Soy un novato relativo. Pero aprendo rápido y tengo la suerte de trabajar para un par de personas brillantes. Miro y escucho con mucha atención. He aprendido que compartir código / datos / módulos entre diferentes proyectos es una mala idea. Vi a mi brillante jefe hacer esto con nuestro código marco y escuché sus maldiciones mientras aprendía a arrepentirse. Yo mismo he tratado de poner un único control perfecto para múltiples usos en una aplicación compleja y no fue bueno. No ahorra tiempo. En absoluto.