Tenemos múltiples clientes con diferentes necesidades. Aunque nuestro software está modularizado hasta cierto punto, es casi seguro que necesitamos ajustar un poco la lógica comercial de cada módulo para cada cliente. Los cambios son probablemente demasiado pequeños para justificar la división del módulo en un módulo (físico) distinto para cada cliente, me temo que haya problemas con la compilación, un caos de enlaces. Sin embargo, esos cambios son demasiado grandes y demasiados para configurarlos mediante conmutadores en algún archivo de configuración, ya que esto generaría 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. Entonces tengo algunas preguntas:
¿Recomendaría dejar que el sistema de compilación cree varias compilaciones? ¿Cómo debo almacenar las diferentes personalizaciones en el control de código fuente, en particular svn?
#ifdef
Funciona para ti?Respuestas:
Lo que necesita es una organización de código similar a la ramificación de características . En su escenario específico, esto debería denominarse ramificación de troncal específica del cliente porque probablemente también utilizará la ramificación de funciones mientras desarrolla cosas nuevas (o resuelve errores).
http://svnbook.red-bean.com/en/1.5/svn.branchmerge.commonpatterns.html
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.
Luego, también tiene sus sucursales específicas del cliente donde también combina las ramas de las mismas características según sea necesario (selección de cerezas si lo desea).
Estas ramas de clientes se parecen a las ramas de características, aunque no son temporales ni de corta duración. Se mantienen durante un período de tiempo más largo y principalmente solo se fusionan. Debe haber el menor desarrollo posible de ramificaciones de características específicas del cliente.
Las ramas específicas del cliente son ramas paralelas al enlace troncal y están activas siempre que el enlace troncal en sí mismo y ni siquiera se fusionan en ningún lugar.
fuente
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 de proyecto. Cada proyecto de cliente es un proyecto separado que luego depende del común como dependencia.
Esto es más fácil si su aplicación base común usa un marco de inyección de dependencias como Spring, para que pueda inyectar fácilmente diferentes variantes de reemplazo de objetos en cada proyecto del cliente, ya que se requieren características personalizadas. Incluso si aún no tiene un marco DI, agregar uno y hacerlo de esta manera puede ser su opción menos dolorosa.
fuente
Almacene el software para todos los clientes en una sola sucursal. No hay necesidad de divergir los cambios realizados para diferentes clientes. Lo más probable es que desee 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 de fusión 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 guárdelo con diferentes definiciones. Haga que su sistema de compilación tenga objetivos específicos para cada cliente, cada objetivo compilando 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 las características para diferentes clientes con "
#ifdef
" o cualquier mecanismo que tenga su lenguaje para la gestión de la configuración de compilación, y preparar un conjunto de definiciones correspondientes a la cantidad de características que ha pagado un cliente.Si no le gustan los
ifdef
s, use "interfaces e implementaciones", "functors", "archivos de objetos" o cualquier herramienta que su lenguaje proporcione para almacenar diferentes cosas en un solo lugar.Si distribuye fuentes a sus clientes, es mejor hacer que sus scripts de compilación tengan "objetivos de distribución de fuentes" especiales. Una vez que invocas dicho objetivo, crea una versión especial de las fuentes de tu software, las copia en una carpeta separada para que puedas enviarlas y no las compila.
fuente
Como muchos han dicho: factorice su código correctamente y personalícelo en función del código común ; esto mejorará significativamente la capacidad de mantenimiento. Ya sea que esté utilizando un lenguaje / sistema orientado a objetos o no, esto es posible (aunque es bastante 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!
fuente
Muy cuidadosamente
La función de ramificación es una opción, pero creo que es algo pesada. También hace que las modificaciones profundas sean fáciles, lo que puede conducir a una bifurcación de su aplicación si no se mantiene bajo control. Idealmente, desea 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.
Así es como lo haría, aunque no sé si es aplicable a su base de código sin grandes modificaciones y refactorizaciones. Tuve un proyecto similar donde la funcionalidad básica era la misma pero cada cliente requería un conjunto muy específico de características. Creé un conjunto de módulos y contenedores que luego ensamblo a través de la configuración (a 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. De vez en cuando coloco allí también algunos componentes personalizados para este cliente. Pero esto es raro y, siempre que sea posible, trato de hacerlo en una forma más genérica y lo empujo hacia abajo para que otros proyectos puedan usarlos.
El resultado final es que obtuve el nivel de personalización que necesitaba, obtuve secuencias de comandos de instalación personalizadas para que cuando llegue al sitio del cliente no parezca que esté modificando el sistema todo el tiempo y, como una ventaja adicional MUY significativa, obtengo para poder crear pruebas de regresión conectadas directamente a la compilación. De esta manera, cada vez que recibo un error específico del cliente, puedo escribir una prueba que reafirmará el sistema a medida que se implementa y, por lo tanto, puede hacer TDD incluso a ese nivel.
en resumen:
Si se realiza correctamente, el ensamblaje de su producto debe contener todos menos algunos archivos de configuración.
Después de usar esto por un tiempo, terminé creando metapaquetes que ensamblan los sistemas más utilizados o esenciales como una unidad central y utilizo este metapaquete para ensambles de clientes. Después de unos años terminé teniendo una gran caja de herramientas que pude armar muy rápidamente para crear soluciones para el cliente. Actualmente estoy investigando Spring Roo y veo si no puedo impulsar la idea un poco más, esperando que algún día pueda crear un primer borrador del sistema con el cliente en nuestra primera entrevista ... Supongo que podría llamarlo User Driven Desarrollo ;-).
Espero que esto haya ayudado
fuente
En mi opinión, no pueden ser demasiado pequeños. Si es posible, factorizaría el código específico del cliente usando el patrón de estrategia prácticamente en todas partes. Esto reducirá la cantidad de código que debe ser 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 utilizando estrategias predeterminadas y probar las clases específicas del cliente por separado.
Si sus módulos están codificados de manera 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.
fuente
Puede usar su 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, mantenga sucursales separadas. Cualquier buena herramienta SCM funcionará realmente bien con la fusión de ramas (Git viene a la mente). Cualquier actualización en la rama maestra debe fusionarse en las ramas personalizadas, pero el código específico del cliente puede permanecer en su propia rama.
Sin embargo, si es posible, intente diseñar el sistema de forma modular y configurable. La desventaja de estas ramas personalizadas puede ser que se aleja demasiado del núcleo / maestro.
fuente
Si está escribiendo en C simple, aquí hay una forma bastante fea de hacerlo.
Código común (por ejemplo, unidad "frangulator.c")
código específico del cliente, piezas que son pequeñas y se usan solo para cada cliente.
en el código de la unidad principal, use #ifdef y #include para hacer algo como
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 genera otros problemas, pero también es simple, y puede comparar los archivos específicos del cliente uno contra el otro con bastante facilidad.
También significa que todas las piezas específicas del cliente son claramente visibles (cada una en su propio archivo) en todo momento, y hay una relación clara entre el archivo de código principal y la parte del archivo específica del cliente.
Si se vuelve realmente inteligente, puede configurar archivos MAKE para crear la definición correcta del cliente, de modo que algo como:
hacer clienta
creará para client_a, y "make clientb" creará para client_b y así sucesivamente.
(y "make" sin objetivo proporcionado puede emitir una advertencia o una descripción de uso).
He usado una idea similar antes, lleva un tiempo configurarlo, pero puede ser muy efectivo. En mi caso, un árbol fuente construyó alrededor de 120 productos diferentes.
fuente
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. Cada vez que se realice un cambio en el código central, simplemente vuelva a clasificar todas las ramas específicas del cliente en la parte superior del maestro, de modo que tenga un conjunto de parches móviles para los clientes que se apliquen en la parte superior de la línea base actual.
Siempre que realice un cambio para un cliente y observe un error que debe incluirse en otras ramas, puede seleccionarlo en el maestro o en las otras ramas que necesitan la solución (aunque las diferentes ramas del cliente comparten código) , probablemente deberías hacer que ambos se ramifiquen de una rama común del maestro).
fuente