O que prejudica a manutenção? [duplicado]

73

Para alguém que ainda não tem muita experiência no mundo real, a noção de código sustentável é um pouco vaga, apesar de seguir as regras típicas de boas práticas. Intuitivamente, posso dizer que o código pode ser bem escrito, mas não pode ser mantido se, por exemplo, ele dificultar as informações que estão sujeitas a constantes mudanças, mas ainda tenho dificuldade em analisar o código e decidir se ele pode ser mantido ou não.

Então a questão é: o que prejudica a manutenção? O que devo procurar?

    
por EpsilonVector 05.12.2011 / 08:58
fonte

17 respostas

94

Existem muitos aspectos para a manutenção, mas o mais importante é o baixo acoplamento e alta coesão . Essencialmente, quando você está trabalhando com um bom código, é possível fazer pequenas alterações aqui e ali sem ter que manter toda a base de código em sua cabeça. Com um código ruim, você teria que levar mais algumas coisas em consideração: conserte-o aqui e ele será quebrado em outro lugar.

Quando você tem mais de 100 kLOC de código à sua frente, isso é crucial. As regras de "boas práticas" frequentemente mencionadas, como formatação de código, comentários, nomeação de variáveis, etc., são superficiais comparadas a questões de acoplamento / coesão.

O problema do acoplamento / coesão é que não é fácil medir ou ver rapidamente. Existem algumas tentativas acadêmicas para fazer isso, e talvez algumas ferramentas de análise de código estático, mas nada do que conheço forneceria uma estimativa confiável de quanto tempo você terá com o código.

    
por 05.12.2011 / 09:36
fonte
67

Código duplicado :

Copiar Colar é provavelmente a operação mais cara quando se trata de custos de manutenção de programas.

Por que se preocupar em mover este código para um componente comum, eu apenas copio e termino com ele Sim ... o programador provavelmente salvou uma hora de trabalho ou fazendo as coisas dessa maneira . No entanto, mais tarde vem um bug e ... sim, claro que fica fixo em apenas metade do código em execução. O que significa que mais tarde ainda será registrada uma regressão resultando em outra correção OF THE SAME THING .

Este parece óbvio, mas no grande esquema das coisas é insidioso. Os programadores parecem bem porque fazem mais. Gerente de equipe de desenvolvimento parece bom porque eles entregaram a tempo. E como o problema só é encontrado, muito mais tarde a culpa nunca recai sobre o verdadeiro culpado.

Eu perdi a conta de quantas vezes eu tive que parar um lançamento por causa de "regressões" Agora eu perdi um dia inteiro rastreando uma correção que já estava pronta, corrigi-la novamente e apertar a nova construção através dos aros. Então, sim, uma hora para um desenvolvedor há seis meses (aproximadamente CAD $ 40) custou apenas um dia inteiro para 1 senior dev + meio dia para um desenvolvedor júnior + um dia inteiro de atraso em um projeto já atrasado ...

você faz as contas ...

então o próximo que puxar isso em mim, eu juro que é melhor ele correr rápido, porque eu estarei bem atrás dele !!!

    
por 05.12.2011 / 09:10
fonte
52

Embora o código que quebra as regras nas outras respostas seja certamente pior para manter, todo o código é difícil de manter , portanto, menos você tenha o melhor . Os defeitos se correlacionam strongmente com a quantidade de código, então, quanto mais código você tiver quanto mais bugs você tiver . Quando o código ultrapassar um determinado tamanho, você não poderá mais manter todo o seu design na sua cabeça e as coisas ficam muito mais difíceis. Por fim, mais código significa mais engenheiros, o que significa mais custo, mais problemas de comunicação e mais problemas de gerenciamento.

Embora tenha em mente que não se deve escrever código mais complexo, mas mais curto. O código mais fácil de manter é simples e simplesmente não contém redundância para mantê-lo o mais curto possível. Delegar tarefas a outros programas ou API também pode ser uma solução viável, desde que as chamadas da API sejam simples o suficiente.

Em suma, qualquer código supérfluo é um problema de manutenção

    
por 08.11.2018 / 10:48
fonte
31

Ironicamente, tentativas explícitas de "proteger o futuro" de um sistema muitas vezes dificultam a manutenção.

