El gran proyecto en el que estoy trabajando durante un par de años ahora es una aplicación de control (y todo) de un dispositivo avanzado, corazón de su firmware.
El dispositivo es bastante avanzado, con más funcionalidades diferentes de las que podría decir de la memoria, y el 98% de ellas son manejadas por este enorme ejecutable. Por un lado, el programa es bastante fácil de mantener, bien modularizado en su interior, debidamente documentado, hay una separación razonable de funcionalidades por directorios y archivos, etc.
Pero al final, todo se agrupa en una aplicación que hace todo, desde la comunicación remota de la base de datos, el manejo de la pantalla táctil, el manejo de una docena de protocolos de comunicación diferentes, mediciones, varios algoritmos de control, captura de video, hora del amanecer y fecha de Pascua (en serio, y son ¡Necesario para propósitos muy serios!) ... En general, cosas que están muy poco relacionadas, a menudo solo relacionadas a través de algunos datos que se filtran entre algunos módulos lejanos.
Podría hacerse como varios ejecutables separados que se comunican entre sí, por ejemplo, a través de sockets, con un propósito más específico, tal vez cargados / descargados según sea necesario, y así sucesivamente. No hay una razón específica por la que se hace de esta manera.
Por un lado, funciona, y funciona bien. El proyecto es más simple, sin mantener la construcción de múltiples binarios. La estructura interna también es más fácil, cuando puedes llamar a un método o leer una variable en lugar de hablar por sockets o memoria compartida.
Pero por otro lado, el tamaño, la escala de esta cosa simplemente me asusta, se siente como pilotar Titanic. Siempre me enseñaron a modularizar, y agrupar todo en un archivo gigantesco se siente mal. Un problema que conozco es que una falla importante de un módulo (incluso insignificante) bloquea todos, pero la calidad del código asegura que esto realmente no ocurra en las versiones de lanzamiento. De lo contrario, la separación interna y la programación defensiva aseguran que esto seguirá funcionando correctamente, incluso si la mitad de los módulos internos falla normalmente por alguna razón.
¿Qué otros peligros pasé por alto? ¿Por qué esto me asusta? ¿Es esto solo miedo irracional a lo desconocido? ¿Hacer grandes proyectos serios de esta manera es una práctica aceptada? Calma mis miedos o dame una buena razón para refactorizar la versión 2.0 en múltiples binarios más pequeños.
Respuestas:
Excepto por el pequeño comentario al final (segundo, supervisando la CPU), podrías describir mi empresa. Sí, también necesitamos Pascua.
Bueno, estamos un poco más adelante. Dividimos el gran ejecutable e intentamos usar componentes estándar para bits estándar. No es exactamente la gran mejora que esperarías. De hecho, el rendimiento se está convirtiendo en un problema importante incluso en hardware más robusto. Y los costos de mantenimiento realmente no han bajado ahora que hay toneladas de código para serializar y sincronizar datos a través de interfaces estrechas.
¿La lección que aprendí? Tener un solo ejecutable es una solución bien probada para sistemas pequeños, y tenemos décadas de experiencia en su administración. Todas nuestras herramientas lo respaldan de forma nativa. La modularidad también se puede realizar dentro de un solo ejecutable, y cuando necesita comprometer la modularidad por otras razones, el truco sigue siendo pequeño.
fuente
Lo dijiste tú mismo: es bastante fácil de mantener, está bien modularizado ...
En mi opinión, y la mayoría de la gerencia estará de acuerdo conmigo en esto, los cambios nunca deben hacerse por el bien de los cambios. No hay nada de malo en este programa (su descripción), aparte de que no sigue las últimas tendencias de programación (que se mencionan en otras respuestas).
Sí, es un programa más grande ... muchos antes también lo han sido. También está bien documentado, por lo que todo lo que tiene que hacer es estudiar su organización interna y verá la lógica en ella. Dudo que todos los que lo escribieron antes que tú fueran incompetentes. Entonces, explora un poco, aprende ... después de eso, mantenlo como está hasta que aparezca una razón sólida para reescribirlo. Su gerente le estará agradecido por tener una buena relación costo / efecto.
Te asusta porque es grande, pero, de nuevo, nadie espera que lo aprendas de memoria en una semana. Así que trabaja, pieza por pieza, poco a poco, hasta que te acostumbres. Al final, aprenderá que probablemente esté bien organizado como está y cómo está organizado.
Si lo reescribiera, lograría lo mismo, pero al mismo tiempo lo arruinaría para todos aquellos que estaban acostumbrados a la organización anterior del código. De esta manera, solo tienes que "adaptarte".
fuente
Me sentiría mejor si dijeras que tienes todo envuelto en pruebas unitarias. Si no lo hace, debe crear un conjunto de pruebas antes de pensar en volver a diseñar la aplicación.
Tener un conjunto de pruebas mejorará su comprensión de la aplicación y le informará cuando un cambio en la aplicación rompa algo.
fuente
Si el código está bien modularizado con bajo acoplamiento y alta cohesión, se divide efectivamente. La sobrecarga de comunicación debe ser menor dentro de un proceso de lo que sería el caso con múltiples procesos.
Para sistemas embebidos, su único proceso puede eliminar la necesidad de implementar un O / S para ejecutar todos los procesos que estaría creando. También necesitará implementar un sistema para verificar que todos los procesos se estén ejecutando y reiniciarlos si es posible. Si no fuera posible, tendría que reaccionar en consecuencia.
Dividir el código en ejecutables separados también aumentaría el tamaño general del código fuente, ya que necesitaría implementar todo el código cliente / servidor entre los módulos. También necesitaría más casos de prueba para manejar este código, y casos en los que el proceso del servidor no estaba allí. Los grandes proyectos son grandes, eliminar las complejidades innecesarias, como parece haberse hecho aquí, ayuda a mantenerlos lo más pequeños posible.
Las pruebas son una especie de arenque rojo aquí, ya que los problemas seguirán ahí con o sin pruebas. Una buena prueba con cualquiera de las dos opciones de diseño ciertamente debería alentarse. Espero que las pruebas sean más simples con el código monolítico.
Forzar un bloqueo cuando sea necesario también es más fácil con un ejecutable monolítico.
fuente
Si tuviera que trabajar en una aplicación monolítica tan grande, las cosas que me asustarían son la falta de encapsulación, baja cohesión, alto acoplamiento y cómo probar todo esto. Los grandes monolitos a menudo son una invitación a abandonar la arquitectura adecuada y comenzar el descenso hacia una gran bola de barro .
Una vez que eso sucede, las pruebas se convierten en una pesadilla y estás en el camino de la obsolescencia.
Sin embargo, si su código está bien estructurado y bien mantenido, y se respetan los principios de alta cohesión, bajo acoplamiento y encapsulación adecuada, entonces veo pocas razones para refactorizar esto en unidades más pequeñas.
Sin embargo, sí quieres tener buenas pruebas.
fuente
Idealmente, la respuesta es sí. Tener múltiples archivos binarios tiene el potencial de hacerlo más estable ...
... sin embargo, también mencionó que el código dentro de la base de código monolítico está bien escrito y modularizado, lo que significa que no está lidiando con un gran lío enredado, solo una gran base de código ...
En general, diría que el dicho aplicable es: "No dejes que lo perfecto sea enemigo de lo bueno". Si bien creo que tiene razón al decir que una base de código monolítico es mala, también siento que no vale la pena preocuparse por ello.
Sería razonable avanzar colocando nuevas piezas de código en sus propios binarios, pero retroceder y refactorizar probablemente no valga la pena.
fuente
Si está utilizando un solo proceso, existe la posibilidad de que un puntero comodín de uno de sus submódulos corrompa una pieza crítica de memoria (y bloquee todo).
Si está utilizando diferentes procesos, al menos no está utilizando el mismo montón o pila de llamadas, y también podría ser más fácil reiniciar un solo subproceso.
Repartir todo en múltiples procesos también lo obligará a limpiar el diseño y evitar que cada módulo comience a usar métodos internos de otros módulos.
Dicho esto, probablemente sea una tarea desalentadora.
Lo que podría comenzar a hacer es decidir que cada módulo nuevo debe ser un proceso aislado.
fuente
Cuando entro en el reino donde el proyecto es demasiado grande para asimilarlo todo de una vez, construyo una tabla para colgar en la pared que presenta todas las secciones grandes y cómo encajan. Luego, cuando estoy trabajando, puedo hacer referencia al módulo en el que me estoy centrando y tener un recordatorio visual de cómo encaja en el todo.
Es muy posible que las complejidades y los peligros de mantener un sistema de compilación y control de versiones para múltiples binarios desconectados superen con creces su inquietud con un proyecto tan grande y monolítico.
fuente