Qual é o equilíbrio certo entre consistência e aprimoramento de código?

53

Recentemente, tive uma discussão com um colega sobre o estilo do código. Ele estava argumentando que o uso de APIs e os padrões gerais que você está usando devem ser o mais semelhante possível com o código circundante, se não com a base de código como um todo, da mesma forma que faria com a aparência do código (posicionamento da chave, letras maiúsculas etc.) . Por exemplo, se eu estivesse adicionando um método a uma classe DAO em C #, tentaria usar o LINQ, quando apropriado, para ajudar a tornar meu código limpo e fácil de manter, mesmo que nenhum dos outros métodos dessa classe o estivesse usando. No entanto, meu colega argumentaria que eu não deveria usá-lo nesse caso, porque seria contra o estilo existente dessa classe e, portanto, mais difícil de entender.

A princípio, achei sua posição bastante extrema, mas, depois de pensar um pouco, estou começando a entender seu ponto de vista. Com o exemplo hipotético do LINQ, talvez essa classe não a contenha porque meus colegas não estão familiarizados com o LINQ? Nesse caso, meu código não seria mais sustentável para meus colegas desenvolvedores se eu não o usasse? Por outro lado, se eu realmente acredito que o uso de uma técnica desse tipo resultaria em um código mais limpo, não deveria usá-lo, mesmo que diferisse drasticamente do código ao redor?

Eu acho que o ponto crucial do argumento do meu colega é que, se todos nós implementamos funcionalidades semelhantes em uma base de código de maneiras diferentes, e cada um de nós acha que o nosso caminho é "melhor", no final, o código como um todo fica mais difícil para entender. No entanto, no momento, ainda acho que, se seguirmos cegamente o código existente, a qualidade irá apodrecer lentamente ao longo do tempo.

Então, até que ponto os padrões fazem parte do estilo do código e onde devemos traçar a linha entre permanecer consistente e fazer melhorias?

Robert Johnson
fonte
14
Então, como a qualidade do código melhoraria se todos se restringissem à maneira "ruim"?
Izkata
Quando você escreve LINQ, você não quer dizer LINQ to SQL? Se sim, eu poderia concordar com seu amigo. Se você está falando sobre o LINQ, não concordo com ele. Todo mundo tem que estar familiarizado com o LINQ hoje em dia. A escolha não é deles. Eles são pagos para serem familiares.
Piotr Perak
@ Peri, eu quis dizer apenas o LINQ básico - acho que meu exemplo deveria ter sido trabalhar com IEnumerables em vez de com um DAO.
Robert Johnson
2
ICYMI - Dilbert cômico sobre esse mesmo assunto: dilbert.com/strips/2013-09-21
Jim Bethancourt 11/11

Respostas:

53

Para dar uma resposta mais geral:

Em um caso como esse, você tem duas "práticas recomendadas" de programação que se opõem: a consistência do código é importante, mas a escolha do melhor método possível para realizar sua tarefa. Não há uma resposta correta para esse dilema; depende de alguns fatores:

  • Quão benéfico é o caminho "correto"?

    • Às vezes, as práticas recomendadas, novas e aprimoradas, aumentam drasticamente o desempenho, eliminam bugs, são muito mais fáceis de programar etc. Nesse caso, eu me inclinaria muito para usar o novo método. Por outro lado , a "maneira correta" pode ser pouco mais que açúcar sintático, ou um método idiomático acordado de fazer algo que não é realmente superior. Nesse caso, a consistência do código é provavelmente mais importante.
  • Qual o tamanho de um problema que a inconsistência criaria?

    • Quão interconectado é o novo código com o código herdado? Seu novo código faz parte de uma biblioteca? Ele cria um objeto que é passado para muitas partes do programa? Em casos como esses, a consistência é muito importante. O uso de uma nova API ou uma nova maneira de fazer as coisas em geral pode criar resultados sutilmente diferentes que quebram suposições em outras partes do seu programa. Por outro lado , se você estiver escrevendo um pedaço de código bastante isolado, é menos provável que seja inconsistente um problema.
    • Qual é o tamanho e a maturidade da sua base de código? Quantos desenvolvedores precisam entender e trabalhar nele? Padrões consistentes e acordados são muito mais importantes para projetos maiores.
    • O código precisa ser executado em ambientes mais antigos que podem não suportar os recursos mais recentes?

