Existem casos excepcionais em que podemos aceitar código duplicado?

57

Estou trabalhando em um projeto de software em que precisamos criar três APIs. Um para o canal de home banking, um para o canal da agência e um terceiro para o canal móvel .

A API da agência é a mais completa, pois possui todas as funcionalidades. Depois, a API Home um pouco menor e a API móvel.

Os arquitetos aqui criaram uma camada comum (serviços EJB entre canais compartilhados por todas as APIs). Mas as APIs são diferentes.

Por enquanto, não há grande diferença entre as APIs. A grande equipe começou com o canal da agência e estamos adaptando-o agora para o canal doméstico. Estamos apenas enriquecendo objetos especificamente para nosso aplicativo doméstico. Caso contrário, o código é 95% semelhante entre APIs. As APIs são construídas sobre o Spring MVC e possuem (controladores, modelos e alguns utilitários).

Basicamente, os controladores estão fazendo o mapeamento do BO para o ChannelObject (parece-me que não é o lugar certo para fazer isso) e alguns utilitários e serializadores extras. Tudo está duplicado por enquanto. Eles estão dizendo que o motivo da duplicação é que desejam as APIs independentes. "Se amanhã quisermos um comportamento diferente em casa do que em agência ou celular, não teremos problemas !!"

Existe um caso em que devemos aceitar código duplicado?

Mohamed Amine Mrad
fonte
22
E se três amanhãs se aproximarem, eles decidem que desejam acesso e representação consistente de dados entre todas as APIs ... bem ... "Lute!"
Becuzz
26
Código duplicado não é necessariamente uma coisa ruim. O ditado "DRY é o inimigo da dissociação" não pode ser enfatizado o suficiente. Tendo dito isso, projetar para o futuro e não para agora é realmente uma coisa muito ruim. Esse futuro quase nunca acontece. Em vez disso, projete uma solução altamente dissociada, coberta por bons testes automatizados para o que é necessário agora. Então, no futuro, se algo diferente for necessário, será mais fácil mudar.
David Arno
2
Acho que os dois casos em que nunca me arrependi de duplicar o código são (a) onde o código duplicado é uma parte muito pequena e não muito importante do total e (b) onde estou copiando o código de um sistema que já está morrendo em um novo sistema projetado para a longevidade. Existem muitos outros casos em que chorei lágrimas amargas depois de bifurcar código.
Michael Kay
14
Onde trabalhei, observou-se com frequência que se deve (muitas vezes) duplicar uma vez e abstrair a comunalidade pela terceira vez. Isso remove o zeitgeist da abstração precoce, possivelmente inadequada, que aumenta o acoplamento em vez da coesão. Quando requisitos futuros são realmente bem compreendidos, é claro que exceções podem ser feitas.
precisa saber é o seguinte
3
Um caso trivial onde código duplicado pode ser aceitável é se é auto-gerado
samgak

Respostas:

70

Duplicação pode ser a coisa certa a se fazer, mas não por esse motivo.

Dizer "podemos querer que esses três lugares na base de código se comportem de maneira diferente, embora agora sejam idênticos" não é um bom motivo para duplicação em larga escala. Essa noção pode se aplicar a todos os sistemas e pode ser usada para justificar qualquer duplicação, o que obviamente não é razoável.

A duplicação deve ser tolerada apenas quando removê-la, no geral, seria mais cara agora por algum outro motivo (não se pode pensar em uma boa no momento, mas tenha certeza de que pode haver uma) - praticamente tudo na programação é uma troca e não um lei).

Para o que você está fazendo, a solução certa pode ser, por exemplo, extrair o comportamento duplicado agora para uma Estratégia ou algum outro padrão que modela o comportamento como classes e depois usa três instâncias da mesma classe. Dessa forma, quando você não quiser mudar o comportamento em um dos três lugares, você só tem que criar uma nova Estratégia e instanciar que em um só lugar. Dessa forma, você só precisa adicionar algumas classes e deixar o restante da base de código quase completamente intocado.

