Dividir um aplicativo potencialmente monolítico em vários aplicativos menores ajuda a evitar bugs? [fechadas]


48

Outra maneira de perguntar isso é; por que os programas tendem a ser monolíticos?

Estou pensando em algo como um pacote de animação como o Maya, que as pessoas usam para vários fluxos de trabalho diferentes.

Se os recursos de animação e modelagem fossem divididos em seu próprio aplicativo separado e desenvolvidos separadamente, com arquivos sendo passados ​​entre eles, eles não seriam mais fáceis de manter?


9
If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?Não misture mais fácil estender com mais fácil manter um módulo - por si - não está livre de complicações ou desenhos dúbios. O Maya pode ser o inferno na terra para manter enquanto seus plugins não o são. Ou vice-versa.
Laiv 12/03

37
Vou acrescentar que um único programa monolítico tende a ser mais fácil de vender , e mais fácil para a maioria das pessoas para usar .
DarthFennec

2
@DarthFennec Os melhores aplicativos parecem um aplicativo para o usuário, mas utilizam tudo o que é necessário. Quantos microsserviços alimentam os vários sites que você visita? Quase nenhum deles é mais monólito!
corsiKa 12/03

23
@corsiKa Geralmente, não há nada a ganhar escrevendo um aplicativo de desktop como vários programas que se comunicam sob o capô, que não é obtido apenas escrevendo vários módulos / bibliotecas e vinculando-os em um binário monolítico. Os microsserviços atendem inteiramente a uma finalidade diferente, pois permitem que um único aplicativo seja executado em vários servidores físicos, permitindo que o desempenho seja escalado com a carga.
DarthFennec

5
@corsiKa - Eu acho que esse número esmagador de sites que eu uso ainda são monólitos. Afinal, a maior parte da internet é executada no Wordpress.
Davor Ždralo 12/03

Respostas:


94

Sim. Geralmente, duas aplicações menores e menos complexas são muito mais fáceis de manter do que uma única grande.

No entanto, você recebe um novo tipo de bug quando todos os aplicativos trabalham juntos para atingir uma meta. Para que eles trabalhem juntos, eles precisam trocar mensagens e essa orquestração pode dar errado de várias maneiras, mesmo que todos os aplicativos funcionem perfeitamente. Ter um milhão de aplicativos minúsculos tem seus próprios problemas especiais.

Um aplicativo monolítico é realmente a opção padrão que você cria quando adiciona mais e mais recursos a um único aplicativo. É a abordagem mais fácil quando você considera cada recurso por conta própria. Só depois que cresce, você pode olhar para o todo e dizer "sabe o que, isso funcionaria melhor se separássemos X e Y".


6
Sim, e também existem considerações de desempenho, por exemplo, o custo de passar um ponteiro contra os dados de serialização.
JimmyJames 12/03

65
"Geralmente, 2 aplicativos menores e menos complexos são muito mais fáceis de manter do que um único grande". - isso é verdade, exceto quando não é. Depende muito de onde e como esses dois aplicativos precisam interagir entre si.
Doc Brown

10
"Geralmente 2 aplicações menores e menos complexas são muito mais fáceis de manter do que uma única grande." Acho que vou querer mais explicações para isso. Por que exatamente o processo de gerar dois em vez de um executável a partir de uma base de código magicamente tornaria o código mais fácil? O que decide como o código é fácil de raciocinar, é o quão fortemente acoplado ele é e coisas semelhantes. Mas essa é uma separação lógica e não tem nada a ver com a física .
Voo

11
@Ew A separação física não força uma separação lógica, esse é o problema. Posso projetar facilmente um sistema em que dois aplicativos separados estejam intimamente acoplados. Certamente, há alguma correlação envolvida aqui, já que as pessoas que gastam tempo para separar um aplicativo provavelmente são competentes o suficiente para considerar essas coisas, mas há poucas razões para assumir qualquer causa . Pela mesma lógica, posso afirmar que o uso da versão mais recente do C # facilita muito a manutenção do código, pois o tipo de equipe que se mantém atualizado com suas ferramentas provavelmente também se preocupará com a manutenção do código.
Voo

