Perigos de enorme aplicação monolítica


20

O grande projeto no qual estou trabalhando há alguns anos é uma aplicação de controle (e tudo) de um dispositivo avançado, o coração de seu firmware.

O dispositivo é bastante avançado, com mais funcionalidades diferentes do que eu poderia dizer da memória, e 98% delas são tratadas por esse grande executável. Por um lado, o programa é de fácil manutenção, bem modularizado por dentro, devidamente documentado, há uma separação razoável de funcionalidades por diretórios e arquivos e assim por diante.

Mas, no final, tudo fica agrupado em um aplicativo que faz de tudo, desde comunicação remota ao banco de dados, manipulação da tela sensível ao toque, manipulação de uma dúzia de vários protocolos de comunicação, medições, vários algoritmos de controle, captura de vídeo, hora do nascer do sol e data da páscoa (sério, e são necessário para propósitos muito sérios!) ... Em geral, coisas que são muito pouco relacionadas, geralmente relacionadas apenas através de alguns dados que gotejam entre alguns módulos distantes.

Isso pode ser feito como vários executáveis ​​separados que se comunicam, por exemplo, por soquetes, com finalidade mais específica, talvez carregados / descarregados conforme necessário, e assim por diante. Nenhuma razão específica para que seja feito dessa maneira.

Por um lado, funciona e funciona bem. O projeto é mais simples, sem manter a construção de vários binários. A estrutura interna também é mais fácil, quando você pode simplesmente chamar um método ou ler uma variável em vez de falar sobre soquetes ou memória compartilhada.

Mas por outro lado, o tamanho, a escala dessa coisa me assusta, parece pilotar o Titanic. Eu sempre fui ensinado a modularizar, e agrupar tudo em um arquivo gigantesco parece errado. Um problema que conheço é uma falha pesada de um módulo (mesmo insignificante) que trava tudo - mas a qualidade do código garante que isso realmente não ocorra nas versões de lançamento. Caso contrário, a separação interna e a programação defensiva garantem que isso ainda funcione corretamente, mesmo que metade dos módulos internos falhe normalmente por algum motivo.

Que outros perigos eu negligenciei? Por que isso me assusta? Isso é apenas medo irracional do desconhecido? Fazer grandes projetos sérios dessa maneira é uma prática aceita? Acalme meus medos ou me dê um bom motivo para refatorar a versão 2.0 em vários binários menores.


7
Se o sol nascer muito cedo, a Inglaterra flutuará para o mar.
Maxpm 13/07

1
Ou a pressão no núcleo da Terra aumenta um pouco.
StuperUser

1
@Maxpm eu vejo o que você fez lá ... xkcd.com/687
teto

3
@ Maxpm: Felizmente, nosso aplicativo não gera nascer e pôr do sol. Confiamos que a princesa Celestia não falhará em seu trabalho.
SF.

1
@rwong: 1. O dispositivo está parado para atualizações. Embora ele sobreviva a atualizações em tempo real, não queremos resultados inesperados, caso a atualização dê errado por qualquer motivo. 2. ARM9 de núcleo único com Linux incorporado. Há um segundo, supervisionando a CPU em execução sem SO, verificando se a CPU principal produz uma saída sã.
SF.

Respostas:


5

Exceto pelo pequeno comentário no final (segundo, supervisionando a CPU), você poderia estar descrevendo minha empresa. Sim, precisamos da Páscoa também.

Bem, estamos um pouco mais adiante. Dividimos o grande executável e tentamos usar componentes padrão para bits padrão. Não é exatamente a grande melhoria que você esperaria. De fato, o desempenho está se tornando uma grande dor, mesmo em hardware mais robusto. E os custos de manutenção não diminuíram agora, pois há muito código para serializar e sincronizar dados em interfaces estreitas.