Kilian Foth
fonte
11
Se duas coisas se comportam da mesma forma, isso é duplicação. Embora, a menos que se comportem da mesma forma por um motivo, não devam ser mesclados. Pode-se extrair uma parte comum das implementações? Às vezes, existem blocos de construção ainda não abstraídos, quem sabe.
Deduplicator
32
Como exemplo, tínhamos um pedaço de código usado por três partes de nosso aplicativo, e ele foi terrivelmente escrito com muitas ramificações condicionais em todos os lugares para cobrir exceções para cada um desses três. Mas , e esse foi o ponto importante, ele foi usado por dois anos sem nenhum relatório significativo de bug de qualquer tipo. Portanto, quando uma quarta parte do aplicativo precisava fazer algo com ele, mas novamente um pouco diferente, foi tomada a decisão de não tocar no código defeituoso que funcionava perfeitamente, copiá-lo e criar uma base flexível e melhor escrita. o futuro.
precisa saber é o seguinte
2
A duplicação é a coisa certa a fazer quando os custos de legibilidade aumentam muito quando comparados aos custos de manutenção que acabam sendo interligados. Eu não acho que isso se aplique de todo nesse cenário, e geralmente é visto em menor escala em um projeto, não em algo assim. Mesmo assim, é muito raro você entrar em uma situação em que esse seja o caso, isso acontecerá se você tiver alguma duplicação de nichos aninhados que forçaria você a usar o padrão do Método do Modelo ou algo semelhante, mas em locais pequenos. Isso normalmente acaba ocultando o código.
opa
Um exemplo em que a duplicação é útil (e "removê-la em geral será mais cara agora ") é a validação de entrada em aplicativos da Web: Empregamos uma primeira validação (possivelmente simplificada) no cliente para que o usuário obtenha feedback imediato sobre os problemas; e, em seguida, fazemos a mesma validação (ou mais completa) no servidor porque os clientes não podem ser confiáveis.
Hagen von Eitzen
3
@HagenvonEitzen Mas isso nem precisa ser duplicação, dependendo da pilha de tecnologia. Por exemplo, você pode ter a entrada de verificação de JavaScript no navegador e, em seguida, verificação de Java no servidor, para ter uma funcionalidade duplicada. Mas se você executasse o Node.js no servidor, poderia usar a mesma validação de JavaScript no navegador e no servidor, eliminando a duplicação. Você ainda deseja que o código seja executado em vários locais (um ambiente confiável e não confiável), mas o código não precisa necessariamente ser duplicado.
21417 Joshua Taylor
87

Sandi Metz, uma renomada engenheira de software e autora do ecossistema Ruby, tem um ótimo post e uma palestra em que também fala sobre a relação entre duplicação e abstração. Ela chega à seguinte conclusão

duplicação é muito mais barata que a abstração errada

E eu concordo completamente com ela. Deixe-me dar mais contexto a esta citação. Às vezes, encontrar a abstração correta é muito difícil. Nesses casos, é tentador optar por qualquer abstração para reduzir a duplicação. Mais tarde, porém, você poderá descobrir que sua abstração não é válida para todos os casos. No entanto, é caro mudar tudo novamente e seguir um caminho diferente (para uma explicação muito melhor, observe-a falar!).

Então, sim, para mim, existem casos excepcionais em que aceitar a duplicação é a decisão certa, especialmente quando você não tem certeza do que está por vir e os requisitos provavelmente mudarão. Da sua postagem, entendo que há muita duplicação agora, mas seus colegas sugerem que isso pode mudar e não associar os aplicativos. Na minha opinião, este é um argumento válido e não pode ser desconsiderado em geral.

Larsbe
fonte
23
Ah, sim, se você não pode deduzir as regras generalizadas, tentar abstraí-las pode falhar. E se a correspondência for coincidente, pode ser de curta duração.
Deduplicator
5
Pode ser dispendioso alterar uma abstração quando estiver no local, mas livrar-se da duplicação quando estiver no local pode ser ainda mais problemático. É claro que isso depende muito do idioma etc. - os sistemas modernos de tipo estático forte podem fazer um bom trabalho para ajudá-lo a obter mesmo alterações em larga escala em alguma abstração. Manter os recursos duplicados sincronizados, no entanto, não é algo com que um sistema de tipos possa ajudá-lo (porque as duplicatas são, para o sistema de tipos , apenas diferentes). Mas suponho que em linguagens dinâmicas, tipadas por patos, é o contrário; então pode fazer sentido para Ruby.
usar o seguinte código
34