Não codificar números mágicos ou strings é uma coisa, mas vá mais além e você começa a colocar a lógica nos arquivos de configuração. O que significa que você está adicionando um analisador e um interpretador para uma nova linguagem obscura (e provavelmente mal projetada) à sua carga de manutenção.

Camadas de abstração e indirecção adicionadas por nenhum outro motivo que algum componente possa ser alterado no futuro e você não queira depender dele diretamente são puro veneno de manutenção e um excelente exemplo para < href="http://en.wikipedia.org/wiki/YAGNI"> YAGNI . O FAQ do SLF4J explica os problemas com essa ideia muito comum:

Given that writing a logging wrapper does not seem that hard, some developers will be tempted to wrap SLF4J and link with it only if it is already present on the classpath, making SLF4J an optional dependency of Wombat. In addition to solving the dependency problem, the wrapper will isolate Wombat from SLF4J's API ensuring that logging in Wombat is future-proof.

On the other hand, any SLF4J-wrapper by definition depends on SLF4J. It is bound to have the same general API. If in the future a new and significantly different logging API comes along, code that uses the wrapper will be equally difficult to migrate to the new API as code that used SLF4J directly. Thus, the wrapper is not likely to future-proof your code, but to make it more complex by adding an additional indirection on top of SLF4J, which is an indirection in itself.

A última meia-frase sugere a doce ironia de que SLF4J em si é um wrapper de logging ao qual este argumento se aplica tão bem (exceto que ele está fazendo uma tentativa confiável de ser o único wrapper que você precisa para sentar em cima de > e abaixo de todas as outras APIs comuns de registro Java).

    
por 19.02.2013 / 17:48
fonte
21

Na minha experiência, um fator importante na manutenção é consistência . Mesmo que o programa funcione de maneiras estranhas e absurdas, ainda é mais fácil mantê-lo da mesma maneira em todos os lugares. Trabalhar com código que usa diferentes esquemas de nomenclatura, padrões de design, algoritmos, etc. para tarefas semelhantes, dependendo de quem o escreveu, é um PITA importante.

    
por 05.12.2011 / 09:54
fonte
18

Acredito que o código que não é testado na unidade prejudica a capacidade de manutenção. Quando o código é coberto por testes de unidade, você pode ter certeza de que, ao alterá-lo, você sabe o que está quebrando, pois os testes de unidade associados falharão.

Ter testes de unidade junto com as outras sugestões aqui torna seu código robusto e você garante que, quando mudar, exatamente quais impactos você está tendo.

    
por 05.12.2011 / 09:45
fonte
15

A única verdadeira maneira de aprender o que é o código sustentável é envolver-se em manter uma grande base de código (ambos correções de bugs e solicitações de recursos).

Você aprenderá sobre:

  • baixo acoplamento e alta coesão
  • código duplicado
  • testes de regressão e refatoração:. com um conjunto de testes de regressão eficiente, você se sente mais confiante em mudar as coisas
  • observabilidade e registro de erros: se o software rastreia o que está errado, o erro é mais facilmente localizado.
  • documentação precisa: não o tipo que é escrito uma vez no início do projeto e nunca foi modificado.
  • código ofuscado indocumentados tomando a forma de uma idéia do projeto supostamente brilhante ou otimizações ala rápida inversa raiz quadrada .
  • tradição oral transmitida de uma geração de desenvolvedores / mantenedores para a próxima.
por 12.04.2017 / 09:31
fonte
5

Eu tenho alguns "pesadelos" que, embora eu nunca os tenha colocado no topo desta lista, eles definitivamente causaram alguma luta.

O mega objeto / página / função : (O chamado " God Object " anti-pattern ): Colocando tudo no método de um objeto (principalmente mundo Java), página (principalmente php e asp) , método (principalmente VB6).

Eu tive que manter o código de um programador que tinha em um script php: código html, lógica de negócios, chamadas para o mysql todas bagunçadas. Algo como:

foeach( item in sql_query("Select * from Items")) {
<a href="/go/to/item['id']">item['name']</a>
}

Baseando-se em recursos obscuros da linguagem que podem não ser bem conhecidos ou estão prestes a se tornar obsoletos.

Isso aconteceu novamente com variáveis php e predefinidas. Breve história: para algumas configurações do php v4, um parâmetro de solicitação foi atribuído automaticamente a uma variável. então link "magicamente" geraria variável $ id com valor '42'.

