Cómo mantener versiones diferentes y personalizadas del mismo software para varios clientes

42

tenemos múltiples clientes con diferentes necesidades. Si bien nuestro software está modularizado hasta cierto punto, es casi seguro que debemos ajustar la lógica de negocios de cada módulo aquí y allá para cada cliente. Es probable que los cambios sean demasiado pequeños para justificar la división del módulo en un módulo (físico) distinto para cada cliente, me temo que hay problemas con la construcción, un caos de enlace. Sin embargo, esos cambios son demasiado grandes y demasiados para configurarlos con los switches en algún archivo de configuración, ya que esto podría generar problemas durante la implementación y probablemente muchos problemas de soporte, especialmente con los administradores de tipo Tinker.

Me gustaría que el sistema de compilación cree varias compilaciones, una para cada cliente, donde los cambios están contenidos en una versión especializada del módulo físico en cuestión. Así que tengo algunas preguntas:

¿Recomendaría que el sistema de compilación cree múltiples compilaciones? ¿Cómo debo almacenar las diferentes personalizaciones en el control de código fuente, en particular svn?

    
pregunta Falcon 21.03.2011 - 11:21

9 respuestas

8

Lo que necesitas es ramificación de características , como una organización de código. En su escenario específico, esto debería llamarse ramificación de troncales específicas del cliente porque es probable que también use la ramificación de características mientras desarrolla nuevas cosas (o resuelve errores).

enlace

La idea es tener un tronco de código con nuevas ramas de características fusionadas. Me refiero a todas las características no específicas del cliente.

También tiene sus sucursales específicas del cliente en las que también combina las mismas ramas de características que las requeridas (si lo desea).

Estas sucursales de clientes tienen un aspecto similar a las sucursales de características, aunque no son temporales ni de corta duración. Se mantienen durante un período de tiempo más largo y se fusionan principalmente. Debería haber el menor desarrollo de funciones específicas para el cliente que sea posible.

Las ramas específicas del cliente son ramas paralelas a troncales y se mantienen activas tanto como el propio troncal y ni siquiera se fusionan como un todo en cualquier lugar.

            feature1
            ———————————.
                        \
trunk                    \
================================================== · · ·
      \ client1            \
       '========================================== · · ·
        \ client2            \
         '======================================== · · ·
              \ client2-specific feature   /
               '——————————————————————————´
    
respondido por el Robert Koritnik 21.03.2011 - 11:37
32

No hagas esto con ramas SCM. Convierta el código común en un proyecto separado que produzca una biblioteca o un artefacto de esqueleto del proyecto. Cada proyecto del cliente es un proyecto separado que luego depende del proyecto común como dependencia.

Esto es más fácil si su aplicación base común utiliza un marco de inyección de dependencias como Spring, de modo que puede inyectar fácilmente diferentes variantes de reemplazo de objetos en cada proyecto del cliente, ya que se requieren funciones personalizadas. Incluso si aún no tiene un marco DI, agregarlo y hacerlo de esta manera puede ser su elección menos dolorosa.

    
respondido por el Alb 22.03.2011 - 00:51
9

Almacene software para todos los clientes en una sola sucursal. No hay necesidad de desviar los cambios realizados para diferentes clientes. Muy a la ligera, querrá que su software sea de la mejor calidad para todos los clientes, y la corrección de errores en su infraestructura central debería afectar a todos sin una sobrecarga innecesaria, lo que también puede introducir más errores.

Modularice el código común e implemente el código que difiere en diferentes archivos o protéjalo con diferentes definiciones. Haga que su sistema de compilación tenga objetivos específicos para cada cliente, cada objetivo compila una versión con solo el código relacionado con un cliente. Si su código es C, por ejemplo, es posible que desee proteger funciones para diferentes clientes con " #ifdef " o cualquier mecanismo que tenga su idioma para la administración de la configuración de compilación, y preparar un conjunto de definiciones que correspondan a la cantidad de funciones que tiene un cliente. pagado por.

Si no te gusta ifdef s, usa "interfaces e implementaciones", "functores", "archivos de objetos" o cualquier herramienta que tu idioma proporcione para almacenar diferentes cosas en un solo lugar.

Si distribuye orígenes a sus clientes, es mejor hacer que sus scripts de compilación tengan "objetivos de distribución de origen" especiales. Una vez que invoca ese objetivo, crea una versión especial de sources de su software, los copia en una carpeta separada para que pueda enviarlos y no los compile.

    
respondido por el P Shved 21.03.2011 - 22:25
7

Como muchos han dicho: factorice su código correctamente y personalice según el código común : esto mejorará significativamente la capacidad de mantenimiento. Si está utilizando un lenguaje / sistema orientado a objetos o no, esto es posible (aunque es un poco más difícil de hacer en C que algo que está realmente orientado a objetos). ¡Este es precisamente el tipo de problema que la herencia y la encapsulación ayudan a resolver!

    
respondido por el Michael Trausch 22.03.2011 - 01:04
4

Muy cuidadosamente

Feature Branching es una opción, pero me parece que es algo pesada. También hace que las modificaciones profundas sean fáciles, lo que puede llevar a un bifurcación directa de su aplicación si no se mantiene bajo control. Lo ideal es que desee aumentar al máximo las personalizaciones en un intento de mantener su base de código central tan común y genérica como sea posible.

Aquí es cómo lo haría, aunque no sé si es aplicable a su base de código sin modificaciones y refacciones pesadas. Tuve un proyecto similar donde la funcionalidad básica era la misma pero cada cliente requería un conjunto de características muy específico. Creé un conjunto de módulos y contenedores que luego armé a través de la configuración (à la IoC).