Com base no equilíbrio desses problemas, você deve fazer a escolha certa sobre qual caminho seguir. Pessoalmente, vejo pouco valor em consistência por causa da consistência e preferiria usar os melhores e mais recentes métodos, a menos que haja um custo significativo para isso.

Obviamente, existe uma terceira opção: reescrever o código existente para que ele use os melhores métodos e seja consistente. Há momentos em que isso é necessário, mas vem com um custo alto.

samthebrand
fonte
7
Resposta muito boa e equilibrada (+1). Na minha experiência, uma equipe pode ser mais produtiva se todos concordarem com um conjunto relativamente pequeno de expressões bem compreendidas do que se cada membro da equipe for livre para usar expressões novas e diferentes que outros membros da equipe possam achar difícil de entender. Um pequeno ponto em relação à declaração "reescrevendo o código existente para que ele use as práticas mais recentes e seja consistente": "mais recente" não implica automaticamente "melhor". É preciso sempre decidir caso a caso.
Giorgio
11
@Giorgio, obrigado pelo feedback; Eu ajustei a linguagem do ponto final.
6
Boa resposta e bons comentários de Giorgio. Talvez valha a pena considerar que, neste exemplo, dependendo da equipe / cultura, é possível ter uma adoção coordenada da equipe em vez de começar sozinho. Isso reduz alguns dos riscos relacionados à manutenção (como todos estão a bordo). (ou seja, colocar mais ênfase em habilidades reais da equipe no momento, em vez do código que você escreveu no passado.)
Matt
+1. Acordado, uma resposta completa e equilibrada - boa consideração de uma variedade de cenários. Novamente, um bom comentário de Giorgio.
22613 Robert Johnson
11
Em relação à adoção de novas expressões idiomáticas, tecnologias: consideraria o início de um novo projeto um bom ponto para introduzir novas coisas. Posteriormente, durante toda a duração do projeto, o estilo do código deve ser mantido o mais consistente possível. Recentemente, tive que descobrir um código de 5 anos e fiquei muito feliz por ainda conseguir encontrar esse caminho, porque toda a equipe aderiu a uma arquitetura comum e a um conjunto de expressões com as quais havíamos concordado (não sem esforço! ) no início do projeto.
Giorgio
26

Permanecer consistente tem pouco valor na minha perspectiva; fazer melhorias continuamente é uma obrigação.

A posição do seu colega realmente impede a inovação. O argumento de consistência leva você a uma situação em que você pode usar, por exemplo, o LINQ apenas se você migrar todo o código para usar o LINQ. E bem, não temos tempo para isso, temos?

Eu prefiro ter inconsistência em que algum código ainda está sendo foreachexecutado sobre ArrayLists e outras partes usam o LINQ IEnumerable<T>, em vez de seguir a maneira mais antiga de fazer as coisas até o final dos tempos.

É responsabilidade dos seus colegas permanecer relevantes e aprender novas maneiras de fazer as coisas.

Joppe
fonte
3
"É responsabilidade dos seus colegas permanecer relevantes e aprender novas maneiras de fazer as coisas.": Uma possibilidade é usar novas expressões e bibliotecas no novo código (novos módulos) e manter um estilo consistente no código legado.
Giorgio
8

A consistência da API é muito importante, tanto para APIs públicas quanto internas.

A consistência da formatação do código é importante e, idealmente, deve ser aplicada pela ferramenta de formatação automática com as mesmas regras de formatação para todos. Facilita a convivência com a base de código compartilhada controlada por versão.