Este foi um pesadelo de manutenção não só porque você não poderia saber de onde os dados vieram, mas também porque o php v5 o removeu completamente (+1), então as pessoas treinadas depois dessa versão nem entenderiam o que estava acontecendo e por quê .

    
por 05.12.2011 / 13:22
fonte
4

O que torna a manutenção necessária é que os requisitos são um alvo em movimento . Eu gosto de ver o código onde as possíveis mudanças futuras foram antecipadas, e pensei em como lidar com elas se elas surgirem.

Eu tenho uma medida de sustentabilidade. Suponha que um novo requisito apareça ou uma mudança em um requisito existente. Em seguida, as alterações no código-fonte são implementadas, juntamente com a correção de erros relacionados, até que a implementação esteja completa e correta. Agora execute um diff entre a base de código e a base de código anterior. Se isso incluir alterações na documentação, inclua as na base de código. A partir do diff , você pode obter uma contagem N de quantas inserções, exclusões e substituições de código foram necessárias para realizar a alteração.

O N menor é, como uma média aproximada das mudanças de requisitos passadas e futuras, quanto mais sustentável for o código. A razão é que os programadores são canais barulhentos. Eles cometem erros. Quanto mais mudanças eles têm que fazer, mais erros cometem, que todos se tornam bugs, e cada bug é mais difícil de encontrar e consertar do que fazer em primeiro lugar.

Então eu estou concordando com as respostas que dizem seguir Don't Repeat Yourself (DRY) ou o que é chamado de cookie-cortador de código.

Também estou concordando com o movimento em direção a idiomas específicos de domínio (DSLs) desde que reduzam N. Às vezes as pessoas assumem que o propósito de uma DSL é ser "amigável ao codificador" ao lidar com "abstrações de alto nível". Isso não necessariamente reduz N. A maneira de reduzir N é entrar em uma linguagem (que pode ser apenas uma coisa definida acima de uma linguagem existente) que mapeia mais de perto os conceitos do domínio do problema.

A manutenção não significa necessariamente que qualquer programador possa simplesmente mergulhar. O exemplo que uso da minha própria experiência é Execução Diferencial . O preço é uma curva de aprendizado significativa. A recompensa é uma redução de ordem de magnitude no código-fonte para as caixas de diálogo da interface do usuário, especialmente aquelas que possuem elementos que mudam dinamicamente. Mudanças simples têm N em torno de 2 ou 3. Mudanças mais complexas têm N em torno de 5 ou 6. Quando N é pequeno assim, a probabilidade de introduzir erros é muito reduzida, e dá a impressão de que "simplesmente funciona".

No outro extremo, vi o código em que N estava normalmente na faixa de 20 a 30.

    
por 23.05.2017 / 14:40
fonte
2

Eu acho que a sustentabilidade também é strongmente associada à complexidade do problema, que pode ser uma questão subjetiva. Eu vi situações em que o único desenvolvedor conseguiu manter com sucesso e crescer consistentemente uma grande base de código, mas quando outros entram em seu lugar, parece uma bagunça inamovível - só porque eles têm modelos mentais bem diferentes.

Padrões e práticas realmente ajudam (veja outras respostas para ótimos conselhos). No entanto, seu abuso pode levar a ainda mais problemas quando a solução original é perdida atrás de fachadas, adaptadores e abstrações desnecessárias.

Em geral, o entendimento vem com a experiência. Uma ótima maneira de aprender é analisar como os outros resolveram problemas semelhantes, tentando encontrar pontos strongs e fracos em sua implementação.

    
por 05.12.2011 / 12:15
fonte
2

Isso não é realmente uma resposta, mas um comentário longo (porque essas respostas já foram apresentadas).

Descobri que lutar religiosamente por dois fatores - interfaces limpas e utilizáveis e nenhuma repetição tornaram meu código muito melhor e com o tempo me tornaram um programador muito melhor.

Às vezes, eliminar o código redundante é HARD e obriga você a criar alguns padrões complicados.

Eu costumo analisar o que deve mudar, em seguida, fazer o meu objetivo. Por exemplo, se você está fazendo uma GUI em um cliente para atualizar um banco de dados - o que você precisa adicionar "Outro" um (outro controle ligado ao banco de dados?) Você precisa adicionar uma linha ao banco de dados, e você precisa a posição do componente, é isso.