9
Acho que a discussão aqui pode ser resumida com 2 declarações: 1) A divisão de um aplicativo em si não torna um aplicativo mais sustentável - pelo contrário, fornece outro possível ponto de falha 2) A divisão de um aplicativo obriga a pensar em onde dividir , o que fornece uma vantagem em comparação com um monólito em que isso nunca foi feito.
R. Schmitz

51

A divisão de um aplicativo potencialmente monolítico em vários aplicativos menores ajuda a evitar bugs

As coisas raramente são tão simples na realidade.

Dividir definitivamente não ajuda a evitar esses erros em primeiro lugar. Às vezes, pode ajudar a encontrar erros mais rapidamente. Um aplicativo que consiste em componentes pequenos e isolados pode permitir testes mais individuais (tipo de "unidade" -) para esses componentes, o que às vezes facilita a identificação da causa raiz de certos bugs e, portanto, corrige-os mais rapidamente.

Contudo,

  • mesmo um aplicativo que parece ser monolítico do lado de fora pode consistir em muitos componentes testáveis ​​por unidade, portanto, o teste de unidade não é necessariamente mais difícil para um aplicativo monolítico

  • como Ewan já mencionou, a interação de vários componentes introduz riscos e erros adicionais. Depurar um sistema de aplicativos com comunicação complexa entre processos pode ser significativamente mais difícil do que depurar um aplicativo de processo único

Isso depende também de quão bem um aplicativo maior pode se dividir em componentes, e quão amplas são as interfaces entre os componentes e como essas interfaces são usadas.

Em resumo, isso geralmente é uma troca, e nada onde uma resposta "sim" ou "não" esteja correta em geral.

por que os programas tendem a ser monolíticos

Eles? Olhe ao seu redor, existem milhões de aplicativos da Web no mundo que não me parecem muito monolíticos, pelo contrário. Também existem muitos programas disponíveis que fornecem um modelo de plug-in (o AFAIK até o software Maya que você mencionou).

eles não seriam mais fáceis de manter

A "manutenção mais fácil" geralmente vem do fato de que diferentes partes de um aplicativo podem ser desenvolvidas mais facilmente por equipes diferentes, para uma carga de trabalho melhor distribuída, equipes especializadas com foco mais claro e assim por diante.


4
Na sua última frase, a lei de Conway diz que a estrutura do sistema tende a imitar a organização. estrutura: os desenvolvedores / equipes estão mais familiarizados com algumas partes do que outras, portanto, embora as correções / melhorias devam ocorrer na parte mais relevante, pode ser mais fácil para um desenvolvedor invadir as partes "suas" em vez de (a) aprender como isso outra parte funciona ou (b) trabalha com alguém mais familiarizado com essa parte. Isso está relacionado às "costuras" mencionadas pelo @TKK e a quão difícil pode ser encontrar e aplicar as "corretas" / simples.
Warbo 13/03

38

Vou ter que discordar da maioria neste caso. Dividir um aplicativo em dois separados não facilita o código ou a manutenção de código.

Separar o código em dois executáveis ​​apenas altera a estrutura física do código, mas não é isso que é importante. O que decide o quão complexa é uma aplicação é o quão fortemente acopladas são as diferentes partes que a compõem. Esta não é uma propriedade física, mas lógica .

Você pode ter um aplicativo monolítico que tenha uma clara separação de diferentes preocupações e interfaces simples. Você pode ter uma arquitetura de microsserviço que se baseia nos detalhes de implementação de outros microsserviços e está fortemente associada a todos os outros.

O que é verdade é que o processo de como dividir um aplicativo grande em aplicativos menores é muito útil ao tentar estabelecer interfaces e requisitos claros para cada parte. No DDD, fale que seria criar seus contextos limitados. Mas se você cria muitos aplicativos minúsculos ou um grande que possui a mesma estrutura lógica é mais uma decisão técnica.


Mas e se alguém pegar um aplicativo da área de trabalho com vários modos de edição e criar apenas um aplicativo da área de trabalho para cada modo que um usuário abriria individualmente, em vez de ter interface. Isso não eliminaria uma quantidade não trivial de código dedicado à produção do "recurso" de "usuário pode alternar entre modos de edição"?
The Great Duck

