O código antigo deve ser atualizado para usar construções de linguagem mais recentes ou as construções desatualizadas devem ser bloqueadas?


15

Eu quero fazer algumas melhorias em algum código ainda funcional que foi escrito há muito tempo, antes que a linguagem de programação em que ele foi escrito aumentasse em recursos. Em teoria, todo o projeto usa a versão atualizada da linguagem; no entanto, esse módulo em particular (e, de fato, muitos outros módulos) ainda são escritos no dialeto antigo.

Eu devo:

  • Não toque nas partes do código que não preciso tocar, mas escreva meu patch usando os novos recursos de idioma que facilitam a gravação do patch, mas não são usados ​​em situações análogas em nenhum outro lugar do módulo? (Essa é a solução que eu escolheria intuitivamente.)
  • Ignore o fato de que os anos se passaram e reflita o estilo usado no restante do código ao escrever meu patch, comportando-se efetivamente como se eu estivesse fazendo a mesma tarefa muitos anos antes? (Essa solução eu intuitivamente consideraria tola, mas, dada a quantidade de confusão que todo mundo que fala sobre "bom código" faz para manter a consistência a todo custo, talvez seja isso o que devo fazer).
  • Atualizar o módulo inteiro para usar as construções e convenções de idioma mais recentes? (Essa é provavelmente a melhor solução, mas pode exigir muito tempo e energia que poderiam ser melhor gastos em uma tarefa diferente.)

1
@JerryCoffin Não vou continuar discutindo nos comentários: postei na meta onde podemos ter uma discussão adequada.

Respostas:


10

É impossível dar uma resposta definitiva a essa pergunta, porque depende muito dos detalhes da situação.

A consistência no estilo da base de código é importante porque ajuda a facilitar a compreensão do código, que é um dos aspectos mais importantes da manutenção.
Você não seria o primeiro programador que foi amaldiçoado ao inferno por tornar o código difícil de entender, misturando estilos diferentes. Pode até ser você mesmo que faz a maldição dentro de alguns anos.

Por outro lado, o uso de novas construções de linguagem que não existiam quando o código foi escrito pela primeira vez não implica automaticamente que você está reduzindo a capacidade de manutenção ou mesmo que está quebrando o estilo do código. Tudo isso depende dos recursos de idioma específicos que você deseja usar e do quanto a equipe está familiarizada com o estilo antigo do código e os novos recursos.

Como exemplo, geralmente não seria aconselhável começar a introduzir conceitos de programação funcional, como mapear / reduzir, para uma base de código completamente no estilo OO, mas poderia funcionar para adicionar uma função lambda aqui e ali a um programa que não usava qualquer coisa assim antes.


1
Eu discordo parcialmente. Devemos seguir um princípio aberto e próximo. Portanto, devemos tentar isolar o novo código o máximo que pudermos e, ao mesmo tempo, escrever o novo código com a construção de linguagem mais recente possível.
Anand Vaidya

3
@ AnandVaidya: essa é uma expectativa irrealista do IMHO, já que assume que o código antigo segue o OCP ou pode ser facilmente alterado para seguir o OCP, o que raramente é o caso ou simplesmente não vale o esforço.
Doc Brown

1
Quando eu disse ocp, não quero dizer ops ocp, mas ocp em geral. Basicamente, tente não modificar o código existente o máximo possível e tenha seu próprio código isolado. Isso é possível com o antigo código processual. Entendo que os códigos de espaguete não podem ser modificados dessa maneira, mas para códigos bem projetados em qualquer paradigma, o fechamento próximo deve ser facilmente aplicável. Que seja oops ou processual ou funcional.
Anand Vaidya

2
@ AnandVaidya: quando você não quer dizer OCP, não deve chamá-lo dessa maneira. Eu acho que o que você realmente quer dizer é o que Feather chama de escrever novo código em um método sprout , para que se possa restringir o novo estilo de código a um novo método separado. Quando isso é possível e sensato, você está correto, tudo bem. Infelizmente, essa abordagem nem sempre é aplicável, pelo menos não se você deseja manter o código antigo DRY.
Doc Brown

@ DocBrown: Eu não acho que o princípio aberto / fechado seja restrito à programação orientada a objetos. Você pode ter módulos nos quais você adiciona novos procedimentos, mas não modifica os existentes, ou seja, mantém a semântica do código existente intacta e, se precisar de novas funcionalidades, escreve um novo código.
Giorgio

5

Em grande medida, o código e a aparência dele são irrelevantes . O que o código faz é o que importa.

Se você pode garantir que alterar / reescrever o código não mudará o que o código faz, você pode prosseguir e refatorar o código para o conteúdo do seu coração. Essa "garantia" é um conjunto exaustivo de testes de unidade que você pode executar antes e depois das alterações, sem nenhuma diferença perceptível.

Se você não pode garantir essa estabilidade (você não tem esses testes), deixe-a em paz.

Ninguém agradecerá por "quebrar" um software crítico para os negócios, mesmo que você esteja tentando torná-lo "melhor". "Trabalhar" supera "sempre" melhor.

