Escrevendo código robusto x superengenharia


33

Como vocês sabem que estão escrevendo o código mais robusto possível sem o excesso de engenharia?

Eu me pego pensando demais em todos os caminhos possíveis que meu código pode seguir, e às vezes parece uma perda de tempo. Acho que depende do tipo de programa que você está escrevendo, mas não quero usar muito do meu tempo levando em consideração situações que nunca acontecerão.


2
Codifique-o no Agda2
SK-logic

um exemplo concreto ajudaria muito a defender seu ponto de vista. :)
João Portela

Posso apenas verificar se você realmente está perguntando sobre robustez, ou seja, "a capacidade de um sistema continuar trabalhando na presença de entradas inválidas ou condições ambientais estressantes", porque algumas respostas parecem pensar que você está falando sobre extensibilidade.
DJClayworth 23/09

Eu trabalho com prazos loucos, além de ser para demonstração, para que eu possa felizmente fugir rapidamente sem paralisia perfeita.
Job

1
Aqui está um artigo que fala sobre o assunto: code-tag.com/2017/04/02/…
San

Respostas:


39

Como vocês sabem que estão escrevendo o código mais robusto possível sem o excesso de engenharia?

O que você considera código robusto? Código que já é à prova do futuro e tão poderoso que pode lidar com qualquer situação? Errado, ninguém pode prever o futuro! E errado de novo, porque será uma bagunça complicada e inatingível.

Sigo vários princípios: Primeiro e acima de tudo YAGNI (ainda) e KISS , para não escrever código desnecessário. Isso também evita efetivamente a superengenharia. Refatoro o aplicativo quando são necessárias extensões. As modernas ferramentas de refatoração permitem criar interfaces com facilidade e trocar implementações posteriormente, quando você precisar delas.

Então tento tornar o código que escrevo o mais robusto possível, que inclui a eliminação de quantos caminhos o programa seguir (e também declara) quanto possível e um pouco de programação espartana . Uma grande ajuda são as funções / métodos "atômicos" que não dependem de estados externos ou, pelo menos, não deixam o programa em um estado inconsistente quando falham. Se você fizer isso bem, também é muito improvável que você acabe com um código de espaguete, e também é uma benção para a manutenção. Além disso, no design orientado a objetos, os princípios do SOLID são um ótimo guia para código robusto.

Eu realmente descobri que muitas vezes você pode reduzir a complexidade, por exemplo, explosões combinatórias de caminhos ou estados de programas, pensando profundamente em como você pode projetá-lo como o caminho mais reto possível. Tente manter no mínimo as combinações possíveis, escolhendo a melhor ordem de suas sub-rotinas e projetando-as para esse fim.

Código robusto é quase sempre código simples e limpo, mas a simplicidade é uma característica que nem sempre é alcançada com facilidade. No entanto, você deve lutar por isso. Sempre basta escrever o código mais simples possível e adicionar complexidade apenas quando você não tiver outra escolha.

A simplicidade é robusta, a complexidade é frágil.

Complexidade mata.


2
Porque não tem toneladas de classes, fábricas e abstrações. É um paradoxo, mas algumas pessoas gostam disso. Não faço ideia do porquê.
Coder

5
Isto é Sparta!!!
Tom Squires

4
As pessoas que não fazem isso há vinte anos simplesmente não entendem como a complexidade pode matá-lo. Eles acham que são tão inteligentes. Eles são burros, não inteligentes. Essa complexidade vai te matar morto.
PeterAllenWebb

1
A robustez não é voltada para o futuro - é sobre continuar funcionando com entradas inválidas ou ambientes estressantes - Code Complete p464.
DJClayworth 23/09