A lição que aprendi? Ter um único executável é uma solução comprovada para pequenos sistemas, e temos décadas de experiência em gerenciá-lo. Todas as nossas ferramentas suportam isso nativamente. A modularidade também pode ser feita em um único executável, e quando você precisa comprometer a modularidade por outros motivos, o hack permanece pequeno.


Obrigado - nenhuma entrada de "melhores práticas a serem seguidas / sacrificadas" e de "ponderação de possíveis benefícios versus possíveis desvantagens" é tão útil quanto alguém com experiência em primeira mão do outro lado da cerca.
SF.

23

O dispositivo é bastante avançado, com mais funcionalidades diferentes do que eu poderia dizer da memória, e 98% delas são tratadas por esse grande executável. Por um lado, o programa é de fácil manutenção, bem modularizado por dentro, devidamente documentado, há uma separação razoável de funcionalidades por diretórios e arquivos e assim por diante.

Você mesmo disse: é bastante sustentável, bem modularizado ...

Na minha opinião, e a maior parte da gerência concordará comigo em relação a isso, nunca devem ser feitas alterações em troca de mudanças. Não há nada errado com este programa (sua descrição) além de não seguir as últimas tendências de programação (mencionadas em outras respostas).

Sim, é um programa maior ... muitos antes também. Também está bem documentado; portanto, tudo o que você precisa fazer é estudar sua organização interna e verá a lógica nela. Duvido que todos aqueles antes de você que o escreveram fossem incompetentes. Então, explore um pouco, aprenda ... depois disso, mantenha-o como está até que uma razão sólida para reescrevê-lo apareça. Seu gerente ficará agradecido por ter uma boa relação custo / efeito.

Que outros perigos eu negligenciei? Por que isso me assusta? Isso é apenas medo irracional do desconhecido? Fazer grandes projetos sérios dessa maneira é uma prática aceita? Acalme meus medos ou me dê um bom motivo para refatorar a versão 2.0 em vários binários menores.

Isso o assusta porque é grande - mas, novamente, ninguém espera que você aprenda tudo de cor em uma semana. Então trabalhe, peça por peça, pouco a pouco, até você pegar o jeito. No final, você aprenderá que provavelmente está bem organizado e como está organizado.

Se você reescrevê-lo, faria o mesmo, mas ao mesmo tempo o arruinaria para todos aqueles que estavam acostumados à organização anterior do código. Dessa forma, somente você precisa "se adaptar".


16

Eu me sentiria melhor se você dissesse que tinha tudo envolto em testes de unidade. Caso contrário, você deve criar um conjunto de testes antes de pensar em re-projetar o aplicativo.

Ter um conjunto de testes melhorará sua compreensão do aplicativo e informará quando uma alteração no aplicativo quebra alguma coisa.


2
Mas isso se aplica às duas arquiteturas possíveis ...
SF.

3
Sim, ele faz ... #
911 Robert Harvey

10
... e, portanto, esta resposta não adiciona nada à discussão, exceto para promover o teste de unidade.
benzado

2
@benzado: Que, no cenário do OP, é de extrema importância.
Robert Harvey

2
@ironcode: dê uma olhada em um livro como "Trabalhando efetivamente com o código herdado". A primeira coisa que ele diz nesse livro é "Se você ainda não possui um conjunto de testes de unidade com alta cobertura de código, escreva-os primeiro antes de fazer qualquer outra coisa " . Eu diria que o conselho é mais do que relevante aqui, dado que o OP perguntou especificamente "Que outros perigos eu negligenciei".
Robert Harvey

8

Se o código for bem modularizado com baixo acoplamento e alta coesão, ele será efetivamente particionado. A sobrecarga de comunicação deve ser menor dentro de um processo do que seria o caso de vários processos.

Para sistemas embarcados, seu único processo pode eliminar a necessidade de implementar um sistema operacional para executar todos os processos que você criaria. Você também precisaria implementar um sistema para verificar se todos os processos estavam em execução e reiniciá-los, se possível. Se não fosse possível, você precisaria reagir de acordo.