Se as pessoas começam a raciocinar sobre design com as palavras "se amanhã" , isso costuma ser um grande sinal de alerta para mim, especialmente quando o argumento é usado para justificar uma decisão que inclui trabalho e esforço extras, pelos quais ninguém realmente sabe se isso acontecerá. pagar, e que é mais difícil de mudar ou reverter do que a decisão oposta.

A duplicação de código reduz o esforço apenas por um curto período, mas aumentará os esforços de manutenção quase imediatamente, proporcional ao número de linhas de código duplicadas. Observe também que, uma vez que o código é duplicado, será difícil removê-lo quando for a decisão errada; enquanto, se não se duplicar o código agora, ainda é fácil introduzir a duplicação posteriormente, se ficar com o DRY. foi a decisão errada.

Disse que, em organizações maiores, às vezes é benéfico favorecer a independência de equipes diferentes em relação ao princípio DRY. Se remover a duplicação extraindo as partes comuns de 95% das APIs dois, um novo componente levará a um acoplamento de duas equipes independentes, mas talvez essa não seja a decisão mais sensata. Por outro lado, se você tiver recursos limitados e houver apenas uma equipe mantendo as duas APIs, tenho certeza de que será do seu interesse não criar um esforço duplo e evitar duplicação desnecessária de código.

Observe ainda que faz diferença se as APIs "Inicial" e "Agência" são usadas exclusivamente por aplicativos totalmente diferentes, ou se alguém pode tentar criar um componente construído sobre as APIs que também podem ser usadas no contexto "Inicial" como em um contexto de "Agência". Para essa situação, ter as partes comuns das APIs exatamente idênticas (o que você só pode garantir se as partes comuns não forem duplicadas) facilitará muito provavelmente o desenvolvimento desse componente.

Portanto, se houver sub-equipes realmente diferentes, cada uma responsável por cada uma das APIs, cada uma com uma agenda e recursos diferentes, é hora de duplicar o código, mas não "apenas por precaução".

Doc Brown
fonte
3
Um sinal de alerta ainda maior do que "se amanhã" para mim é "Isso nunca mudaria".
Abuzittin gillifirca
@abuzittingillifirca: e o que isso tem a ver com a pergunta ou a minha resposta?
Doc Brown
11
Estou desafiando seu primeiro parágrafo.
abuzittin gillifirca
2
@abuzittingillifirca: bem, leia a pergunta novamente: trata-se de raciocinar com um argumento "se amanhã" para justificar uma decisão de duplicação difícil de reverter , tornando o software realmente mais difícil de mudar em determinados casos. Pode ser um pouco contra-intuitivo, mas a melhor maneira de manter o software alterável para o futuro não é fazer suposições (provavelmente erradas) sobre o futuro, mas manter o software o mais pequeno e SÓLIDO possível.
Doc Brown
13

Duplicação para evitar o acoplamento . Digamos que você tenha dois grandes sistemas e os force a usar a mesma biblioteca. Você pode estar acoplando o ciclo de liberação de ambos os sistemas. Isso pode não ser muito ruim, mas digamos que um sistema precise introduzir uma mudança. O outro precisa analisar a mudança e pode ser afetado. Às vezes, isso pode quebrar as coisas. Mesmo que ambas as partes sejam capazes de coordenar as mudanças, pode haver muitas reuniões, passando por gerentes, testes, dependências e o fim da pequena equipe autônoma.

Então você está pagando o preço do código duplicado para ganhar autonomia e independência.

Borjab
fonte
Portanto, essa é a duplicação entre componentes para evitar o acoplamento. Mas você ainda deseja evitar a duplicação intracomponente, certo?
TemplateRex
Bem, sim, você deseja minimizar o código duplicado, pois isso facilitará seu entendimento e modificação. Mas lembre-se de que existem boas respostas com exceções viáveis. A abstração correta para evitar duplicação pode ser difícil de encontrar.
Borjab