¿Cómo evitamos el desarrollo impulsado por CI ...?

45

Estoy trabajando en un gran proyecto de código abierto dirigido por la investigación, con muchos otros colaboradores habituales. Debido a que el proyecto ahora es bastante grande, un consorcio (compuesto por dos empleados a tiempo completo y pocos miembros) se encarga de mantener el proyecto, la integración continua (CI), etc. Simplemente no tienen tiempo para la integración de contribuciones sin embargo.

El proyecto se compone de un marco "central", de aproximadamente medio millón de líneas de código, un montón de "complementos" que mantiene el consorcio y varios complementos externos, la mayoría de los cuales no estamos t incluso consciente de.

Actualmente, nuestro CI crea el núcleo y los complementos mantenidos.

Uno de los grandes problemas que enfrentamos es que la mayoría de los contribuyentes (y especialmente los ocasionales) no están construyendo el 90% de los complementos mantenidos, por lo que cuando proponen cambios de refactorización en el núcleo (que actualmente ocurre de manera bastante regular), comprobaron que el código se compila en su máquina antes de realizar una solicitud de extracción en GitHub.

El código funciona, están contentos, y luego el CI termina de compilarse y comienzan los problemas: la compilación falló en un complemento mantenido por el consorcio, que el contribuyente no construyó en su máquina.

Ese complemento puede tener dependencias de bibliotecas de terceros, como CUDA, por ejemplo, y el usuario no quiere, no sabe cómo, o simplemente no puede, por razones de hardware, compilar ese complemento roto.

De manera que - ya sea el estancias de relaciones públicas ad aeternam en el limbo de la no-a-ser-fusionado RP - O el contribuyente GREPS la variable cuyo nombre cambió en la fuente del plugin roto, cambia el código, empuja a su / su rama, espera el CI para terminar de compilar, generalmente recibe más errores y reitera el proceso hasta que CI esté contento, o uno de los dos permanentes que ya están sobrevendidos en el consorcio echa una mano e intenta arreglar el RP en su máquina.

Ninguna de esas opciones es viable, pero simplemente no sabemos cómo hacerlo de manera diferente. ¿Alguna vez te has enfrentado a una situación similar de tus proyectos? Y si es así, ¿cómo manejó este problema? ¿Hay alguna solución que no esté viendo aquí?

lagarkane
fuente
84
La regla más importante para proporcionar un complemento API a un sistema es que se mantenga estable o al menos compatible con versiones anteriores. Los cambios en el núcleo sin cambios intencionados en la API de complementos nunca interrumpirán la compilación de ningún complemento (puede suceder que rompa la funcionalidad por accidente, pero no la compilación). Si un simple cambio de un nombre de variable dentro del núcleo puede conducir a una compilación rota de un complemento , la separación entre los complementos y el núcleo parece estar completamente rota.
Doc Brown
"Interfaces públicas versus publicadas" de Martin Fowler podría ser una lectura útil.
Schwern
1
@KevinKrumwiede: Estoy seguro de que ya lo saben ;-) Si experimentaste incompatibilidades, estoy bastante seguro de que cambiaron la API intencionalmente.
Doc Brown
3
Reformularía la pregunta, ya que es realmente engañosa. Algo así como ¿Cómo puedo gestionar las relaciones públicas cuando rompen nuestro CI actual? captura mejor tu situación, creo.
bracco23
2
¿Qué tan difícil / complejo es su proceso de compilación / prueba? Debería ser solo cuestión de ejecutar un solo comando o hacer clic en un solo botón. En ese momento, es razonable esperar que los usuarios realicen todas las pruebas por sí mismos antes de enviar un RP.
Alexander

Respuestas:

68