As convenções de nomenclatura devem ser consistentes, também para coisas como variáveis ​​locais etc.

As ferramentas de análise estática devem ser usadas para impor certas outras convenções e boas práticas (mas não seguem cegamente os padrões de tal ferramenta, às vezes os padrões podem parecer insanos), embora se algo exigir, não tenha medo de desativar alguns verifique (normalmente com uma diretiva dentro de um comentário) temporariamente, se você pode justificá-lo.

O que acontece nas funções / métodos , além do listado acima, não precisa ser consistente de forma alguma, desde que seja um bom código por si só . Um código bom, compreensível e bem comentado é muito importante, mas depois disso, se as ferramentas de análise estática e de compilador acharem que é um código consistente, isso é consistente o suficiente.


Sobre os novos recursos de idioma, como o LINQ (bem, isso não é exatamente novo), a única coisa que precisa ser considerada é: será que uma nova versão suficiente da linguagem / bibliotecas será usada em qualquer lugar onde o código será usado? Em caso de dúvida, atenha-se aos recursos que são conhecidos por serem compatíveis. Obviamente, isso não impede que você faça uma atualização de versão em todo o sistema, para poder começar a usar as novas coisas legais.

E todo desenvolvedor que trabalha com código deve se manter atualizado, para que qualquer desenvolvedor .NET conheça o LINQ e, caso contrário, seja forçado a aprender, para seu próprio bem (você nunca sabe quando procurará um novo emprego neste negócio, por um motivo ou outro).

hyde
fonte
6

A resposta pra isso é simples.

A consistência é de suma importância.

mas vem com uma ressalva ...

Você e seu colega de trabalho provavelmente estão obcecados com o tipo errado de consistência

As implementações são descartáveis. Eles podem ser completamente revisados ​​com vários graus de facilidade, dependendo da qualidade e abrangência do conjunto de testes. Preocupar-se com coisas como "Isso deve ser uma propriedade?", "Esse código não deve usar LINQ em vez de uma construção de nível inferior?" é de valor duvidoso. É muito difícil vincular quaisquer medidas ao valor da consistência no nível da implementação. Uma pergunta muito melhor a ser feita nesse nível é "Esse código funciona como anunciado?". A consistência da implementação de TL; DR é onde as "mentes pequenas" entram em ação.

Por que a consistência não é tão importante aqui? As implementações geralmente têm um pequeno número de colaboradores. A maioria dos métodos é escrita e nunca é tocada novamente. Do código restante, o número de métodos que têm dois colaboradores quase certamente com maioria. Esse padrão continua ad infinitum . Nesse contexto, a consistência simplesmente não é tão importante. Se o prazo de validade do código for bem pequeno ( alguns anos ), os ganhos com consistência agressiva provavelmente não serão um fator.

Isso não quer dizer que você deva enlouquecer em suas implementações. Em vez disso, é preciso dizer que um design simples, limpo e agradável será uma ordem de magnitude mais valiosa para o seu hipotético mantenedor futuro do que o método de consistência de placas de caldeiras bobo por método. Isso nos leva ao ponto real ...

APIs não são descartáveis.

Esse é todo o nível de código das APIs, serviços da web, SDKs etc. Esses devem, devem, DEVEM ser consistentes. Os ganhos de produtividade com essa variedade de consistência são enormes por vários motivos:

Testes de integração:

Se você mantiver a API consistente, poderá criar conjuntos de testes de integração. Isso permite que os desenvolvedores troquem livremente os detalhes da implementação e obtenham validação imediata. Quer trocar sua porcaria de colegas de trabalho pelo LINQ? Os testes de integração são executados? Ele também fornece validação ao se preparar para ir para a produção. Como os computadores são rápidos, um único laptop pode realizar o trabalho de mil testadores executando tarefas comuns. É o equivalente a aumentar consideravelmente o número de funcionários da sua organização.

Produtividade