3
@TheGreatDuck Parece que isso também eliminaria uma quantidade não trivial de usuários que não gostam de ter que alternar entre aplicativos diferentes. ;) Mas sim, a eliminação de recursos geralmente leva a um código mais simples. Elimine a verificação ortográfica e você removerá a possibilidade de ter erros de verificação ortográfica. Isso raramente é feito porque o recurso foi adicionado porque alguém o desejava.
Odalrick 13/03

11
@TheGreatDuck Certamente o design do UX deve vir antes de qualquer decisão arquitetural. Não faz sentido ter a melhor arquitetura projetada se ninguém usar o seu programa. Primeiro, decida o que você deseja criar e com base na decisão sobre os detalhes técnicos. Se dois aplicativos separados forem preferidos, vá em frente. Você ainda pode compartilhar muito código através de bibliotecas compartilhadas.
Voo

É realmente verdade que a complexidade do sistema se deve ao acoplamento rígido das peças? Eu gostaria de dizer que a complexidade total aumenta se você particionar seu sistema ao introduzir indireto e comunicação, embora a complexidade dos componentes individuais específicos esteja isolada em um estado limitado de complexidade mais limitada.
Alex

11
@TheGreatDuck A suposição subjacente aqui era que os sistemas têm algo em comum e, na verdade, precisam se comunicar de uma maneira ou de outra. Não acho que o OP estivesse perguntando se dois aplicativos completamente diferentes agrupados por algum motivo estranho seriam mais fáceis de manter se separados. Parece um caso estranho que geralmente não aparece na prática (embora eu tenha certeza de que alguém em algum lugar fez isso).
Voo 16/03

15

Mais fácil de manter depois de terminar de dividi-los, sim. Mas dividi-los nem sempre é fácil. Tentar dividir um pedaço de um programa em uma biblioteca reutilizável revela onde os desenvolvedores originais não conseguiram pensar onde deveriam estar as costuras . Se uma parte do aplicativo estiver atingindo outra parte do aplicativo, pode ser difícil corrigi-lo. Rasgar as costuras obriga a definir as APIs internas de forma mais clara, e é isso que facilita a manutenção da base de códigos. Reutilização e manutenção são produtos de costuras bem definidas.


ótimo post. Eu acho que um exemplo clássico / canônico do que você fala é uma aplicação GUI. muitas vezes um aplicativo GUI é um programa e o back-end / front-end é fortemente associado. À medida que o tempo passa, surgem problemas ... como se alguém precisasse usar o back-end, mas não pode porque está vinculado ao front-end. ou o processamento de back-end leva muito tempo e atola o front-end. geralmente o único aplicativo GUI dividido em dois programas: um é o GUI de front-end e o outro é um back-end.
Trevor Boyd Smith

13

É importante lembrar que correlação não é causalidade.

Construir um monólito grande e depois dividi-lo em várias partes pequenas pode ou não levar a um bom design. ( Pode melhorar o design, mas não é garantido.)

Mas um bom design geralmente leva à construção de um sistema como várias pequenas partes, em vez de um grande monólito. (Um monólito pode ser o melhor design, é muito menos provável que seja.)

Por que as peças pequenas são melhores? Porque eles são mais fáceis de raciocinar. E se for fácil argumentar sobre a correção, é mais provável que você obtenha um resultado correto.

Para citar o CAR Hoare:

Existem duas maneiras de construir um design de software: uma maneira é tornar tão simples que obviamente não há deficiências, e a outra maneira é torná-lo tão complicado que não há deficiências óbvias .

Se for esse o caso, por que alguém criaria uma solução desnecessariamente complicada ou monolítica? Hoare fornece a resposta na próxima frase:

O primeiro método é muito mais difícil.

E mais tarde na mesma fonte (a Palestra do Prêmio Turing de 1980):

O preço da confiabilidade é a busca da maior simplicidade. É um preço que os muito ricos acham mais difícil de pagar.


6

Esta não é uma pergunta com resposta sim ou não. A questão não é apenas a facilidade de manutenção, mas também o uso eficiente de habilidades.