¡El desarrollo impulsado por CI está bien! ¡Esto es mucho mejor que no ejecutar pruebas e incluir código roto! Sin embargo, hay un par de cosas para facilitar esto a todos los involucrados:

  • Establezca expectativas: tenga documentación de contribución que explique que CI a menudo encuentra problemas adicionales, y que estos deberán corregirse antes de una fusión. Quizás explique que los cambios locales pequeños tienen más probabilidades de funcionar bien, por lo que dividir un cambio grande en múltiples RP puede ser sensato.

  • Fomente las pruebas locales: facilite la configuración de un entorno de prueba para su sistema. ¿Un script que verifica que se hayan instalado todas las dependencias? ¿Un contenedor Docker listo para usar? ¿Una imagen de máquina virtual? ¿Tiene su corredor de pruebas mecanismos que permitan priorizar las pruebas más importantes?

  • Explicar cómo usar CI para sí mismos: parte de la frustración es que esta retroalimentación solo llega después de enviar un RP. Si los contribuyentes configuran CI para sus propios repositorios, recibirán comentarios anteriores y producirán menos notificaciones de CI para otras personas.

  • Resuelva todos los RP, de cualquier manera: si algo no se puede fusionar porque está roto, y si no hay progreso para solucionar los problemas, simplemente ciérrelo. Estas relaciones públicas abiertas abandonadas desordenan todo, y cualquier comentario es mejor que simplemente ignorar el problema. Es posible expresar esto muy bien y dejar en claro que, por supuesto, sería feliz fusionarse cuando se solucionen los problemas. (ver también: El arte de cerrar por Jessie Frazelle , Mejores prácticas para mantenedores : aprender a decir que no )

    También considere hacer que estos RP abandonados sean reconocibles para que otra persona pueda recogerlos. Esto incluso puede ser una buena tarea para los nuevos contribuyentes, si los problemas restantes son más mecánicos y no necesitan una profunda familiaridad con el sistema.

Para la perspectiva a largo plazo, esos cambios parecen romper funcionalidades no relacionadas que a menudo podrían significar que su diseño actual es un poco problemático. Por ejemplo, ¿las interfaces del complemento encapsulan adecuadamente las partes internas de su núcleo? C ++ facilita la filtración accidental de detalles de implementación, pero también hace posible crear fuertes abstracciones que son muy difíciles de usar mal. No puede cambiar esto durante la noche, pero puede guiar la evolución a largo plazo del software hacia una arquitectura menos frágil.

amon
fuente
13
"Estas relaciones públicas abiertas abandonadas desordenan todo" Ojalá más mantenedores tuvieran esta actitud 😔
GammaGames
34

La construcción de un modelo de complemento sostenible requiere que su marco principal exponga una interfaz estable en la que los complementos puedan confiar. La regla de oro es que puede introducir nuevas interfaces con el tiempo, pero nunca puede modificar una interfaz ya publicada. Si sigue esta regla, puede refactorizar la implementación del marco central todo lo que desee sin temor a romper accidentalmente los complementos, ya sea uno mantenido por el consorcio o uno externo.

Por lo que describió, parece que no tiene una interfaz bien definida, y eso hace que sea difícil saber si un cambio romperá los complementos. Trabaje para definir esta interfaz y hacerla explícita en su base de código, para que los contribuyentes sepan lo que no deben modificar.

casablanca
fuente
20
CI debería tener pruebas automatizadas. Si desea asegurarse de que los complementos tengan la misma interfaz, cada complemento debe contribuir con pruebas que expresen la interfaz que necesitan. Hágalo de esta manera y cuando la interfaz cambie, lo que hará, sabrá qué complementos está rompiendo. Dame estas pruebas para ejecutar localmente y sabré lo que estoy rompiendo antes de emitir el PR
candied_orange
1
La buena definición de @lagarkane es más una cuestión de política que técnica. Hay software por ahí que, como el suyo, simplemente abandona el comportamiento anterior en una actualización. Perl5 no es compatible con Perl6, Python2.7 no es totalmente compatible con Python3.4, etc. Luego, hay software que, pase lo que pase, todavía admite código antiguo. Todavía puede ejecutar casi todo el código JavaScript escrito para Netscape Navigator 4 en los navegadores modernos. El lenguaje de programación Tcl es compatible con backwords desde la versión original, etc ...
slebetman
3
@lagarkane: Formar el consorcio fue un paso en la dirección correcta, y si los miembros principales concentran su energía en crear estas interfaces, entonces puede aprovechar el poder de futuros doctorados y pasantes para mantener su proyecto en marcha y minimizar las roturas. :)
casablanca
44
@Fattie: Eso funciona para Apple porque construyen productos exitosos para el consumidor y los desarrolladores se ven obligados a seguir el juego si quieren ser parte de él. Es poco probable que a esos desarrolladores les gusten los cambios importantes, y definitivamente no es un buen modelo para un proyecto de código abierto.
casablanca
1
@casablanca tanto el linaje MacOS como WindowsOS tienen un gran éxito. (Podría decirse que los dos mejores productos en términos de dólares en la existencia humana). A lo largo de las décadas tuvieron enfoques absolutamente opuestos. Al parecer, ambos tuvieron éxito!
Fattie
8