Então, se esse é o seu mínimo, não vejo NENHUM código nisso - você DEVE fazer isso sem tocar na sua base de código. Isso é incrivelmente difícil de conseguir, alguns toolkits vão fazer isso (geralmente mal), mas é um objetivo. Quão difícil é chegar perto? Não terrivelmente. Eu posso fazer isso e ter feito isso com código zero de duas maneiras, uma é marcando novos componentes GUI com o nome da tabela no banco de dados, outro criando um arquivo XML - o arquivo XML (ou YAML se você odeia XML) pode ser realmente útil porque você pode vincular validadores e ações especiais ao campo, tornando o padrão extremamente flexível.

Além disso, não leva mais tempo para implementar soluções corretamente - no momento em que você enviou a maioria dos projetos, é realmente mais barato.

Posso dizer que, se você depender muito de Setters & Getters ("Bean Patterns"), Generics & Classes internas anônimas você provavelmente não está codificando genericamente assim. Nos exemplos acima, tentar forçar qualquer um deles vai estragar tudo. Setters & Getters forçam você a usar o código para novos atributos, os Genéricos forçam você a instanciar classes (o que requer código) & Classes internas anônimas tendem a não ser fáceis de reutilizar em outros lugares. Se você está realmente codificando genericamente, essas construções de linguagem não são ruins, mas podem dificultar a visualização de um padrão. Para um exemplo totalmente sem sentido:

user.setFirstName(screen.getFirstName());
user.setLastName(screen.getLastName());

Parece ótimo - não é realmente redundante - pelo menos não de uma maneira que você possa consertar, certo? Mas faz com que você adicione uma linha quando você quer adicionar um "Nome do Meio", então é "Código Redundante"

user.getNameAttributesFrom(screen);

Não precisa de um novo código para essa tarefa - ele simplesmente exige que alguns atributos na "tela" sejam marcados com um atributo "Nome", mas agora não podemos copiar o endereço, e quanto a isso:

user.getAttributesFrom(screen, NAME_FIELDS, ADDRESS_FIELDS);

Mais bonito, um var-args permite incluir um grupo de atributos (de um enum) para coletar da tela - ainda é necessário editar o código para modificar os tipos de atributos desejados. Observe que "NAME_FIELDS" é uma instância de enum simples - os campos na "tela" são marcados com esse enum quando projetados para colocá-los na (s) categoria (s) correta (s) - não há tabela de conversão.

Attribute[] attrs=new Attributes[]{NAME_FIELDS, ADDRESS_FIELDS, FRIENDS_FIELDS};
user.getAttributesFrom(screen, attrs);

Agora você está onde está apenas mudando "Data". É onde normalmente deixo - com os dados do código - porque é "bom o suficiente". O próximo passo é externalizar os dados, se isso for necessário, para que você possa lê-los a partir de um arquivo de texto, mas isso raramente acontece. Refatorações "Enrole" assim muito quando você adquire o hábito, e o que acabei de fazer lá ^ criou um novo enum e padrão que acabará rolando em muitas outras refatorações.

Por fim, observe que boas soluções genéricas para problemas como esse NÃO dependem do idioma. Implementei uma solução no-code-per-loc para analisar uma GUI de texto a partir da linha de comando de um roteador, atualizando-a na tela e gravando-a de volta no dispositivo - no VB 3. Só é preciso dedicação ao princípio de não escrever código redundante, nunca - mesmo se você tiver que escrever o dobro do código para fazer isso!

A interface limpa (Fully Factored & não permite a passagem de material ilegal) também é importante. Quando você fatora uma interface entre duas unidades de código corretamente, ela permite manipular o código em qualquer lado da interface com impunidade e permite que novos usuários implementem as interfaces de forma limpa.

    
por 05.12.2011 / 18:26
fonte
2

além dos padrões mencionados, uma das coisas mais importantes é

código legível , cada linha de código que você colocar em uma base de código será lida mais de 100 vezes e, enquanto as pessoas rastreiam bugs, você não quer que elas gastem 30 minutos para tentar descobrir o que faz cada vez.

Portanto, não tente ser inteligente com código "otimizado" a menos que seja realmente um gargalo e, em seguida, comente e coloque o código original legível (e funcional) nos comentários (ou caminho de compilação alternativo para teste) como referência para o que deveria fazer.