Quando as APIs são consistentes, você pode adivinhar sobre como usar uma API apenas seguindo o que aprendeu sobre o uso de outras partes da API. Isso ocorre porque a API oferece uma aparência e sensação natural e consistente. Isso significa que seu cliente gasta menos tempo vasculhando a documentação. A integração é mais fácil e mais barata. Menos perguntas são feitas às pessoas que desenvolveram a API. Consistência faz de todos um vencedor

Por que a consistência é importante nesse cenário? Porque as APIs têm o problema exatamente oposto das implementações. O número de pessoas que os utiliza normalmente é muito maior que o número de pessoas que contribuem para suas implementações. Pequenos ganhos com um pouco de consistência são multiplicados e os custos de manutenção dessa consistência são amortizados.

Conclusão

Consistência é cara. Em face disso, diminui a produtividade. Ele restringe os desenvolvedores e dificulta suas vidas. Ele impõe limitações às maneiras pelas quais eles podem resolver um problema, às vezes forçando-os a resolvê-lo de maneira não ideal. Isso geralmente ocorre por razões que eles não entendem, são mal concebidos ou não têm acesso a (contratos, políticas organizacionais ou interorganizacionais maiores).

Raymond Hettinger fez alguns pontos excelentes em sua palestra no Pycon 2015 sobre o uso do guia de estilo PEP8 para equipes de programadores de python. Ele mostrou que a obsessão pela consistência estilística em um pedaço de código fazia com que os revisores de código perdessem sérias falhas de lógica e design. Sua hipótese pode ser resumida, pois é fácil encontrar inconsistências estilísticas; determinar a qualidade real de um pedaço de código é difícil

O ponto aqui é ser crítico. Identifique onde a consistência é importante e proteja-a agressivamente. Onde não é importante, não perca seu tempo. Se você não puder fornecer uma maneira objetiva de medir o valor da consistência (nos casos acima, "número efetivo de funcionários", custo em função da produtividade) e não puder demonstrar que os retornos são substanciais, é provável que esteja fazendo um desserviço para sua organização.

nsfyn55
fonte
4

Você não conhece o LINQ? Nesse caso, meu código não seria mais sustentável para meus colegas desenvolvedores se eu não o usasse?

A linguagem C # ainda está evoluindo. Se as pessoas não aprendessem as alterações do C # 1, elas estariam perdendo:

  • Genéricos
  • Parciais
  • Métodos anônimos
  • Iteradores
  • Tipos anuláveis
  • Propriedades automáticas
  • Tipos anônimos
  • Métodos de extensão
  • Ling
  • Lambdas
  • Métodos assíncronos

Esta é apenas uma pequena seleção de recursos comuns encontrados no artigo da Wikipedia. O que quero dizer é que, se os desenvolvedores não aprenderem, a base de código permanecerá no vácuo. É de se esperar que seus desenvolvedores melhorem continuamente e que a base de código evolua, suportada por um conjunto de testes completo. Você se lembra de como era ruim no .NET 1 implementar propriedades manualmente.

O Linq facilita a vida. Use-o onde puder. Você pode motivar os membros da sua equipe.

Então, até que ponto os padrões fazem parte do estilo do código e onde devemos traçar a linha entre permanecer consistente e fazer melhorias?

As melhorias são graduais. Para mim, não faz sentido manter um código de estilo antigo que seja menos legível e potencialmente menos sustentável. Os membros da sua equipe devem ser capazes de descobrir o que está acontecendo, mesmo que não possam escrever.

Sam
fonte
2
Eu concordo plenamente com o que você está dizendo, mas minha pergunta é menos sobre os novos recursos de linguagem e a responsabilidade dos desenvolvedores de aprendê-los, mas a questão mais geral de resolver problemas semelhantes de maneiras diferentes em uma área relativamente pequena de uma base de código. Mencionei o LINQ porque é um bom exemplo de como usar uma maneira diferente de resolver um problema. Meu colega tem prazer em usar novos recursos de idioma, mas somente se eles não forem contra o código do código no qual ele está trabalhando.
Robert Johnson
@RobertJohnson Gostaria de salientar ao seu colega que a manutenção supera a consistência; portanto, mesmo que algo "vá contra o grão" do código existente, se for mais sustentável, você deve fazê-lo. Duvido que o uso da antiga DAO seja mais sustentável do que o Linq.
Andy
3