Para ser honesto, no creo que pueda manejar esto de una mejor manera: si los cambios resultan en la ruptura de partes mantenidas de su proyecto, el CI debería fallar.

¿Tiene su proyecto contributing.mdalgo o algo similar para ayudar a los contribuyentes nuevos y ocasionales a preparar sus contribuciones? ¿Tiene una lista clara, qué complementos son parte del núcleo y deben seguir siendo compatibles?

Si es difícil construir todo en una máquina debido a dependencias, etc., podría pensar en crear imágenes de acoplador listas para usar como entornos de compilación para que las utilicen sus colaboradores.

mhr
fuente
1
¡Gracias por la respuesta! Sí, tenemos pautas de contribución disponibles públicamente, pero no enumera los complementos como sugiere, lo que ya sería una buena idea. ¡Hacer imágenes de Docker suena como una gran mejora para el proceso de contribución actual! Gracias por la entrada
lagarkane
8

por lo tanto, cuando proponen cambios de refactorización en el núcleo (que actualmente ocurre de manera bastante regular), verificaron que el código se compila en su máquina antes de realizar una solicitud de extracción en github.

Así que creo que aquí es donde puede caer el estilo suelto de los proyectos de código abierto; La mayoría de los proyectos organizados centralmente desconfían de la refactorización central, especialmente cuando cruza un límite API. Si refactorizan un límite de API, generalmente es un "big bang" donde todos los cambios se programan a la vez con un incremento a la versión principal de API, y se mantiene la API anterior.

Propondría una regla "todos los cambios de API deben planificarse por adelantado": si entra un RP que realiza un cambio incompatible hacia atrás en la API, de alguien que no ha estado en contacto con los mantenedores para acordar su enfoque por adelantado, simplemente se cierra y el remitente señaló la regla.

También necesitará versiones explícitas de la API del complemento. Esto le permite desarrollar v2 mientras todos los complementos v1 continúan construyéndose y funcionando.

También me preguntaría un poco más por qué se están realizando tantos cambios de API y refactorización central. ¿Son realmente necesarios o simplemente personas que imponen su gusto personal en el proyecto?

pjc50
fuente
2

Parece que el proceso de CI debe ser más estricto, más completo y más visible para los contribuyentes antes de que generen un PR Como ejemplo, BitBucket tiene una función de canalización que permite esto, donde le da un archivo que define en código el proceso de compilación de CI, y si falla, se evita que la rama se fusione.

Independientemente de la tecnología, proporcionar compilaciones automáticas cuando un contribuyente se dirige a una sucursal les dará una respuesta mucho más rápida sobre lo que deben tener en cuenta al realizar cambios y conducirá a relaciones públicas que no necesitan reparaciones después del hecho.

Sería bueno solucionar los problemas de diseño, pero son ortogonales a este problema.

Nathan Adams
fuente
2

El código funciona, están contentos, y luego el CI termina de compilarse y comienzan los problemas: la compilación falló en un complemento mantenido por el consorcio, que el contribuyente no construyó en su máquina.

Ese complemento puede tener dependencias de bibliotecas de terceros, como CUDA, por ejemplo, y el usuario no quiere, no sabe cómo, o simplemente no puede, por razones de hardware, compilar ese complemento roto.

Su solución es simple: baje la barrera a la contribución .