Geralmente, um aplicativo monolítico bem escrito é eficiente. A comunicação entre processos e entre dispositivos não é barata. Quebrar um único processo diminui a eficiência. No entanto, a execução de tudo em um único processador pode sobrecarregar o processador e diminuir o desempenho. Esse é o problema básico de escalabilidade. Quando a rede entra em cena, o problema fica mais complicado.

Um aplicativo monolítico bem escrito que pode operar eficientemente como um único processo em um único servidor pode ser fácil de manter e manter livre de defeitos, mas ainda assim não é um uso eficiente das habilidades de codificação e arquitetura. A primeira etapa é dividir o processo em bibliotecas que ainda são executadas como o mesmo processo, mas são codificadas independentemente, seguindo disciplinas de coesão e acoplamento flexível. Um bom trabalho nesse nível melhora a capacidade de manutenção e raramente afeta o desempenho.

O próximo estágio é dividir o monólito em processos separados. Isso é mais difícil porque você entra em território complicado. É fácil introduzir erros de condição de corrida. A sobrecarga de comunicação aumenta e você deve ter cuidado com "interfaces de conversação". As recompensas são ótimas porque você rompe uma barreira de escalabilidade, mas o potencial de defeitos também aumenta. Os aplicativos com vários processos são mais fáceis de manter no nível do módulo, mas o sistema geral é mais complicado e difícil de solucionar. As correções podem ser diabolicamente complicadas.

Quando os processos são distribuídos para servidores separados ou para uma implementação no estilo de nuvem, os problemas ficam mais difíceis e as recompensas maiores. A escalabilidade aumenta. (Se você está considerando uma implementação em nuvem que não gera escalabilidade, pense bem.) Mas os problemas que entram nesse estágio podem ser incrivelmente difíceis de identificar e pensar.


4

Não . isso não facilita a manutenção. Se alguma coisa bem-vinda a mais problemas.

Por quê?

  • Os programas não são ortogonais, eles precisam preservar o trabalho uns dos outros na medida do razoável, o que implica um entendimento comum.
  • Muitos códigos dos dois programas são idênticos. Você está mantendo uma biblioteca compartilhada comum ou duas cópias separadas?
  • Agora você tem duas equipes de desenvolvimento. Como eles estão se comunicando?
  • Agora você tem dois produtos que precisam:

    • um estilo de interface do usuário comum, mecanismos de interação, etc ... Então agora você tem problemas de design. (Como as equipes de desenvolvimento estão se comunicando novamente?)
    • compatibilidade com versões anteriores (o modelador v1 pode ser importado para o animador v3?)
    • a integração de nuvem / rede (se for um recurso) agora precisa ser atualizada com o dobro de produtos.
  • Agora você tem três mercados de consumo: modeladores, animadores e modeladores de animadores

    • Eles terão prioridades conflitantes
    • Eles terão necessidades conflitantes de apoio
    • Eles terão estilos de uso conflitantes
  • Os Modeller Animators precisam abrir dois aplicativos separados para trabalhar no mesmo arquivo? Existe um terceiro aplicativo com ambas as funções, um aplicativo carrega as funções do outro?
  • etc ...

Dito isto, bases de código menores são igualmente fáceis de manter no nível do aplicativo, você simplesmente não vai receber um almoço grátis. Esse é o mesmo problema no coração do Micro-Service / Any-Modular-Architecture. Não é uma panacéia, a dificuldade de manutenção no nível do aplicativo é trocada por dificuldades de manutenção no nível da orquestração. Esses problemas ainda são problemas, eles simplesmente não estão mais na base de código, precisam ser evitados ou resolvidos.

Se a solução do problema no nível de orquestração é mais simples, resolva-o em cada nível de aplicativo, faz sentido dividi-lo em duas bases de código e lidar com os problemas de orquestração.

Caso contrário, não, apenas não faça isso, você seria melhor atendido ao melhorar a modularidade interna do próprio aplicativo. Envie seções do código para bibliotecas coesas e fáceis de manter, nas quais o aplicativo atua como um plug-in. Afinal, um monólito é apenas a camada de orquestração de uma paisagem de biblioteca.


3

Havia muitas respostas boas, mas como há quase uma cisão, eu também jogarei meu chapéu no ringue.