Você deve ser consistente, mas não necessariamente com o código antigo. Ou seja, sua equipe deve concordar com a maneira correta de fazer algo e usá-la sempre que um novo código for escrito ou quando forem feitas alterações substanciais.

Dessa forma, você colhe a maioria dos benefícios da consistência (em particular, não importa quem escreve o código), mas ainda pode melhorar se a equipe obtiver um benefício claro fazendo as coisas de outra maneira.

Em um mundo ideal, reescreveríamos todo o código antigo para estar de acordo com o novo caminho certo. Embora isso não seja economicamente viável, deve fazer sentido técnico (ou então, o novo caminho não é o melhor), e seu plano de longo prazo deve ser atualizá-lo. Se isso não vale a pena, não se preocupe com o novo caminho. Em outras palavras, deve haver uma clara vantagem na nova maneira de considerá-la correta.

meriton - em greve
fonte
1

Eu acho que é importante seguir um estilo de código em um projeto, por exemplo. convenções de nomes, recuos etc.

Não concordo que você deva se limitar a usar novas construções de linguagem ou padrões de design simplesmente porque seus colegas não as entendem ou porque não foram usadas no projeto antes.

Claro que essas coisas devem ter boas razões para usá-las, por exemplo. desempenho ou brevidade, se apropriado.

ozz
fonte
Para que foi o menos?
Ozz
0

Eu seria extremamente cuidadoso seguindo cegamente os padrões atuais de design. Obviamente, quando não há uma desvantagem significativa, deve-se sempre tentar seguir padrões semelhantes em toda a base de código.

No entanto, é muito fácil entrar em circunstâncias em que a maioria dos desenvolvedores sente que é uma "prática recomendada" seguir os padrões ruins existentes, levando a situações em que bons desenvolvedores escrevem códigos ruins e inatingíveis.

Por outro lado, a consistência é importante. Ao lidar com enormes bases de código, é extremamente útil tentar seguir padrões semelhantes para que, embora os desenvolvedores não tenham lido cada centímetro da base de código, eles saibam o que esperar.

Portanto, acredito que é melhor estabelecer e atualizar diretrizes sobre quais padrões os desenvolvedores devem seguir, se possível. Dessa forma, qualquer novo código é consistente com outro novo código.

user606723
fonte
-1

Temos reuniões técnicas regulares, bastante informais, que qualquer um de nós pode realizar. Se encontrarmos uma nova técnica, apresentamos na reunião. Você geralmente tem uma boa noção de quão bem a idéia é aceita e compreendida por seus colegas. Isso ajuda você a decidir se é aconselhável incluí-lo em seu código, ajuda outras pessoas a decifrá-lo se você o fizer e também estabelece a pessoa 'ir para' se outros precisarem de ajuda com esse estilo. Se ninguém reinventasse a roda, ainda estaríamos arrastando coisas nos troncos.

Formiga
fonte
Para que foi o voto negativo?
Ant
Eu não era o menos votado, mas isso realmente não parece direcionado para a pergunta. É um processo de equipe que pode ser aplicado a qualquer coisa, em vez de uma discussão sobre questões de estilo de codificação. Além disso, você não quis dizer "inventou a roda" em vez de "reinventar a roda"?
Suponho que seja semântica se um tronco é ou não um tipo de roda, mas gira, reduz o atrito, tem uma superfície em contato sucessivo com o solo e é redonda. Estou certo de que existem exemplos melhores do que quero dizer - deixarei isso como um exercício para o leitor.
Ant