5
A menos que o interlocutor esteja usando 'robusto' em um sentido diferente daquele que eu entendo, você está respondendo a uma pergunta diferente. Ele não está perguntando "devo codificar para permitir requisitos futuros"? Ele está perguntando "com quais casos incomuns de entrada devo lidar". Nenhum de YAGNI, KISS e SOLID é relevante. Você precisa permitir um milhão de usuários tentando fazer logon simultaneamente? O que acontecerá se um nome de login começar com uma barra invertida? Nenhuma dessas perguntas é respondida por YAGNI.
DJClayworth 23/09

8

Eu tento manter um equilíbrio, focando

  • manipular todos os caminhos de execução possíveis nos casos de uso existentes (essa é a parte "robustez"),
  • recursos / requisitos de habilitação Tenho certeza de que virão no futuro próximo e
  • coisas que sei por experiência que serão necessárias para a manutenção a longo prazo da base de código (ou seja, mantendo o código limpo e testável).

É uma área de fronteira confusa - às vezes eu consigo fazer um trabalho desnecessário, às vezes deixo de fazer algo que acaba sendo necessário mais tarde. Se as falhas não são grandes, eu estou bem. De qualquer forma, eu me esforço para aprender com meus erros.


Um exemplo seria ótimo aqui manipulação de todos os caminhos de execução possíveis nos casos de uso existentes
CodeYogi

5

A diferença entre engenharia robusta e superengenharia é a diferença entre lidar com todos os casos de uso possíveis, mesmo os casos de uso bizarros e marginais que NÃO DEVEM acontecer. Quando digo graciosamente, quero dizer, o usuário entra em um caso de exceção bizarro ou se depara com uma situação que exige um recurso não suportado ou não especificado que não foi definido e o código sai normalmente sem travar ou informa o usuário sobre a funcionalidade não suportada.

A superengenharia, por outro lado, poderia cair no campo da implementação completa de recursos que não eram necessários ou solicitados (alguns recursos que o cliente PRECISA, mas nunca foram solicitados!) OU pode ser definido derivando um design excessivamente complexo ou excessivamente complexo. código para lidar com um problema relativamente simples.


4

1) Obtenha requisitos.

2) Escreva um código mínimo para atender aos requisitos. Se algo for ambíguo, faça um palpite. Se for super ambíguo, volte para 1.

3) Enviar para teste.

4) Se os testadores considerarem bom, documente a aprovação. Se algo estiver errado, volte para 1.

Concentre-se em passar nos testes, não em prever os testes. Se você não possui testadores ... obtenha testadores! Eles são essenciais não apenas para verificar a correção do código, mas para todo o processo de desenvolvimento.


1
+1 em Foco na aprovação de testes, não na previsão de testes. No entanto, espera-se que muitos desenvolvedores como eu façam as duas coisas, na falta de fortes analistas de negócios.
maple_shaft

@maple_shaft - Muito verdadeiro. A questão é que esses problemas surgem por causa da incompetência de outra pessoa. Stressing sobre o trabalho de outra pessoa é o caminho para o esgotamento. Se minha empresa fosse burra o suficiente para que eu fizesse as contas a receber do mês, não ficaria muito decepcionado comigo mesmo se não desse certo. Definir requisitos geralmente implica apenas descrever o que você faz todos os dias, para que possa ser automatizado. Se nenhum dos funcionários puder fazer isso, bem ... a empresa pode estar com problemas.
Morgan Herlocker

3

Em primeiro lugar, mantenha os dados normalizados (não redundantes) o máximo que puder. Se os dados estiverem totalmente normalizados, nenhuma atualização única poderá torná-los inconsistentes.

Você nem sempre pode manter os dados normalizados; em outras palavras, talvez não seja possível eliminar a redundância; nesse caso, eles podem ter estados inconsistentes. O que se deve fazer é tolerar a inconsistência e repará-la periodicamente com algum tipo de programa que varre e corrige o problema.

Há uma forte tendência para tentar gerenciar a redundância com força por meio de notificações. Estes não são apenas difíceis de garantir que estão corretos, mas podem levar a enormes ineficiências. (Parte da tentação de escrever notificações surge porque na OOP elas são praticamente incentivadas.)

