Peligros de gran aplicación monolítica.


20

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.


77
Si el sol sale demasiado temprano, Inglaterra se irá al mar.
Maxpm

1
O la presión en el núcleo de la Tierra aumenta ligeramente.
StuperUser

1
@Maxpm veo lo que hizo allí ... xkcd.com/687
teto

3
@Maxpm: Afortunadamente, nuestra aplicación no genera amaneceres y puestas de sol. Confiamos en que la Princesa Celestia no falle en su trabajo.
SF.

1
@rwong: 1. El dispositivo se detiene para las actualizaciones. Si bien sobreviviría a las actualizaciones sobre la marcha, no queremos ningún resultado inesperado en caso de que la actualización salga mal por cualquier motivo. 2. ARM9 de un solo núcleo con Linux incorporado. Hay una segunda CPU de supervisión que se ejecuta sin SO, verificando que la CPU principal produzca una salida sensata.
SF.

Respuestas:


5

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.


Gracias: las entradas de "mejores prácticas a seguir / sacrificadas" y "ponderar los beneficios potenciales frente a las desventajas potenciales" son tan útiles como alguien con experiencia de primera mano desde el otro lado de la cerca.
SF.

23

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.

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.

¿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.

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".


16

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.


2
Pero eso se aplica a ambas arquitecturas posibles ...
SF.

3
Sí, lo hace ...
Robert Harvey

10
... y, por lo tanto, esta respuesta no agrega nada a la discusión, excepto para promover las pruebas unitarias.
benzado

2
@benzado: lo cual, en el escenario del OP, es de suma importancia.
Robert Harvey

2
@ironcode: Eche un vistazo a un libro como "Trabajar eficazmente con código heredado". Lo primero que dice en ese libro es: "Si no tiene un conjunto de pruebas unitarias con una alta cobertura de código, escríbalas primero, antes de hacer cualquier otra cosa " . Diría que el consejo es más que relevante aquí, dado que el OP específicamente preguntó "¿Qué otros peligros he pasado por alto?"
Robert Harvey

8

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.


1
1, la ingeniería está a punto de compensaciones, los binarios más pequeños tienen un costo, también
benzado

+1 Estoy totalmente de acuerdo. No hay una razón específica dada para garantizar múltiples ejecutables. La comunicación y coordinación entre ejecutables tiene su precio.
Erick Robertson

+1: Si no está roto, no lo arregles.
Bob Murphy

1

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.


1

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.


1

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.

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.

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.


Está asumiendo muchísimo sobre el hardware de tiempo de ejecución y el entorno del sistema operativo, especialmente cuando están involucrados firware y dispositivos integrados.
Patrick Hughes el

0

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.