luego, para cada cliente, creé un proyecto que básicamente contiene las configuraciones y el script de compilación para crear una instalación totalmente configurada para su sitio. Ocasionalmente coloco también algunos componentes hechos a medida para este cliente. Pero esto es raro y siempre que sea posible, trato de hacerlo de una forma más genérica y lo rechazo para que otros proyectos puedan usarlos.

El resultado final es que obtuve el nivel de personalización que necesitaba, que obtuve scripts de instalación personalizados, de modo que cuando llego al sitio del cliente no parezco estar ajustando el sistema todo el tiempo y, como agregado, MUY significativo. Además, puedo crear pruebas de regresión conectadas directamente a la compilación. De esta manera, en cualquier momento en que recibo un error que es específico para el cliente, puedo escribir una prueba que confirmará el sistema a medida que se implementa y, por lo tanto, puedo hacer TDD incluso a ese nivel.

en resumen:

  1. Sistema muy modularizado con una estructura de proyecto plana.
  2. Cree un proyecto para cada perfil de configuración (cliente, aunque más de uno puede compartir un perfil)
  3. Arme los conjuntos de funciones requeridos como un producto diferente y trátelos como tales.

Si se realiza correctamente, el ensamblaje de su producto debe contener todos menos unos pocos archivos de configuración.

Después de usar esto por un tiempo, terminé creando meta-paquetes que ensamblan los sistemas más utilizados o esenciales como una unidad central y usan este meta-paquete para los ensamblajes de los clientes. Después de unos años, terminé teniendo una gran caja de herramientas que pude ensamblar muy rápidamente para crear soluciones para los clientes. Actualmente estoy buscando en Spring Roo y veo si puedo impulsar la idea un poco más esperando que algún día pueda crear un primer borrador. del sistema justo con el cliente en nuestra primera entrevista ... Supongo que podría llamarlo User Driven Development ;-).

Espero que esto haya ayudado

    
respondido por el Newtopian 22.03.2011 - 03:34
3
  

Es probable que los cambios sean demasiado pequeños para justificar la división del módulo en un módulo (físico) distinto para cada cliente, me temo que hay problemas con la construcción, un caos de enlace.

OMI, no pueden ser demasiado pequeños. Si es posible, me gustaría factorizar el código específico del cliente utilizando el patrón de estrategia en casi todas partes. Esto reducirá la cantidad de código que debe estar ramificado y reducirá la fusión requerida para mantener el código general sincronizado para todos los clientes. También simplificará las pruebas ... puede probar el código general usando estrategias predeterminadas y probar las clases específicas del cliente por separado.

Si sus módulos están codificados de modo que el uso de la política X1 en el módulo A requiere el uso de la política X2 en el módulo B, piense en la refactorización para que X1 y X2 puedan combinarse en una sola clase de política.

    
respondido por el kevin cline 21.03.2011 - 20:28
1

Puedes usar tu SCM para mantener sucursales. Mantenga la rama maestra prístina / limpia del código personalizado del cliente. Hacer desarrollo principal en esta rama. Para cada versión personalizada de la aplicación mantener sucursales separadas. Cualquier buena herramienta de SCM funcionará muy bien combinando ramas (Git viene a la mente). Cualquier actualización en la rama maestra debe combinarse con las ramas personalizadas, pero el código específico del cliente puede permanecer en su propia rama.

Aunque, si es posible, intente diseñar el sistema de manera modular y configurable. La desventaja de estas ramas personalizadas puede ser que se aleja demasiado del núcleo / maestro.

    
respondido por el Htbaa 21.03.2011 - 11:36
1

Si estás escribiendo en C simple, aquí hay una manera bastante fea de hacerlo.

  • Código común (por ejemplo, unidad "frangulator.c")

  • código específico del cliente, piezas pequeñas y utilizadas solo para cada cliente.

  • en el código de la unidad principal, usa #ifdef y #include para hacer algo como

#ifdef CLIENT=CLIENTA
#include "frangulator_client_a.c"
#endif

Use esto como un patrón una y otra vez en todas las unidades de código que requieren personalización específica del cliente.

Esto es MUY feo, y conduce a otros problemas, pero también es simple, y puedes comparar los archivos específicos del cliente uno con el otro con bastante facilidad.

También significa que todas las partes específicas del cliente están claramente visibles (cada una en su propio archivo) en todo momento, y existe una relación clara entre el archivo de código principal y la parte específica del cliente del archivo.

Si te vuelves realmente listo, puedes configurar makefiles para crear la definición de cliente correcta, por lo que algo como:

hacer clienta

se compilará para client_a, y "make clientb" se creará para client_b y así sucesivamente.

(y "make" sin un destino proporcionado puede emitir una advertencia o descripción de uso)

He usado una idea similar anteriormente, se necesita un tiempo para configurarlo, pero puede ser muy efectivo. En mi caso, un árbol fuente construyó alrededor de 120 productos diferentes.

    
respondido por el quickly_now 22.03.2011 - 02:15
0

En git, la forma en que lo haría es tener una rama maestra con todo el código común y ramas para cada cliente. Siempre que se realice un cambio en el código central, simplemente vuelva a borrar todas las ramas específicas del cliente en la parte superior del maestro, para que tenga un conjunto de parches móviles para los clientes que se aplican sobre la línea de base actual.

Cada vez que realice un cambio para un cliente y observe un error que debería incluirse en otras sucursales, puede seleccionarlo en cualquiera de los dos maestros, o en las otras sucursales que necesitan la solución (aunque de diferentes sucursales de clientes). están compartiendo código, probablemente debería hacer que ambos se bifurquen en una rama común desde el maestro).

    
respondido por el Cercerilla 21.03.2011 - 23:30

Lea otras preguntas en las etiquetas