isso inclui função descritiva, nomes de parâmetros e variáveis e comentários explicando por que um determinado algoritmo é escolhido em detrimento de outro

    
por 05.12.2011 / 23:16
fonte
1

Quase tudo o que viola alguns bons princípios de desenvolvimento de software prejudica a capacidade de manutenção. Se você olhar para algum código e quiser avaliar como ele é sustentável, você pode escolher alguns princípios específicos e verificar o quanto ele viola .

O princípio DRY é um bom ponto de partida: quantas informações são repetidas no código? O YAGNI também é muito útil: Quanta funcionalidade existe que não é necessária?

Se você fizer alguma pesquisa sobre DRY e YAGNI você encontrará outros princípios, que são aplicáveis como princípios gerais de desenvolvimento de software. Se você estiver usando alguma linguagem de programação orientada a objeto e quiser ser mais específico, os princípios SOLID dê algumas boas orientações para um bom design orientado a objetos.

Código que viola qualquer um desses princípios tende a ser menos sustentável. Eu coloco ênfase em tender aqui, porque - geralmente falado - há situações em que é legítimo (levemente) violar qualquer tipo de princípio, especialmente se ele foi feito por uma questão de manutenção.

O que também ajuda é procurar cheiros de códigos . Dê uma olhada em Refatorando - Melhorando o design do código existente de Martin Fowler. Lá você pode encontrar exemplos de cheiros de código, o que aguça seu senso de código menos sustentável.

    
por 05.12.2011 / 11:05
fonte
1

Muitos recursos.

Os próprios recursos por natureza prejudicam a capacidade de manutenção porque você precisa escrever código extra para implementá-los, mas um aplicativo sem recursos é inútil, por isso adicionamos recursos. No entanto, deixando de fora os recursos desnecessários, você pode manter seu código mais fácil de manter.

    
por 05.12.2011 / 16:29
fonte
-1
  1. Falta de testes unitários adequados
  2. Falta de testes de integração adequados
  3. Falta de testes de desempenho de benchmarking adequados
  4. YAGNI

3 e 4 são, na minha experiência, os ofensores mais comuns em sistemas da vida real. Não ter um conjunto adequado de desempenho / teste de carga torna impossível avaliar o impacto que as mudanças futuras terão no desempenho e na estabilidade, o que causará degradação muito mais rápida e forçará mais frequentemente as refatorações dispendiosas. YAGNI (o que é chamado de "desperdício" no vocabulário Lean ) tornará um sistema muito mais difícil de refatorar e manter, já que muito tempo será desperdiçado (trocadilho intencional) na manutenção de código e compatibilidade retroativa para recursos que nem são usados.

    
por 05.12.2011 / 11:23
fonte
-2

A melhor abordagem para a manutenção da minha experiência é ter o menor código possível e direcionar o trabalho para outras pessoas (ou seja, os fornecedores de software) que fazem a manutenção para você.

Eu prefiro ter a menor quantidade possível de fontes personalizadas e empurrar o máximo possível para planilhas do Excel, arquivos XML ou de propriedade que representem os dados corporativos e forneçam leitores simples ou use uma ferramenta ETL para fazer a conversão para o código do aplicativo. / p>

Eu também tento encontrar algo que seja padrão (ou seja, nenhum único fornecedor), se possível. No entanto, existem algumas coisas que não podemos evitar, e. Mola para injeção de dependência antes de JEE6 e Hibernate para ORM antes de JEE5, log4j / commons-logging / slf4j antes do Java 1.4. Escolhendo algo baseado em padrões, há uma possibilidade maior de trocar de provedor, especialmente se um se tornar extinto ou proibitivamente caro e você evitar os aborrecimentos do caminho de classe.

Claro, se você é o fornecedor que está fornecendo o software, isso é outro assunto. No entanto, se você estiver lidando com situações da vida real, às vezes é melhor investir dinheiro no problema do que em habilidade e tempo.

    
por 05.12.2011 / 14:02
fonte
-3

Qualquer coisa que não seja simples de entender.

A força mais destrutiva para qualquer tipo de código é a manutenção. Infelizmente, a manutenção também é absolutamente necessária. Se o mantenedor não entender rapidamente o código, ele será roteado em torno dele, o que é tão destrutivo ou quase certamente o quebra.

Para citar erroneamente Einstein: Torne-o o mais simples possível - mas não mais simples.

    
por 06.12.2011 / 10:29
fonte