Obviamente, não há nada que o impeça de criar um conjunto desses testes de prontidão para esse exercício ...


4

Quase tudo pode ser analisado em termos de custos e benefícios, e acho que isso se aplica aqui.

Os benefícios óbvios da primeira opção são que ela minimiza o trabalho a curto prazo e as chances de quebrar algo reescrevendo o código de trabalho. O custo óbvio é que ele introduz inconsistência na base de código. Quando você está executando alguma operação X, isso é feito de uma maneira em algumas partes do código, e de uma maneira diferente em uma parte diferente do código.

Para a segunda abordagem, você já notou o benefício óbvio: consistência. O custo óbvio é que você precisa se empenhar para trabalhar de maneiras que, de outra forma, teria abandonado anos atrás, e o código permanece sempre ilegível.

Para a terceira abordagem, o custo óbvio é simplesmente ter que fazer muito mais trabalho. Um custo menos óbvio é que você pode quebrar as coisas que estavam funcionando. Isso é particularmente provável se (como geralmente acontece) o código antigo tiver testes inadequados para garantir que ele continue funcionando corretamente. O benefício óbvio é que (supondo que você faça isso com êxito) você tenha um código novo e brilhante. Junto com o uso de novas construções de linguagem, você tem a chance de refatorar a base de código, que quase sempre dará melhorias em si mesma, mesmo se você ainda usou a linguagem exatamente como ela existia quando foi escrita - adicione novas construções que tornem o trabalho mais fácil, e pode muito bem ser uma grande vitória.

Um outro ponto importante: no momento, parece que este módulo teve manutenção mínima por um longo tempo (mesmo que o projeto como um todo esteja sendo mantido). Isso tende a indicar que é muito bem escrito e relativamente livre de erros - caso contrário, provavelmente teria sofrido mais manutenção nesse ínterim.

Isso leva a outra pergunta: qual é a fonte da mudança que você está fazendo agora? Se você estiver corrigindo um pequeno bug em um módulo que, no geral, ainda atende bem a seus requisitos, isso tenderia a indicar que o tempo e o esforço em refatorar todo o módulo provavelmente serão desperdiçados em grande parte - no momento em que alguém precisar mexer com novamente, eles podem estar aproximadamente na mesma posição em que você está agora, mantendo um código que não atende às expectativas "modernas".

Também é possível, no entanto, que os requisitos tenham mudado e você esteja trabalhando no código para atender a esses novos requisitos. Nesse caso, é bem provável que suas primeiras tentativas não atendam aos requisitos atuais. Há também uma chance substancialmente maior de que os requisitos passem por algumas rodadas de revisão antes de se estabilizarem novamente. Isso significa que é muito mais provável que você esteja fazendo um trabalho significativo neste módulo no (relativamente) curto prazo, e muito mais provável de fazer alterações no restante do módulo, não apenas na área que você conhece corretamente agora. Nesse caso, é muito mais provável que refatorar todo o módulo tenha benefícios tangíveis e de curto prazo que justifiquem o trabalho extra.

Se os requisitos foram alterados, você também pode precisar analisar que tipo de mudança está envolvida e o que está motivando essa mudança. Por exemplo, vamos supor que você estava modificando o Git para substituir o uso do SHA-1 pelo SHA-256. Essa é uma alteração nos requisitos, mas o escopo é claramente definido e bastante restrito. Depois de armazenar e usar o SHA-256 corretamente, é improvável que você encontre outras alterações que afetem o restante da base de código.

Por outro lado, mesmo quando pequeno e discreto, uma alteração na interface do usuário tende a aumentar, então o que começou como "adicionar uma nova caixa de seleção a essa tela" termina mais como: "altere essa interface do usuário fixa para oferecer suporte a modelos definidos pelo usuário, campos personalizados, esquemas de cores personalizados etc. "

Para o exemplo anterior, provavelmente faz mais sentido minimizar as alterações e errar no lado da consistência. Para este último, é muito mais provável que a refatoração completa seja recompensada.


1

Eu iria para você primeira opção. Quando codifico, sigo a regra para deixá-la em um estado melhor do que estava.

Portanto, o novo código segue as práticas recomendadas, mas não tocarei no código não relacionado aos problemas em que estou trabalhando. Se você fizer isso bem, seu novo código deverá ser mais legível e sustentável do que o código antigo. Eu descobri que a capacidade total de manutenção fica melhor, porque se você continuar com isso, o código que você precisa tocar para o seu trabalho é cada vez mais o código "melhor". O que leva a uma resolução mais rápida de problemas.


1

A resposta, como muitas outras coisas, é que depende . Se houver grandes vantagens em atualizar as construções mais antigas, como a capacidade de manutenção de longo prazo amplamente aprimorada (evitando o inferno de retorno de chamada, por exemplo) do que seguir em frente. Se não houver grande vantagem, a consistência provavelmente é sua amiga

Além disso, convém evitar a incorporação de dois estilos na mesma função mais do que evitá-lo em duas funções separadas.

Para resumir: sua decisão final deve ser baseada em uma análise de 'custo' / 'benefício' do seu caso em particular.

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.