Dividir o código em executáveis ​​separados também aumentaria o tamanho geral do código-fonte, pois seria necessário implementar todo o código do cliente / servidor entre os módulos. Você também precisaria de mais casos de teste para manipular esse código e casos em que o processo do servidor não estava lá. Os grandes projetos são grandes, eliminando complexidades desnecessárias, como parece ter sido feito aqui, ajuda a mantê-los o menor possível.

Os testes são uma espécie de arenque vermelho aqui, pois os problemas ainda estarão lá com ou sem testes. Um bom teste com qualquer uma das opções de design certamente deve ser incentivado. Eu espero, testar é mais simples com o código monolítico.

Forçar uma falha quando necessário também é mais fácil com um executável monolítico.


1
+1, a engenharia é sobre compensações, binários menores têm um custo, também
benzado

+1 Eu concordo plenamente. Não há motivo específico para justificar vários executáveis. A comunicação e a coordenação entre os executáveis ​​têm seu preço.
Erick Robertson

+1: Se não estiver quebrado, não conserte.
Bob Murphy

1

Se eu trabalhasse em uma aplicação monolítica tão grande, as coisas que me assustariam são falta de encapsulamento, baixa coesão, alto acoplamento e como testar tudo isso. Grandes monólitos costumam ser um convite para abandonar a arquitetura adequada e iniciar a descida em uma grande bola de barro .

Quando isso acontece, o teste se torna um pesadelo e você está no caminho da obsolescência.

No entanto, se seu código é bem estruturado e bem mantido, e os princípios de alta coesão, baixo acoplamento e encapsulamento adequado são respeitados, vejo poucas razões para refatorá-lo em unidades menores.

Você deseja ter bons testes, no entanto.


1

Idealmente, a resposta é sim. Ter vários binários tem o potencial de torná-lo mais estável ....

... no entanto, você também mencionou que o código na base de código monolítico é bem escrito e modularizado, o que significa que você não está lidando com uma enorme confusão emaranhada, apenas uma enorme base de código ...

No geral, eu diria que o ditado aplicável é: "Não deixe o perfeito ser o inimigo do bem". Embora eu ache que você está correto ao dizer que uma base de código monolítica é ruim, também sinto que não vale a pena se preocupar com isso.

Seria razoável avançar colocando novos trechos de código em seus próprios binários, mas voltar e refatorar provavelmente não vale a pena.


1

Se você estiver usando um único processo, há alguma chance de um ponteiro selvagem de um de seu submódulo corromper uma parte crítica da memória (e travar a coisa toda).

Se você estiver usando processos diferentes, pelo menos não estará usando o mesmo heap ou pilha de chamadas, e também poderá ser mais fácil reiniciar um único subprocesso.

A estrutura interna também é mais fácil, quando você pode simplesmente chamar um método ou ler uma variável em vez de falar sobre soquetes ou memória compartilhada.

Redividir tudo em vários processos também forçará você a limpar o design e evitar que cada módulo comece a usar métodos internos de outros módulos.

Dito isto, é provavelmente uma tarefa assustadora.

O que você pode começar a fazer é decidir que todo novo módulo deve ser um processo isolado.


Você está assumindo muita coisa sobre o hardware de tempo de execução e o ambiente do SO, especialmente quando há firmware e dispositivos incorporados.
Patrick Hughes

0

Quando entro no reino em que o projeto é grande demais para grudar de uma só vez, construo um gráfico para pendurar na parede que mostra todas as grandes seções e como elas se encaixam. Então, quando estou trabalhando, posso referenciar o módulo em que estou focando e ter um lembrete visual de como ele se encaixa no todo.

É bem possível que as complexidades e os perigos de manter um sistema de criação e versão para vários binários desconectados superem em muito sua inquietação com um projeto tão grande e monolítico.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.