Entonces tenemos este enorme archivo fuente mainmodule.cpp (¿11000 líneas es enorme?) En nuestro proyecto y cada vez que tengo que tocarlo me da escalofríos.
Como este archivo es tan central y grande, sigue acumulando más y más código y no puedo pensar en una buena manera de hacer que realmente empiece a reducirse.
El archivo se usa y cambia activamente en varias (> 10) versiones de mantenimiento de nuestro producto, por lo que es realmente difícil refactorizarlo. Si tuviera que "simplemente" dividirlo, digamos para empezar, en 3 archivos, fusionar los cambios de las versiones de mantenimiento se convertirá en una pesadilla. Y también si divide un archivo con un historial tan largo y rico, el seguimiento y la verificación de los cambios antiguos en el SCC
historial de repente se vuelven mucho más difíciles.
Básicamente, el archivo contiene la "clase principal" (distribución y coordinación del trabajo interno principal) de nuestro programa, por lo que cada vez que se agrega una característica, también afecta a este archivo y cada vez que crece. :-(
¿Qué haría usted en esta situación? ¿Alguna idea sobre cómo mover nuevas funciones a un archivo fuente separado sin desordenar el SCC
flujo de trabajo?
(Nota sobre las herramientas: Usamos C ++ con Visual Studio
; Usamos AccuRev
como SCC
pero creo que el tipo de SCC
realmente no importa aquí; Usamos Araxis Merge
para hacer una comparación y fusión real de archivos)
fuente
Respuestas:
Encuentre algún código en el archivo que sea relativamente estable (que no cambie rápidamente y que no varíe mucho entre las ramas) y que pueda ser una unidad independiente. Mueva esto a su propio archivo, y para el caso a su propia clase, en todas las ramas. Debido a que es estable, esto no causará (muchas) fusiones "incómodas" que deben aplicarse a un archivo diferente del que se hicieron originalmente, cuando combina el cambio de una rama a otra. Repetir.
Encuentre un código en el archivo que básicamente solo se aplica a un pequeño número de sucursales, y podría estar solo. No importa si está cambiando rápidamente o no, debido a la pequeña cantidad de ramas. Mueva esto a sus propias clases y archivos. Repetir.
Entonces, nos hemos librado del código que es el mismo en todas partes, y el código que es específico para ciertas ramas.
Esto te deja con un núcleo de código mal administrado: se necesita en todas partes, pero es diferente en cada rama (y / o cambia constantemente para que algunas ramas se ejecuten detrás de otras), y sin embargo, está en un solo archivo que estás tratando sin éxito de fusionarse entre ramas. Para de hacer eso. Ramifique el archivo de forma permanente , tal vez renombrándolo en cada rama. Ya no es "principal", es "principal para la configuración X". OK, entonces pierde la capacidad de aplicar el mismo cambio a múltiples ramas al fusionar, pero este es en cualquier caso el núcleo del código donde la fusión no funciona muy bien. Si de todos modos tiene que administrar manualmente las fusiones para lidiar con los conflictos, entonces no es una pérdida aplicarlas manualmente de forma independiente en cada rama.
Creo que te equivocas al decir que el tipo de SCC no importa, porque, por ejemplo, las habilidades de fusión de git son probablemente mejores que la herramienta de fusión que estás usando. Entonces, el problema central, "la fusión es difícil" ocurre en diferentes momentos para diferentes SCC. Sin embargo, es poco probable que pueda cambiar los SCC, por lo que el problema probablemente sea irrelevante.
fuente
La fusión no será una pesadilla tan grande como lo será cuando obtenga el archivo 30000 LOC en el futuro. Entonces:
Si no puede dejar de codificar durante el proceso de refactorización, puede dejar este archivo grande tal como está durante un tiempo al menos sin agregarle más código: ya que contiene una "clase principal" que podría heredar de él y mantener la clase heredada ( es) con funciones sobrecargadas en varios archivos nuevos y bien diseñados.
fuente
Me parece que te enfrentas a una serie de olores de código aquí. En primer lugar, la clase principal parece violar el principio abierto / cerrado . También parece que está manejando demasiadas responsabilidades . Debido a esto, asumiría que el código es más frágil de lo necesario.
Si bien puedo entender sus inquietudes con respecto a la trazabilidad después de una refactorización, esperaría que esta clase sea bastante difícil de mantener y mejorar y que cualquier cambio que realice es probable que cause efectos secundarios. Supongo que el costo de estos supera el costo de refactorizar la clase.
En cualquier caso, dado que los olores del código solo empeorarán con el tiempo, al menos en algún momento el costo de estos superará el costo de la refactorización. Por su descripción, supongo que ha pasado el punto de inflexión.
Refactorizar esto debe hacerse en pequeños pasos. Si es posible, agregue pruebas automatizadas para verificar el comportamiento actual antes de refactorizar cualquier cosa. Luego seleccione pequeñas áreas de funcionalidad aislada y extráigalas como tipos para delegar la responsabilidad.
En cualquier caso, parece un gran proyecto, así que buena suerte :)
fuente
La única solución que he imaginado para tales problemas es la siguiente. La ganancia real por el método descrito es la progresividad de las evoluciones. No hay revoluciones aquí, de lo contrario estarás en problemas muy rápido.
Inserte una nueva clase de cpp sobre la clase principal original. Por ahora, básicamente redirigiría todas las llamadas a la clase principal actual, pero apuntaría a hacer que la API de esta nueva clase sea lo más clara y sucinta posible.
Una vez hecho esto, tiene la posibilidad de agregar nuevas funcionalidades en nuevas clases.
En cuanto a las funcionalidades existentes, debe moverlas progresivamente en nuevas clases a medida que se vuelven lo suficientemente estables. Perderá la ayuda de SCC para este código, pero no se puede hacer mucho al respecto. Simplemente elige el momento adecuado.
Sé que esto no es perfecto, aunque espero que pueda ayudar, ¡y el proceso debe adaptarse a sus necesidades!
Información Adicional
Tenga en cuenta que Git es un SCC que puede seguir fragmentos de código de un archivo a otro. He escuchado cosas buenas al respecto, por lo que podría ayudar mientras avanza progresivamente su trabajo.
Git se construye alrededor de la noción de blobs que, si entiendo correctamente, representan fragmentos de archivos de código. Mueva estas piezas en diferentes archivos y Git las encontrará, incluso si las modifica. Aparte del video de Linus Torvalds mencionado en los comentarios a continuación, no he podido encontrar algo claro sobre esto.
fuente
Confucio dice: "el primer paso para salir del hoyo es dejar de cavar hoyos".
fuente
Déjame adivinar: ¿Diez clientes con conjuntos de características divergentes y un gerente de ventas que promueve la "personalización"? He trabajado en productos como ese antes. Tuvimos esencialmente el mismo problema.
Reconoce que tener un archivo enorme es un problema, pero aún más problemas son las diez versiones que debe mantener "actualizadas". Eso es mantenimiento múltiple. SCC puede facilitarlo, pero no puede hacerlo bien.
Antes de intentar dividir el archivo en partes, debe volver a sincronizar las diez ramas para que pueda ver y dar forma a todo el código a la vez. Puede hacer esto una rama a la vez, probando ambas ramas contra el mismo archivo de código principal. Para hacer cumplir el comportamiento personalizado, puede usar #ifdef y amigos, pero es mejor usar lo normal si / de lo contrario contra constantes definidas. De esta manera, su compilador verificará todos los tipos y probablemente eliminará el código objeto "muerto" de todos modos. (Sin embargo, es posible que desee desactivar la advertencia sobre el código muerto).
Una vez que solo hay una versión de ese archivo compartida implícitamente por todas las ramas, es más fácil comenzar con los métodos tradicionales de refactorización.
Los #ifdefs son principalmente mejores para las secciones donde el código afectado solo tiene sentido en el contexto de otras personalizaciones por rama. Se puede argumentar que estos también presentan una oportunidad para el mismo esquema de fusión de ramas, pero no se vuelvan locos. Un proyecto colosal a la vez, por favor.
A corto plazo, el archivo parecerá crecer. Esto esta bien. Lo que estás haciendo es unir cosas que necesitan estar juntas. Luego, comenzará a ver áreas que son claramente las mismas independientemente de la versión; estos se pueden dejar solos o refactorizados a voluntad. Otras áreas diferirán claramente según la versión. Tienes varias opciones en este caso. Un método es delegar las diferencias a objetos de estrategia por versión. Otra es derivar versiones de cliente de una clase abstracta común. Pero ninguna de estas transformaciones es posible siempre que tenga diez "consejos" de desarrollo en diferentes ramas.
fuente
No sé si esto resuelve su problema, pero supongo que lo que quiere hacer es migrar el contenido del archivo a archivos más pequeños independientes entre sí (resumidos). Lo que también entiendo es que tienes alrededor de 10 versiones diferentes del software flotando y necesitas apoyarlas a todas sin estropear las cosas.
En primer lugar, no hay forma de que esto sea fácil y se resuelva solo en unos pocos minutos de lluvia de ideas. Las funciones vinculadas en su archivo son vitales para su aplicación, y simplemente cortarlas y migrarlas a otros archivos no salvará su problema.
Creo que solo tienes estas opciones:
No migres y quédate con lo que tienes. Posiblemente abandone su trabajo y comience a trabajar en software serio con un buen diseño además. La programación extrema no siempre es la mejor solución si está trabajando en un proyecto a largo plazo con fondos suficientes para sobrevivir a un choque o dos.
Elabore un diseño de cómo le gustaría que se vea su archivo una vez que se haya dividido. Cree los archivos necesarios e intégrelos en su aplicación. Cambie el nombre de las funciones o sobrecarguelas para tomar un parámetro adicional (¿tal vez solo un simple booleano?). Una vez que tenga que trabajar en su código, migre las funciones que necesita para trabajar al nuevo archivo y asigne las llamadas de función de las funciones antiguas a las nuevas funciones. Aún debe tener su archivo principal de esta manera, y aún así poder ver los cambios que se le hicieron, una vez que se trata de una función específica, sabe exactamente cuándo se subcontrató, etc.
Intente convencer a sus compañeros de trabajo con un buen pastel de que el flujo de trabajo está sobrevalorado y que necesita reescribir algunas partes de la aplicación para hacer negocios serios.
fuente
Exactamente este problema se maneja en uno de los capítulos del libro "Trabajando efectivamente con código heredado" ( http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 ).
fuente
Creo que sería mejor crear un conjunto de clases de comandos que se asignen a los puntos API de mainmodule.cpp.
Una vez que estén en su lugar, necesitará refactorizar la base de código existente para acceder a estos puntos API a través de las clases de comando, una vez hecho esto, puede refactorizar la implementación de cada comando en una nueva estructura de clases.
Por supuesto, con una sola clase de 11 KLOC, el código allí es probablemente muy acoplado y frágil, pero crear clases de comando individuales ayudará mucho más que cualquier otra estrategia de proxy / fachada.
No envidio la tarea, pero a medida que pasa el tiempo este problema solo empeorará si no se aborda.
Actualizar
Sugeriría que el patrón de Comando es preferible a una Fachada.
Es preferible mantener / organizar muchas clases de Comando diferentes en una Fachada (relativamente) monolítica. La asignación de una sola Fachada en un archivo 11 KLOC probablemente tendrá que dividirse en algunos grupos diferentes.
¿Por qué molestarse en tratar de descubrir estos grupos de fachadas? Con el patrón Command podrá agrupar y organizar estas clases pequeñas de forma orgánica, por lo que tiene mucha más flexibilidad.
Por supuesto, ambas opciones son mejores que el solo 11 KLOC y el archivo en crecimiento.
fuente
Un consejo importante: no mezcle refactorización y corrección de errores. Lo que desea es una versión de su programa que sea idéntica a la versión anterior, excepto que el código fuente es diferente.
Una forma podría ser comenzar a dividir la función / parte menos grande en su propio archivo y luego incluirlo con un encabezado (convirtiendo main.cpp en una lista de #incluidos, lo que suena a olor de código en sí mismo * No estoy un C ++ Guru sin embargo), pero al menos ahora está dividido en archivos).
A continuación, puede intentar cambiar todas las versiones de mantenimiento al "nuevo" main.cpp o sea cual sea su estructura. Nuevamente: no hay otros cambios o correcciones de errores porque rastrearlos es confuso como el infierno.
Otra cosa: por mucho que desee hacer un gran paso para refactorizar todo de una vez, puede morder más de lo que puede masticar. Tal vez solo elija una o dos "partes", introdúzcalas en todos los lanzamientos, luego agregue más valor para su cliente (después de todo, la refactorización no agrega valor directo, por lo que es un costo que debe justificarse) y luego elija otro una o dos partes
Obviamente, eso requiere cierta disciplina en el equipo para usar realmente los archivos divididos y no solo agregar cosas nuevas al main.cpp todo el tiempo, sino nuevamente, tratar de hacer un refactor masivo puede no ser el mejor curso de acción.
fuente
Rofl, esto me recuerda a mi antiguo trabajo. Parece que, antes de unirme, todo estaba dentro de un archivo enorme (también C ++). Luego lo han dividido (en puntos completamente aleatorios usando incluye) en aproximadamente tres (todavía archivos enormes). La calidad de este software fue, como era de esperar, horrible. El proyecto totalizó alrededor de 40k LOC. (que casi no contiene comentarios pero MUCHOS códigos duplicados)
Al final hice una reescritura completa del proyecto. Comencé rehaciendo la peor parte del proyecto desde cero. Por supuesto, tenía en mente una posible interfaz (pequeña) entre esta nueva parte y el resto. Luego inserté esta parte en el antiguo proyecto. No refactoré el código anterior para crear la interfaz necesaria, sino que lo reemplacé. Luego di pequeños pasos desde allí, reescribiendo el viejo código.
Tengo que decir que esto tomó aproximadamente medio año y no hubo desarrollo de la antigua base de código además de las correcciones de errores durante ese tiempo.
editar:
El tamaño se mantuvo en aproximadamente 40k LOC, pero la nueva aplicación contenía muchas más funciones y presumiblemente menos errores en su versión inicial que el software de 8 años. Una razón de la reescritura también fue que necesitábamos las nuevas características y era casi imposible introducirlas dentro del código anterior.
El software era para un sistema integrado, una impresora de etiquetas.
Otro punto que debo agregar es que, en teoría, el proyecto era C ++. Pero no era OO en absoluto, podría haber sido C. La nueva versión estaba orientada a objetos.
fuente
OK, por lo general, reescribir la API del código de producción es una mala idea como comienzo. Deben suceder dos cosas.
Primero, debe hacer que su equipo decida congelar el código en la versión de producción actual de este archivo.
Dos, debe tomar esta versión de producción y crear una rama que gestione las compilaciones utilizando directivas de preprocesamiento para dividir el archivo grande. Dividir la compilación usando las directivas de preprocesador JUST (#ifdefs, #includes, #endifs) es más fácil que recodificar la API. Definitivamente es más fácil para sus SLA y soporte continuo.
Aquí puede simplemente cortar las funciones que se relacionan con un subsistema en particular dentro de la clase y ponerlas en un archivo como mainloop_foostuff.cpp e incluirlo en mainloop.cpp en la ubicación correcta.
O
Una manera más lenta pero robusta sería diseñar una estructura de dependencias internas con doble indirección en la forma en que se incluyen las cosas. Esto le permitirá dividir las cosas y aún ocuparse de las codependencias. Tenga en cuenta que este enfoque requiere codificación posicional y, por lo tanto, debe combinarse con los comentarios apropiados.
Este enfoque incluiría componentes que se utilizan según la variante que está compilando.
La estructura básica es que su mainclass.cpp incluirá un nuevo archivo llamado MainClassComponents.cpp después de un bloque de declaraciones como el siguiente:
La estructura primaria del archivo MainClassComponents.cpp estaría allí para resolver las dependencias dentro de los subcomponentes de esta manera:
Y ahora para cada componente crea un archivo component_xx.cpp.
Por supuesto, estoy usando números, pero deberías usar algo más lógico basado en tu código.
El uso del preprocesador le permite dividir las cosas sin tener que preocuparse por los cambios de API, lo cual es una pesadilla en la producción.
Una vez que haya establecido la producción, puede trabajar en el rediseño.
fuente
Bueno, entiendo tu dolor :) He estado en algunos de esos proyectos también y no es bonito. No hay una respuesta fácil para esto.
Un enfoque que puede funcionar para usted es comenzar a agregar protecciones de seguridad en todas las funciones, es decir, verificar argumentos, condiciones previas / posteriores en los métodos, y luego agregar pruebas unitarias para capturar la funcionalidad actual de las fuentes. Una vez que tenga esto, estará mejor equipado para volver a factorizar el código porque aparecerá afirmaciones y errores que le alertarán si ha olvidado algo.
A veces, aunque hay momentos en que la refactorización solo puede traer más dolor que beneficio. Entonces puede ser mejor dejar el proyecto original y en un estado de pseudo mantenimiento y comenzar desde cero y luego agregar gradualmente la funcionalidad de la bestia.
fuente
No debe preocuparse por reducir el tamaño del archivo, sino por reducir el tamaño de la clase. Se reduce a casi lo mismo, pero te hace ver el problema desde un ángulo diferente (como sugiere @Brian Rasmussen , tu clase parece tener muchas responsabilidades).
fuente
Lo que tienes es un ejemplo clásico de un antipatrón de diseño conocido llamado blob . Tómese un tiempo para leer el artículo que señalo aquí, y tal vez pueda encontrar algo útil. Además, si este proyecto es tan grande como parece, debe considerar algún diseño para evitar convertirse en código que no puede controlar.
fuente
Esta no es una respuesta al gran problema, sino una solución teórica a una parte específica del mismo:
Averigua dónde quieres dividir el archivo grande en subarchivos. Ponga comentarios en algún formato especial en cada uno de esos puntos.
Escriba un script bastante trivial que separe el archivo en subarchivos en esos puntos. (Quizás los comentarios especiales tienen nombres de archivo incrustados que el script puede usar como instrucciones sobre cómo dividirlo). Debe conservar los comentarios como parte de la división.
Ejecute el script Eliminar el archivo original.
Cuando necesite fusionar desde una rama, primero vuelva a crear el archivo grande al concatenar las piezas nuevamente, realice la fusión y luego vuelva a dividirlo.
Además, si desea preservar el historial de archivos SCC, espero que la mejor manera de hacerlo sea decirle a su sistema de control de origen que los archivos de piezas individuales son copias del original. Luego conservará el historial de las secciones que se guardaron en ese archivo, aunque, por supuesto, también registrará que las partes grandes fueron "eliminadas".
fuente
Una forma de dividirlo sin demasiado peligro sería echar un vistazo histórico a todos los cambios de línea. ¿Hay ciertas funciones que son más estables que otras? Puntos calientes de cambio si lo desea.
Si una línea no ha cambiado en algunos años, probablemente pueda moverla a otro archivo sin demasiada preocupación. Echaría un vistazo a la fuente anotada con la última revisión que tocó una línea determinada y veré si hay alguna función que pueda realizar.
fuente
Wow, suena genial Creo que explicarle a tu jefe que necesitas mucho tiempo para refactorizar a la bestia vale la pena intentarlo. Si no está de acuerdo, dejar de fumar es una opción.
De todos modos, lo que sugiero es básicamente descartar toda la implementación y reagruparla en nuevos módulos, llamemos a esos "servicios globales". El "módulo principal" solo reenviará esos servicios y CUALQUIER código nuevo que escriba los usará en lugar del "módulo principal". Esto debería ser factible en un período de tiempo razonable (porque es principalmente copiar y pegar), no se rompe el código existente y puede hacerlo una versión de mantenimiento a la vez. Y si aún le queda tiempo, puede gastarlo refactorizando todos los módulos dependientes antiguos para que también utilicen los servicios globales.
fuente
Mis condolencias: en mi trabajo anterior me encontré con una situación similar con un archivo que era varias veces más grande que el que tenía que tratar. La solución fue:
Las clases que cree en el paso 3. Las iteraciones probablemente crecerán para absorber más código que sea apropiado para su nueva función clara.
También podría agregar:
0: compre el libro de Michael Feathers sobre cómo trabajar con código heredado
Desafortunadamente, este tipo de trabajo es muy común, pero mi experiencia es que hay un gran valor en hacer que el código de trabajo pero horrible sea incrementalmente menos horrible mientras lo mantiene funcionando.
fuente
Considere formas de reescribir toda la aplicación de una manera más sensata. Tal vez reescriba una pequeña sección como prototipo para ver si su idea es factible.
Si ha identificado una solución viable, refactorice la aplicación en consecuencia.
Si todos los intentos de producir una arquitectura más racional fallan, entonces al menos usted sabe que la solución probablemente sea redefinir la funcionalidad del programa.
fuente
Mis 0.05 céntimos de euro:
Rediseñe todo el desorden, divídalo en subsistemas teniendo en cuenta los requisitos técnicos y comerciales (= muchas pistas de mantenimiento paralelas con una base de código potencialmente diferente para cada una, obviamente existe la necesidad de una alta modificabilidad, etc.).
Al dividirse en subsistemas, analice los lugares que más han cambiado y separe los de las partes que no cambian. Esto debería mostrarte los puntos problemáticos. Separe las partes más cambiantes en sus propios módulos (por ejemplo, dll) de tal manera que la API del módulo pueda mantenerse intacta y no necesite romper BC todo el tiempo. De esta manera, puede implementar diferentes versiones del módulo para diferentes ramas de mantenimiento, si es necesario, sin modificar el núcleo.
El rediseño probablemente necesitará ser un proyecto separado, tratar de hacerlo a un objetivo en movimiento no funcionará.
En cuanto al historial del código fuente, mi opinión: olvídalo del nuevo código. Pero mantenga el historial en algún lugar para que pueda consultarlo, si es necesario. Apuesto a que no lo necesitarás tanto después del comienzo.
Lo más probable es que necesite obtener la aceptación de la administración para este proyecto. Quizás pueda discutir con un tiempo de desarrollo más rápido, menos errores, mantenimiento más fácil y menos caos general. Algo parecido a "Habilitar de forma proactiva la viabilidad de mantenimiento y seguridad en el futuro de nuestros activos de software críticos" :)
Así es como comenzaría a abordar el problema al menos.
fuente
Comience por agregarle comentarios. Con referencia a dónde se llaman las funciones y si puede mover las cosas. Esto puede hacer que las cosas se muevan. Realmente necesita evaluar qué tan frágil se basa el código. Luego mueva partes comunes de funcionalidad juntas. Pequeños cambios a la vez.
fuente
Otro libro que puede encontrar interesante / útil es Refactoring .
fuente
Algo que me parece útil hacer (y lo estoy haciendo ahora, aunque no a la escala que enfrenta), es extraer métodos como clases (refactorización de objetos de método). Los métodos que difieren entre las diferentes versiones se convertirán en diferentes clases que se pueden inyectar en una base común para proporcionar el comportamiento diferente que necesita.
fuente
Encontré esta oración como la parte más interesante de tu publicación:
> El archivo se usa y cambia activamente en varias (> 10) versiones de mantenimiento de nuestro producto, por lo que es muy difícil refactorizarlo
Primero, recomendaría que utilice un sistema de control de fuente para desarrollar estas versiones de mantenimiento 10+ que admitan la ramificación.
En segundo lugar, crearía diez ramas (una para cada una de sus versiones de mantenimiento).
¡Ya puedo sentirte encogido! Pero o su control de fuente no funciona para su situación debido a la falta de características, o no se está utilizando correctamente.
Ahora a la rama en la que trabaja, refactorícela como mejor le parezca, seguro sabiendo que no alterará las otras nueve ramas de su producto.
Me preocuparía un poco que tengas tanto en tu función main ().
En cualquier proyecto que escriba, usaría main () solo para realizar la inicialización de los objetos principales, como una simulación o un objeto de aplicación, estas clases es donde debe continuar el trabajo real.
También inicializaría un objeto de registro de aplicaciones en main para su uso global en todo el programa.
Finalmente, en main también agrego código de detección de fugas en bloques de preprocesador que aseguran que solo esté habilitado en las compilaciones DEBUG. Esto es todo lo que agregaría a main (). ¡Main () debe ser corto!
Tu dices eso
> El archivo contiene básicamente la "clase principal" (despacho y coordinación de trabajo interno principal) de nuestro programa
Parece que estas dos tareas podrían dividirse en dos objetos separados: un coordinador y un despachador de trabajo.
Cuando los divide, puede estropear su "flujo de trabajo SCC", pero parece que cumplir estrictamente con su flujo de trabajo SCC está causando problemas de mantenimiento de software. Deshazte de él ahora y no mires atrás, porque tan pronto como lo arregles, comenzarás a dormir tranquilo.
Si no puede tomar la decisión, luche con uñas y dientes con su gerente por ello, su aplicación debe ser refactorizada y, por lo que parece, ¡mal! ¡No aceptes un no por respuesta!
fuente
Como lo ha descrito, el problema principal es diferenciar entre la división previa y la división posterior, la combinación de correcciones de errores, etc. Herramienta a su alrededor. No tomará tanto tiempo codificar un script en Perl, Ruby, etc. para eliminar la mayor parte del ruido de la división previa y la concatenación de la división posterior. Haga lo que sea más fácil en términos de manejo del ruido:
Incluso podría hacerlo para que siempre que haya un registro, se ejecute la concatenación y tenga algo preparado para diferenciarse de las versiones de un solo archivo.
fuente
fuente
"El archivo contiene básicamente la" clase principal "(distribución y coordinación de trabajo interno principal) de nuestro programa, por lo que cada vez que se agrega una característica, también afecta a este archivo y cada vez que crece".
Si ese gran INTERRUPTOR (que creo que existe) se convierte en el principal problema de mantenimiento, puede refactorizarlo para usar el diccionario y el patrón de Comando y eliminar toda la lógica del interruptor del código existente al cargador, que llena ese mapa, es decir:
fuente
Creo que la forma más fácil de rastrear el historial de la fuente al dividir un archivo sería algo como esto:
fuente
Creo que lo que haría en esta situación es morder la bala y:
El seguimiento de los cambios antiguos en el archivo se resuelve simplemente con su primer comentario de registro que dice algo como "dividir desde mainmodule.cpp". Si necesita volver a algo reciente, la mayoría de las personas recordará el cambio, si es dentro de 2 años, el comentario les dirá dónde buscar. Por supuesto, ¿qué tan valioso será retroceder más de 2 años para ver quién cambió el código y por qué?
fuente