Em geral, qualquer coisa que dependa da sequência temporal de eventos, mensagens etc. será vulnerável e exigirá toneladas de codificação defensiva. Eventos e mensagens são característicos dos dados com redundância, porque estão comunicando alterações de uma parte para outra, tentando evitar inconsistências.

Como eu disse, se você precisar de redundância (e as chances são muito boas), é melhor ser capaz de: a) tolerar eb) reparar. Se você tentar impedir a inconsistência apenas por meio de mensagens, notificações, gatilhos, etc., será muito difícil torná-la robusta.


3
  • escreva para reutilização.
  • escreva testes. trivial, não trivial, alguns absurdamente complexos para ver como ele lida sob tais condições. testes também o ajudarão a determinar a forma da interface.
  • escreva o programa com falha total (por exemplo, afirmação). meu código tem uma tonelada de reutilização e eu testo uma tonelada de casos - há mais verificação / tratamento de erros do que implementação real (com base na contagem de linhas).
  • reuso.
  • conserte imediatamente as coisas que dão errado.
  • aprender e construir a partir da experiência.

erros vai vir para cima ao longo do caminho, mas eles vão (felizmente) ser localizada e eles vão (na maioria dos casos) mostram-se muito cedo no teste. o outro benefício da reutilização é que o cliente / responsável pela chamada pode salvar a maior parte da verificação / andaime de erros usando o que é trazido pela implementação.

seus testes definirão os recursos de seu programa e quão robustos eles são - continue adicionando testes até que você esteja satisfeito com as taxas e contribuições de sucesso; melhorar, estender e fortalecer conforme necessário.


2

Eu faço essa distinção escrevendo código com um comportamento bem definido, mas não necessariamente ideal, para passagens de execução muito improváveis. Por exemplo, quando tenho certeza (comprovada, mas não testada) de que uma matriz será positiva definida, insiro uma asserção ou exceção no programa para testar o estado, mas não escrevo um caminho de código próprio para ela. Assim, o comportamento é definido, mas subótimo.


2

Robustez: o grau em que um sistema continua funcionando na presença de entradas inválidas ou condições ambientais estressantes. (Código completo 2, p464)

A questão importante aqui é perguntar o quão importante é a robustez para você. Se você é o Facebook, é realmente importante que seu site continue funcionando quando alguém coloca caracteres especiais na entrada e que seu servidor permaneça ativo quando 100 milhões de usuários estiverem conectados simultaneamente. Se você estiver escrevendo um script para executar uma operação comum que somente você faz, não se importa muito. Entre existem muitos níveis. Julgar quanto de robustez você precisa é uma das habilidades importantes que um desenvolvedor deve aprender.

O princípio do YAGNI se aplica à adição de recursos que um programa pode precisar. Mas esse princípio não se aplica à robustez. Os programadores tendem a superestimar a probabilidade de que uma determinada extensão futura seja necessária (especialmente se for legal), mas subestimam a probabilidade de que algo dê errado. Além disso, se um recurso omitido for necessário depois, o programador poderá escrevê-lo mais tarde. Se uma verificação de erro omitida for necessária, o dano pode ser causado.

Portanto, é realmente melhor errar ao fazer verificações de condições de erro incomuns. Mas existe um equilíbrio. Algumas das coisas a considerar neste equilíbrio:

  • Com que frequência esse erro pode ocorrer?
  • Qual é o custo de ter esse erro?
  • É para uso interno ou externo?

Não esqueça que as pessoas podem - e vão - tentar usar seu programa de maneiras inesperadas. É melhor se algo previsível acontecer quando isso acontecer.

Como última linha de defesa, use a declaração ou o desligamento. Se acontecer algo que você não saiba como lidar com isso, encerre o programa. Isso geralmente é melhor do que permitir que o programa continue e faça algo imprevisível.

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.