Na minha experiência como engenheiro de software, achei que esse não era um problema simples. Realmente depende do tamanho , escala e finalidade do aplicativo. As aplicações mais antigas em virtude da inércia necessária para alterá-las são geralmente monolíticas, pois essa era uma prática comum por muito tempo (o Maya se qualificaria nessa categoria). Suponho que você esteja falando sobre aplicativos mais recentes em geral.

Em aplicações pequenas o suficiente, que são mais ou menos uma preocupação, a sobrecarga necessária para manter muitas partes separadas geralmente excede a utilidade de ter a separação. Se puder ser mantido por uma pessoa, provavelmente poderá ser monolítico sem causar muitos problemas. A exceção a essa regra é quando você tem muitas partes diferentes (um front-end, back-end, talvez algumas camadas de dados no meio) que são convenientemente separadas (logicamente).

Em aplicações muito grandes, mesmo com uma única preocupação, dividi-lo faz sentido na minha experiência. Você tem o benefício de reduzir um subconjunto da classe de erros possível em troca de outros erros (às vezes mais fáceis de resolver). Em geral, você também pode ter equipes de pessoas trabalhando isoladamente, o que melhora a produtividade. Atualmente, muitas aplicações são divididas de maneira bastante fina, às vezes em seu próprio prejuízo. Também participei de equipes nas quais o aplicativo foi dividido em tantos microsserviços desnecessariamente que introduziu muita sobrecarga quando as coisas pararam de se falar. Além disso, ter todo o conhecimento de como cada parte se comunica com as outras partes fica muito mais difícil a cada divisão sucessiva. Existe um equilíbrio e, como você pode ver pelas respostas aqui, a maneira de fazer isso não é muito clara,


2
Meu primeiro trabalho como programador foi como programador de bug do milênio. O software em que eu estava trabalhando foi dividido em centenas de pequenos programas, todos fazendo uma pequena parte, reunidos com arquivos em lotes e usando arquivos para comunicar o estado. Foi uma grande bagunça, inventada em uma época em que os computadores eram lentos, tinham pouca memória e o armazenamento era caro. Quando trabalhei com ele, o código já tinha 10 a 15 anos. Quando terminamos, eles pediram meu conselho e meu conselho era converter tudo em um novo aplicativo monolítico. Eles fizeram e um ano depois recebi um grande obrigado.
Pieter B

@ PieterB Eu tive uma experiência semelhante. Infelizmente, a tecnologia "de ponta" é um culto à carga muito grande de várias maneiras. Em vez de escolher o melhor método para o trabalho, muitas empresas apenas seguem o que um FAANG está fazendo no momento sem nenhuma pergunta.
CL40 13/03

e também: o que pode sair como um aplicativo monolítico uma vez compilado, pode ser um aplicativo muito modular, em termos de código.
Pieter B

1

Para aplicativos de interface do usuário, é improvável que diminua a quantidade geral de bugs, mas mudará o equilíbrio da mistura de bugs em direção a problemas causados ​​pela comunicação.

Por falar em aplicativos / sites de interface do usuário - os usuários são extremamente pacientes e exigem baixo tempo de resposta. Isso transforma qualquer atraso na comunicação em bugs. Como resultado, será negociado um potencial decréscimo de bugs devido à menor complexidade de um único componente, com bugs muito rígidos e requisitos de tempo de comunicação entre processos / máquinas.

Se as unidades de dados com as quais o programa lida são grandes (ou seja, imagens), qualquer atraso no processo seria mais longo e mais difícil de eliminar - algo como "aplicar transformação à imagem de 10 mb" ganhará instantaneamente + 20 mb de E / S de disco / rede, além conversão para 2 do formato na memória para o formato serializabe e vice-versa. Realmente, não há muito que você possa fazer para ocultar o tempo necessário ao usuário.

Além disso, qualquer comunicação e, especialmente, E / S de disco, está sujeita a verificações de antivírus / firewall - isso inevitavelmente adiciona outra camada de bugs difíceis de reproduzir e ainda mais atrasos.

