Contexto: sou desenvolvedor corporativo em uma loja exclusiva para MS.
Alguém pode recomendar uma boa maneira de medir objetivamente a capacidade de manutenção de um pedaço de código ou aplicativo?
Por que manutenção : Estou cansado das métricas de "qualidade" do meu grupo, girando apenas em torno do número de bugs e da cobertura do código. Ambas as métricas são fáceis de jogar, especialmente quando você não está medindo a capacidade de manutenção. A falta de visão e os prazos resultam em enormes quantidades de dívida técnica que nunca são realmente resolvidas.
Por que a capacidade de medir objetivamente : trabalho em um grande grupo empresarial. Se você não pode mensurá-lo objetivamente, não pode responsabilizar as pessoas por elas ou fazê-las melhorar. As medidas subjetivas não acontecem ou não ocorrem de forma consistente.
Estou analisando as métricas de código do VS2010 , mas estou imaginando se alguém tem outras recomendações.
fonte
Respostas:
A realidade é que, a menos que você tenha evidências concretas de que o código não pode ser mantido como está ... ou seja, a correção de um bug causou N horas de tempo desnecessário devido a um código que não pode ser mantido; então, ter uma perna para sustentar será inerentemente difícil. Neste exemplo, poderia ter sido causado pelo fato de uma metodologia excessivamente complexa ter sido usada quando algo muito mais simples seria suficiente. Entrar em uma área onde você tenta medir metodologias, paradigmas e melhores práticas se torna cada vez mais difícil, com pouco ou nenhum ganho a longo prazo.
Infelizmente, seguir esse caminho é um caminho para lugar nenhum. Concentre-se na descoberta de problemas raiz que tenham mérito substancial e não estejam ligados a sentimentos pessoais sobre um problema, como a falta de convenções de nomenclatura na base de código e encontre uma maneira de medir o sucesso e as falhas em torno desse problema raiz. Isso permitirá que você comece a montar um conjunto de blocos de construção a partir dos quais você poderá começar a formular soluções.
fonte
Bem, a medida que uso, ou gosto de pensar que uso, é esta:
Para cada requisito funcional independente, de uma linha, do tipo pegar ou largar, faça um instantâneo da base de código antes de implementá-la. Em seguida, implemente-o, incluindo a localização e correção de bugs introduzidos no processo. Em seguida, execute um
diff
entre a base de código antes e depois. Odiff
mostrará uma lista de todas as inserções, exclusões e modificações que implementaram a alteração. (Como inserir 10 linhas consecutivas de código, há uma alteração.) Quantas alterações ocorreram? Quanto menor esse número, normalmente, mais sustentável o código.Eu chamo isso de redundância do código fonte, porque é como a redundância de um código de correção de erros. As informações estavam contidas em 1 pedaço, mas foram codificadas como N pedaços, que todos precisam ser feitos juntos, para serem consistentes.
Eu acho que essa é a idéia por trás do DRY, mas é um pouco mais geral. O motivo pelo qual essa contagem é baixa é boa: se forem necessárias N alterações para implementar um requisito típico, e como programador falível, você obtém apenas N-1 ou N-2 deles corretamente no início, você inseriu 1 ou 2 erros. Além do esforço de programação O (N), esses bugs precisam ser descobertos, localizados e reparados. É por isso que N pequeno é bom.
Manter não significa necessariamente legível para um programador que não aprendeu como o código funciona. A otimização de N pode exigir algumas ações que criam uma curva de aprendizado para os programadores. Aqui está um exemplo. Uma coisa que ajuda é se o programador tentar antecipar mudanças futuras e deixar instruções no comentário do programa.
Eu acho que quando N é reduzido o suficiente (o ideal é 1), o código fonte se parece mais com uma linguagem específica de domínio (DSL). O programa não "resolve" tanto o problema como "declara" o problema, porque, idealmente, cada requisito é apenas reapresentado como um único pedaço de código.
Infelizmente, não vejo pessoas aprendendo muito a fazer isso. Em vez disso, eles parecem pensar que substantivos mentais devem se tornar classes, e verbos, métodos, e tudo o que eles precisam fazer é girar a manivela. Isso resulta em código com N de 30 ou mais, na minha experiência.
fonte
A manutenção não é realmente mensurável. É uma visão subjetiva de um indivíduo com base em suas experiências e preferências.
Para um pedaço de código, forneça uma idéia de um design perfeito .
Então, para qualquer desvio do código real daquele perfeito, diminua o valor de 100 em algum número. Pelo que exatamente depende das consequências de uma abordagem não perfeita escolhida.
Um exemplo:
Um pedaço de código lê e importa algum formato de dados e pode mostrar uma mensagem de erro se algo estiver errado.
Uma solução perfeita (100) teria mensagens de erro mantidas em um local comum. Se a sua solução os codificar como constantes de string diretamente no código, você deve digitar 15 off. Portanto, seu índice de manutenção se torna 85.
fonte
Um resultado do código difícil de manter é que você levará mais tempo (em "média") para corrigir bugs. Portanto, à primeira vista, uma métrica parece ser o tempo necessário para corrigir um bug desde quando é atribuído (ou seja, a correção é iniciada) até quando está "pronto para teste".
Agora, isso só funcionará realmente depois que você tiver corrigido um número razoável de bugs para obter o tempo "médio" (o que isso significa). Você não pode usar a figura para nenhum bug específico, pois a dificuldade de rastrear não depende apenas da "capacidade de manutenção" do código.
Obviamente, à medida que você corrige mais bugs, o código se torna "mais fácil" de manter, à medida que você o melhora (ou pelo menos deveria ser) e você está se familiarizando com o código. Contrariando isso, os bugs tendem a ser mais obscuros e, portanto, ainda mais difíceis de rastrear.
Isso também sofre com o problema de que, se as pessoas tenderem a executar correções de bugs para obter uma pontuação mais baixa, causando novos bugs ou não corrigindo adequadamente o existente, levando a mais trabalho e possivelmente a códigos ainda piores.
fonte
Acho que as métricas de código do Visual Studio são bastante decentes por fornecer uma métrica rápida de "manutenção". 5 métricas principais são capturadas:
O Índice de Manutenção é o que eu acho útil. É um índice composto, baseado em:
Ocasionalmente, examinarei meus métodos com um baixo Índice de Manutenção (baixo = ruim para este). Quase sem falhas, os métodos no meu projeto com o menor Índice de Manutenção são os que mais precisam ser reescritos e os mais difíceis de ler (ou manter).
Consulte o white paper para obter mais informações sobre os cálculos.
fonte
Dois que serão significativos são a complexidade ciclomática e o acoplamento de classes. Você não pode eliminar a complexidade, tudo que você pode fazer é particioná-la em partes gerenciáveis. Essas duas medidas devem fornecer uma idéia de onde o código de difícil manutenção pode ser localizado ou, pelo menos, onde parecer mais difícil.
A complexidade ciclomática é uma medida de quantos caminhos existem no código. Cada caminho deve ser testado (mas provavelmente não é). Algo com uma complexidade acima de 20 deve ser dividido em módulos menores. Um módulo com uma complexidade cromática de 20 (pode-se duplicar isso com 20
if then else
blocos sucessivos ) terá um limite superior de 2 ^ 20 caminhos para testar.O acoplamento de classe é uma medida de quão fortemente vinculadas são as classes. Um exemplo de código incorreto com o qual trabalhei no meu empregador anterior inclui um componente "camada de dados" com cerca de 30 itens no construtor. A pessoa "responsável" por esse componente continuou adicionando parâmetros de negócios e da camada de interface do usuário às chamadas de abertura / construção até que se tornou realmente uma grande bola de barro. Se a memória me servir corretamente, houve cerca de 15 chamadas novas / abertas diferentes (algumas já não são mais usadas), todas com conjuntos de parâmetros ligeiramente diferentes. Instituímos revisões de código com o único objetivo de impedi-lo de fazer mais coisas como essa - e para evitar parecer que estávamos destacando-o, revisamos o código de todos na equipe, então perdemos cerca de meio dia por 4-6 pessoas todos os dias porque nós não
fonte
No final das contas, a manutenção só pode realmente ser medida após a necessidade , não antes . Ou seja, você só pode dizer se um pedaço de código é passível de manutenção quando é necessário mantê-lo.
É relativamente óbvio medir como foi fácil adaptar um pedaço de código às mudanças nos requisitos. É quase impossível medir com antecedência, como responderá às mudanças nos requisitos. Isso significaria que você precisa prever alterações nos requisitos. E se você puder fazer isso, deverá obter um preço nobel;)
A única coisa que você pode fazer é concordar com sua equipe, com base em um conjunto de regras concretas (como os princípios do SOLID), que todos acreditam que geralmente aumentam a capacidade de manutenção.
Se os princípios forem bem escolhidos (acho que ir com o SOLID seria uma boa opção para começar), você pode demonstrar claramente que eles estão sendo violados e responsabilizar os autores por isso.
Você terá muita dificuldade, tentando promover uma medida absoluta de manutenção, enquanto convence cada vez mais a sua equipe a seguir um conjunto acordado de princípios estabelecidos, realistas.
fonte
E a dívida técnica que é "superada pelos eventos"?
Eu escrevo códigos ruins e apresso para a produção.
Você observa - corretamente - que não é sustentável.
Esse código, no entanto, é a última rodada de recursos para uma linha de produtos que será desativada porque o contexto jurídico mudou e a linha de produtos não tem futuro.
A "dívida técnica" é eliminada por uma mudança legislativa que torna tudo obsoleto.
A métrica "manutenibilidade" passou de "ruim" para "irrelevante" devido a considerações externas.
Como isso pode ser medido?
fonte
A próxima melhor coisa para uma revisão de código por pares é criar uma arquitetura capaz de trabalhar antes de codificar uma unidade ou produto. O refator vermelho-verde é uma maneira bem legal de fazer isso. Peça a um Sr. que crie uma interface viável e divida o trabalho. Todos podem pegar sua peça do quebra-cabeça e esverdear o caminho da vitória. Depois disso, uma revisão e refator de código por pares estariam em ordem. Isso funcionou muito bem em um grande produto do passado em que trabalhei.
fonte
Questionário
Que tal fazer um questionário anônimo para os desenvolvedores, para preencher uma vez por mês mais ou menos? As perguntas seriam algo como:
(Sinta-se à vontade para adicionar perguntas adicionais que julgar úteis na avaliação da manutenção nos comentários e eu as adicionarei.)
fonte
Posso pensar em duas maneiras de analisar a manutenção (tenho certeza de que há mais esperançosamente que outras pessoas possam apresentar boas definições.
Modificação sem entendimento.
Um conserto de erros pode entrar no código e corrigir um problema sem precisar entender como o sistema inteiro funciona.
Isso pode ser conseguido fornecendo testes de unidade abrangentes (testes de regressão). Você deve poder verificar se qualquer alteração no sistema não altera a maneira como o sistema se comporta com qualquer entrada específica específica.
Nessa situação, um consertador de erros deve poder entrar e consertar um erro (simples) com apenas um conhecimento mínimo do sistema. Se a correção funcionar, nenhum dos testes de regressão deve falhar. Se algum teste de regressão falhar, será necessário passar para o estágio 2.
Modificação com entendimento.
Se uma correção de bug se tornar trivial e você precisar entender o sistema. Então, como é a documentação do sistema. Estamos não falar documentação da API externa (eles são relativamente inútil). O que precisamos entender é como o sistema funciona onde truques inteligentes (leia hacks) usados nas implementações etc.
Mas a documentação não é suficiente, o código precisa ser claro e compreensível. Para medir a compreensibilidade de um código, podemos usar um pequeno truque. Depois que o desenvolvedor terminar a codificação, dê a ele um mês para trabalhar em outra coisa. Depois, peça que eles voltem e documentem o sistema a ponto de um píer agora entender o sistema. Se o código é relativamente fácil de entender, deve ser rápido. Se estiver mal escrito, eles levarão mais tempo para descobrir o que construíram e escrever a documentação.
Então, talvez possamos criar alguma medida disso:
fonte
Costumo achar que a solução "equivalente mais curto" tende a ser mais sustentável.
Aqui, o menor significa menos operações (não linhas). E equivalente significa que a solução mais curta não deve ter pior complexidade de tempo ou espaço do que a solução anterior.
Isso significa que todos os padrões de repetição logicamente semelhantes devem ser extraídos para a abstração apropriada: Blocos de código similares? Extraia-o para funcionar. Variáveis que parecem ocorrer juntas? Extraia-os em uma estrutura / classe. Classes cujos membros diferem apenas por tipo? Você precisa de um genérico. Você parece recalcular a mesma coisa em muitos lugares? Calcule no início e armazene o valor em uma variável. Isso resultará em um código mais curto. Esse é o princípio DRY basicamente.
Também podemos concordar que abstrações não utilizadas devem ser excluídas: classes, funções que não são mais necessárias são código morto e, portanto, devem ser removidas. O controle de versão lembrará se precisaremos restabelecê-lo.
O que geralmente é debatido são abstrações referenciadas apenas uma vez: funções sem retorno de chamada que são chamadas apenas uma vez sem motivo para serem chamadas mais de uma vez. Um genérico que é instanciado usando apenas um tipo e não há motivo para ser instanciado com outro tipo. Interfaces que são implementadas apenas uma vez e não há motivo real para que alguma vez seja implementado por qualquer outra classe e assim por diante. Minha opinião de que essas coisas são desnecessárias e devem ser removidas, esse é basicamente o princípio YAGNI.
Portanto, deve haver uma ferramenta que possa detectar a repetição de código, mas acho que esse problema é semelhante a encontrar a compressão ideal, que é o problema da complexidade de Kolmogorov, que é indecidível. Por outro lado, porém, as abstrações não utilizadas e subutilizadas são fáceis de identificar com base no número de referências: uma verificação disso pode ser automatizada.
fonte
É tudo subjetivo e qualquer medida baseada no próprio código é irrelevante. No final, tudo se resume à sua capacidade de atender às demandas. Você ainda pode fornecer os recursos que estão sendo solicitados e, se puder, com que frequência essas alterações voltarão para você, porque algo ainda não está certo e qual a gravidade desses problemas?
Acabei de (re) definir manutenção, mas ainda é subjetiva. Por outro lado, isso pode não importar muito. Nós apenas precisamos satisfazer nosso cliente e apreciá-lo, é isso que estamos buscando.
Aparentemente, você sente que precisa provar ao seu chefe ou colegas de trabalho que algo precisa ser feito para melhorar o estado da base de código. Eu diria que seria suficiente você dizer que está frustrado pelo fato de que, para cada pequena coisa que você precisa alterar ou adicionar, você precisa corrigir ou solucionar outras 10 questões que poderiam ter sido evitadas. Em seguida, nomeie uma área notória e lance-a de cabeça para baixo. Se isso não suscitar nenhum apoio em sua equipe, você poderá se sair melhor em outro lugar. Se as pessoas ao seu redor não se importam, provar o seu ponto de vista não vai mudar de idéia.
fonte