La forma más sencilla de (1) acelerar el ciclo de edición-compilación-prueba y (2) las diferencias de entorno sin problemas es proporcionar servidores de compilación :

  • Elija máquinas robustas: 24, 48 o 96 núcleos, 2 GB de RAM / núcleo, SSD, para acelerar la compilación.
  • Asegúrese de que tengan el hardware adecuado: FPGA, tarjeta gráfica, lo que sea necesario.
  • Cree una imagen de Docker con todas las bibliotecas de software necesarias preinstaladas.

Y luego abra esos servidores de compilación para los contribuyentes. Deben poder iniciar sesión de forma remota en una nueva imagen de Docker y editar, compilar y probar de forma remota en esta máquina.

Entonces:

  • No tienen excusa para no construir / probar los complementos mantenidos: tienen todo disponible.
  • No tienen que esperar largos comentarios con los RP impulsados ​​por CI: tienen una compilación incremental y la capacidad de depurar (en lugar de adivinar).

En general, los servidores de compilación pueden compartirse entre múltiples contribuyentes, sin embargo, cuando se trata de periféricos de hardware especiales, puede ser necesario que un contribuyente use dicho periférico por sí mismo.


Fuente: trabajando en software que utiliza FPGA, dado el precio de las bestias y la variedad de modelos que necesitamos, no encuentra cada modelo de FPGA instalado en la máquina de cada desarrollador.

Matthieu M.
fuente
1

Si contribuir al núcleo sin cambiar ningún contrato puede romper el software dependiente, sugiere que:

  • Los contratos de sus interfaces pueden ser ambiguos. Tal vez agregar atributos en sus funciones y parámetros de función ayudaría a exponer restricciones adicionales al código del cliente para aclarar los contratos. O si está aplicando cambios de ruptura de contrato, tal vez la adopción de versiones semánticas pueda ayudar.
  • Las pruebas unitarias no cubren suficientes escenarios de llamadas posibles.

Cualquiera de los problemas debería ser fácil de resolver, pero usted menciona que el equipo central podría no tener la capacidad para hacerlo. Una opción sería pedir ayuda a la comunidad para abordar el problema.

jcayzac
fuente
1

Nadie más parece haber planteado esto como una posible solución.

  • enumere todos los complementos a los que puede acceder.
  • ejecuta todas las pruebas que definen estos complementos
  • registrar todas las solicitudes / respuestas / interacciones entre el núcleo y todos los complementos
  • almacenar esas grabaciones, estas son ahora pruebas de compatibilidad aproximadas.

Al desarrollar el núcleo, aliente a los desarrolladores a ejecutar estas pruebas de compatibilidad. Si fallan, no se registre.

Esto no garantizará al 100% la compatibilidad, pero detectará muchos más problemas y pronto.

Un beneficio secundario es que estas grabaciones pueden resaltar qué interfaces se usan activamente y qué características se usan activamente.

Kain0_0
fuente
0

Tengo problemas para entender la situación como parece ser: ¿el CI solo crea una rama?

¿Hay alguna razón por la que no puede construir más de una rama con el CI?

La solución más simple a este problema sería hacer posible que cualquier contribuyente ejecute la compilación de CI en su rama de características .

Luego, simplemente necesita una compilación de CI exitosa en la rama de características para que se acepte la solicitud de extracción de esa rama.

Kyralessa
fuente
Esto parece resumir el problema.
Fattie
1
La pregunta dice "O el contribuyente [...] cambia el código, empuja su rama, espera a que el CI termine de compilar, generalmente obtiene más errores y reitera el proceso hasta que CI esté contento", así que creo que esto ya es el caso, pero el problema es que es algo doloroso desarrollarse con un ciclo de edición-depuración tan largo.
npostavs
@npostavs Gracias, supongo que eso es lo que me perdí la primera o dos veces que lo leí. Aun así ... supongo que no parece el problema. Hay muchas dependencias, no se pueden romper, por lo que un contribuyente debe mantenerse compatible con todas ellas. Esa es la naturaleza del gran software. Ciertamente, se podría trabajar para acelerar la construcción, tal vez, pero de lo contrario, ¿qué atajo podría haber?
Kyralessa