"Programa" monolítico dividido brilha onde os atrasos na comunicação não são críticos ou já são inevitáveis

  • processamento em massa paralelamente agradável de informações, onde você pode negociar pequenos atrasos extras para melhorar significativamente as etapas individuais (às vezes eliminando a necessidade de componentes personalizados usando uma vez de prateleira). A presença de uma etapa individual pequena pode permitir o uso de várias máquinas mais baratas, em vez de uma única e cara, por exemplo.
  • dividir serviços monolíticos em microsserviços menos acoplados - chamar vários serviços em paralelo em vez de um provavelmente não adicionará atrasos extras (pode até diminuir o tempo geral se cada indivíduo for mais rápido e não houver dependências)
  • movendo operações que os usuários esperam levar muito tempo - renderizando cenas / filmes em 3D complexos, computando métricas complexas sobre dados, ...
  • todos os tipos de "preenchimento automático", "verificação ortográfica" e outros auxílios opcionais podem e muitas vezes podem ser externos - o exemplo mais óbvio são as sugestões automáticas de URL do navegador, onde sua entrada é enviada para o serviço externo (mecanismo de pesquisa) o tempo todo .

Observe que isso se aplica aos aplicativos de desktop e aos sites - parte do programa voltada para o usuário tende a ser "monolítica" - todo o código de interação do usuário vinculado a um único dado é geralmente executado em um único processo (não é incomum dividir processos por peça de dados, como uma página HTML ou uma imagem, mas é ortogonal a esta pergunta). Mesmo para o site mais básico com entrada do usuário, você verá a lógica de validação sendo executada no lado do cliente, mesmo que torná-lo no servidor seja mais modular e reduza a complexidade / duplicação de código.


0

Isso ajuda a evitar bugs?

Evita? Bem, não, na verdade não.

  • Ajuda a detectar erros .
    Ou seja, todos os erros que você nem sabia que tinha, que você só descobriu quando tentou dividir toda essa bagunça em partes menores. Então, de certa forma, impediu que esses erros aparecessem na produção - mas os erros já estavam lá.
  • Ajuda a reduzir o impacto dos erros .
    Erros em aplicativos monolíticos têm o potencial de derrubar todo o sistema e impedir que o usuário interaja com seu aplicativo. Se você dividir esse aplicativo em componentes, a maioria dos bugs, por design, afetará apenas um dos componentes.
  • Ele cria um cenário para novos erros .
    Se você deseja manter a experiência do usuário a mesma, precisará incluir uma nova lógica para todos esses componentes se comunicarem (por meio de serviços REST, por chamadas do sistema OS, o que você tem) para que eles possam interagir perfeitamente a partir do ponto de vista do usuário.
    Como um exemplo simples: seu aplicativo monolítico permite que os usuários criem um modelo e o animem sem sair do aplicativo. Você divide o aplicativo em dois componentes: modelagem e animação. Agora, seus usuários precisam exportar o modelo do aplicativo de modelagem para um arquivo, encontrar o arquivo e abri-lo com o aplicativo de animação ... Vamos ser sinceros, alguns usuários não vão gostar disso, então você deve incluir uma nova lógica para o arquivo. aplicativo de modelagem para exportar o arquivo einicie automaticamente o aplicativo de animação e abra o arquivo. E essa nova lógica, por mais simples que seja, pode ter vários bugs relacionados à serialização de dados, acesso a arquivos e permissões, usuários alterando o caminho de instalação dos aplicativos, etc.
  • É a desculpa perfeita para aplicar a refatoração muito necessária .
    Quando você decide dividir um aplicativo monolítico em componentes menores, espero que você o faça com muito mais conhecimento e experiência sobre o sistema do que quando ele foi projetado pela primeira vez e, graças a isso, você pode aplicar vários refatores para criar o código mais limpo, mais simples, mais eficiente, mais resiliente, mais seguro. E essa refatoração pode, de certa forma, ajudar a evitar bugs. Obviamente, você também pode aplicar a mesma refatoração ao aplicativo monolítico para evitar os mesmos erros, mas não o faz porque é tão monolítico que tem medo de tocar em algo na interface do usuário e quebrar a lógica de negócios ¯ \ _ (ツ) _ / ¯

Portanto, eu não diria que você está impedindo bugs apenas dividindo um aplicativo monolítico em componentes menores, mas você está realmente facilitando chegar a um ponto em que os bugs podem ser mais facilmente evitados.

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.