-
-
Save ldaniel/2149474 to your computer and use it in GitHub Desktop.
Pessoal, gostaria de saber como vocês endereçam as questões que farei após explicar o cenário | |
abaixo. Pra mim, é muito claro que todas podem ser respondidas com um belo "depende", contudo, | |
vamos focar as respostas no que atenderia a maioria dos casos (lembrando que esse é um cenário | |
muito simplório). | |
-- CENÁRIO ----------------------------------------------------------------------------------------- | |
Um desenvolvedor codificou inicialmente a classe (escrita em C#) no gist "exemplo_antes.cs". | |
Após aplicar um refactoring, ele chegou no resultado mostrado no gist "exemplo_depois.cs". | |
Algumas métricas foram coletadas antes e depois do refactoring (vide gist "métricas.txt"). | |
Obs.: Esse código foi tirado daqui -> | |
http://whileicompile.wordpress.com/2010/09/14/what-is-too-simple-and-small-to-refactor-as-clean-code | |
-- QUESTÕES ---------------------------------------------------------------------------------------- | |
1) De modo geral, podemos ter como prática que o set de testes deve, no mínimo, ter o mesmo número | |
de testes que o resultado do indicador CC (cyclomatic complexity)? Por exemplo, se um método tem | |
5 de CC, ele deve ter no mínimo 5 testes escritos para ele. | |
Notas: a) estou considerando apenas um assert por método de test. | |
b) não considerando a eficácia do teste escrito. | |
2) Membros privados podem ser ignorados nos testes? Devemos garantir os testes pelos membros | |
públicos observando a cobertura de código dos testes. Isso é suficiente? | |
Nota: sem entrar no mérito da necessidade ou não de 100% de cobertura. A intenção aqui é | |
deduzir uma relação entre métricas (total de membros privados/publicos) e testes. | |
3) Quando, através de métricas, chegamos a conclusão que o código ficou mais complexo, é uma boa | |
estratégia considerar LoC (lines of code) como indicador para comparar "antes" e "depois"? Que | |
outras métricas vocês considerariam? | |
4) Qual a melhor "unidade" para orientarmos a escrita de testes (de unidade, claro): método, classe, | |
assembly, assunto de negócio ou outra? (estou falando aqui de "Testes de Unidade": qual unidade | |
você comumente utiliza?) |
/* Classe inicial, antes do refatoring */ | |
using System; | |
namespace CleanCode.Before | |
{ | |
public class WageCalculator | |
{ | |
public static float Calculate(float hours, float rate, bool isHourlyWorker) | |
{ | |
if (hours < 0 || hours > 80) | |
throw new ArgumentException(); | |
float wages = 0; | |
if (hours > 40) | |
{ | |
var overTimeHours = hours - 40; | |
if (isHourlyWorker) | |
wages += (overTimeHours * 1.5f) * rate; | |
else | |
wages += overTimeHours * rate; | |
hours -= overTimeHours; | |
} | |
wages += hours * rate; | |
return wages; | |
} | |
} | |
} |
/* Classes geradas, após refactoring */ | |
using System; | |
namespace CleanCode.After | |
{ | |
public abstract class WageCalculatorBase | |
{ | |
public float HoursWorked { get; protected set; } | |
public float HourlyRate { get; protected set; } | |
public WageCalculatorBase(float hours, float hourlyRate) | |
{ | |
if (hours < 0 || hours > 80) | |
throw new | |
ArgumentOutOfRangeException("Hours must be between 0 and 80."); | |
HoursWorked = hours; | |
HourlyRate = hourlyRate; | |
} | |
public abstract float Calculate(); | |
} | |
public class WageCalculatorForContractor : WageCalculatorBase | |
{ | |
public WageCalculatorForContractor(float hours, float hourlyRate) | |
: base(hours, hourlyRate) | |
{ | |
} | |
public override float Calculate() | |
{ | |
return HoursWorked * HourlyRate; | |
} | |
public static float Calculate(float hours, float hourlyRate) | |
{ | |
WageCalculatorForContractor payCalc = new | |
WageCalculatorForContractor(hours, hourlyRate); | |
return payCalc.Calculate(); | |
} | |
} | |
public class WageCalculatorForEmployee : WageCalculatorBase | |
{ | |
public WageCalculatorForEmployee(float hours, float hourlyRate) | |
: base(hours, hourlyRate) | |
{ | |
} | |
public override float Calculate() | |
{ | |
if (IsOvertimeRequired) | |
return CalculateWithOvertime(); | |
return CalculateWithoutOvertime(); | |
} | |
protected bool IsOvertimeRequired | |
{ | |
get | |
{ | |
return HoursWorked > 40; | |
} | |
} | |
protected float CalculateWithoutOvertime() | |
{ | |
return HoursWorked * HourlyRate; | |
} | |
protected float CalculateWithOvertime() | |
{ | |
float overTimeHours = HoursWorked - 40; | |
return (overTimeHours * 1.5f + 40) * HourlyRate; | |
} | |
public static float Calculate(float hours, float hourlyRate) | |
{ | |
WageCalculatorForEmployee payCalc = new | |
WageCalculatorForEmployee(hours, hourlyRate); | |
return payCalc.Calculate(); | |
} | |
} | |
} |
Type Member CC LoC | |
-------------------------------------------------------------------- | |
CleanCode.After........................................[18] [27] | |
WageCalculatorBase..............................(8) (9) | |
Calculate() 1 0 | |
HourlyRate.get() 1 1 | |
HourlyRate.set(float) 1 1 | |
HoursWorked.get() 1 1 | |
HoursWorked.set(float) 1 1 | |
WageCalculatorBase(float, float) 3 5 | |
WageCalculatorForContractor.....................(3) (5) | |
Calculate() 1 2 | |
Calculate(float, float) 1 2 | |
WageCalculatorForContractor(float,float) 1 1 | |
WageCalculatorForEmployee.......................(7) (13) | |
Calculate() 2 3 | |
Calculate(float, float) 1 2 | |
CalculateWithoutOvertime() 1 2 | |
CalculateWithOvertime() 1 3 | |
IsOvertimeRequired.get() 1 2 | |
WageCalculatorForEmployee(float, float) 1 1 | |
CleanCode.Before........................................[6] [14] | |
WageCalculator..................................(6) (14) | |
Calculate(float, float, bool) 5 13 | |
WageCalculator() 1 1 |
Nunca é tarde para entrar numa discussão entre amigos que AMAM o que fazem. =D
Concordo com o que você disse: os problemas do dia-a-dia são outros. O meu primeiro post da série sobre métricas enfatizava isso colocando a utilidade de utilizar esse tipo ferramenta na mão do arquiteto (cargo ou papel, não importa). Creio que essa seja a figura adequada para focar nesse tipo de análise, pois no geral, os desenvolvedores estão mais focados na parte e não no todo.
Hmm, cálculo fiscal complexo. Muita coisa se explica, então. :)
Discordo que os problemas do dia-a-dia sejam outros. Isso depende muito de como você resolve os problemas. Não sou escravo de métricas, mas meu painel de code coverage / cyclomatic complexity do IntelliJ fica aberto 24/7.
Valeu!
Depois que estiver tudo publicado, quero ver o resultado desse estudo. será produto? se for, cria uma licença para fins não comerciais. =D
Putz, Leandro, não há como eu discordar mais disso. Desenvolvedores estão focados na parte e não no toco? Desenvolvedores não são responsáveis por design bem feito? Que espécie de desenvolvedores são esses?
Pior que olhar essas métricas deveria ser o mínimo dentro das obrigações de qualquer desenvolvedor. =\
os que eu vejo por aí. =(
Métricas são ferramentas, não obrigação. Obrigar o desenvolvedor a alcançar certas métricas é pedir para a métrica ser corrompida.
Obrigação do desenvolvedor saber escrever código claro, desacoplado e coeso. E cabe a ele encontrar a melhor forma de fazer isso.
Desenvolvedores estão focados na parte e não no toco?
O que tentei dizer é que desenvolvimento é uma atividade com foco distinto de arquitetura.
Existe um certo nível de arquitetura que até concordo que alguns desenvolvedores não estejam tão engajados quanto outros. Apesar de crer que essa divisão de responsabilidades só gera impedância nos processos pelos interesses divergentes.
Mas isso que estamos discutindo aqui não é arquitetura, é design. E o mínimo que eu espero de um desenvolvedor é ser capaz de modelar algo sem precisar de uma forminha de bolo.
as métricas citadas são muito baratas de serem coletadas. não vejo problema algum de se tornarem parte do cotidiano do desenvolvedor, é quase como termos a janela quickwatch durante o debug... deveria ser algo natural.
Sim, realmente são bastante baratas, eu uso. Mas quando se torna obrigação, é da natureza humana deturpar a métrica. Então o foco passará a ser bater a métrica, e não gerar código de qualidade (que é o objetivo inicial).
Quem faz nossa realidade somos nós, disso eu sei, mas ainda assim vou insistir neste ponto. Ando sem muita vontade de cantar uma bela canção, mas nosso ambiente mercadológico hoje em dia não ajuda muito. Tentando fazer um trabalho de team lead, a maioria das vezes eu lido com gente que tem dificuldade em fazer o FizzBuzz. Outro dia passei como teste pra um desenvolvedor "Senior" o Mars Rover da TW e o cara insistiu até o final que o exercicio estava errado e não tinha jeito de resolver. Sei que esse mimimi todo não ajuda, mas acho que na maioria dos casos achamos que o problema esta na falta de conhecimento OO, no processo errado ou na falta de patterns ou sei-la-mais-oque.
No entanto, acredito fortemente que o problema esta no básico. O filtro de entrada hoje é muito baixo.
Falei todo esse bla bla simplesmente pelo seguinte. Métricas são importantes, úteis pra caramba e bem legais, mas não são práticas na maioria dos cenários então acho que não vale muito a pena discutir isso. É a mesma coisa que perguntar se podemos ir pra Lua. O caboclo vem e responde que sim e tudo mais, que é fácil. No entanto ninguém vai. Ou seja, na prática mesmo, apesar de ser possível, não é. Acho que a melhor analogia é o may e can em inglês. We can, but may not.
Concordo contigo. Acho que esse tipo de coisa é pra um dos poucos caras que ta ali distante do código (no dia-a-dia), mas olhando por cima pra empresa como um todo. Não sei não, mas IMO esse papel ta cada vez mais raro já que a qualidade base de tudo está cada vez pior, ainda mais com o monte de tecnologia nova que sai e que só complica mais a vida dos ja não tão bons desenvolvedores.
Bão, ignorem o velho chato aqui resmungando da vida, porque faz todo sentido o assunto.
E o mínimo que eu espero de um desenvolvedor é ser capaz de modelar algo sem precisar de uma forminha de bolo.
Métrica não é forminha de bolo, certo? O que vc quis dizer com isso?
Complementando o que o @juanplopes disse:
Métricas são números, que sem compreensão e contexto, não significam nada. Pior ainda, podem fomentar decisões questionáveis, igual estatísticas. Por isso mesmo que vejo que são ferramentas e não objetivos.
E justamente, entender a importância e como elas podem ajudar é algo muito interessante para se espalhar por aí, pois muita gente não conhece ou não entende.
Bom, acho que concordamos então. São ferramentas úteis. Ninguém obriga um desenvolvedor a usar a janelinha quickwatch, mas esperamos um código de qualidade e com o mínimo de bugs.
@tucaz não consigo deixar de lembrar das discussões do DNAD em 2008, quando tudo começou. poxa, era exatamente isso aqui. que coisa legal.
... ah saudosismo... =P
Métricas não são forminhas de bolo, mas estabelecer que elas (muitas vezes sozinhas) são obrigatórias para um bom design é nocivo.
Mas o que eu estava falando, na verdade, são desenvolvedores que não pensam em nada além da receita que seguem para desenvolver uma funcionalidade. Por isso tantas perguntas sobre DDD, repositórios, camadas, etc. no DNA. Por que a grande massa não quer ter que pensar. Quer "comprar" uma modelagem pronta na "prateleira das boas práticas" e usar.
pois é, eu continuo batendo na tecla de que métricas (as mais comuns, pelo menos) são muito baratas para serem tiradas (custo, tempo, esforço etc). acho que talvez o problema seja um pouco de tudo:
- zilhões de tecnologias e possibilidades de estudo hoje em dia
- banalização do skill necessário para um desenvolvedor
- alta oferta de empregos e poucos profissionais (bons) disponíveis
- sei lá, tudo pode ser usado como "muleta" hoje em dia...
a impressão que tenho quando olho para o grupo que está reunido aqui respondendo essas questões, é que não representamos os "fodões" da área de TI. caramba, fazer um trabalho bem feito deveria ser a meta básica de qualquer um (independente de contexto, pelo brio mesmo, poxa). no entanto, tenho que concordar com o Tuca quando ele se desanima com o atual mercado.
sim, concordo com tudo.
É "impossível" porque o desenvolvedor que ta ali do meu lado todo dia tem dificuldade em compreender como funciona um ORM, javascript básico e até HTML e CSS. Outro dia pedi pra um cara mudar uma classe CSS e ele não sabia como fazer. E isso não é nem aqui no Brasil somente. Esse caso em especifico aconteceu nos EUA então esse problema não é exclusividade nossa.
Imagina falar pra ele que o código dele tem uma complexidade enorme por causa dos caminhos que os IF's geram? Ou pedir pro cara refatorar pra deixar mais coeso? Não é prático.
Se olharmos pro indivíduo dentro do grupo, talvez até seja possível, mas disseminar esse comportamento pra um grupo de desenvolvedores que tem outras motivações é impraticável.
@ldaniel, mais uma vez vou ter que concordar com você que a discussão está interessante. Fazia tempo que eu não via algo assim, onde o pessoal não da carteirada pra fugir do assunto e provar um ponto. Way to go!
Nunca fiz um estudo formal para tentar confirmar isso, mas tenho a sincera impressão que desenvolvedores despreparados assim geram mais prejuízo que valor nos produtos em que desenvolvem.
Não que desenvolvedores iniciantes não possam trabalhar, mas por esse motivo sou a favor de code review ou (para os mais xp-fans) pair programming. É claro que se a proporção de desenvolvedores iniciantes for absurdamente maior que a de experientes, o custo disso fica inviável. E talvez seja irreal na maior parte das empresas com o mercado que temos.
Curiosamente, sem querer puxar sardinha, eu vejo que a comunidade Java tem muito mais apego às métricas que a .NET. Em .NET você tem dificuldade até em encontrar ferramentas adequadas para tirar essas métricas.
compartilho da sua impressão. Tanto é que ultimamente adotei a postura de aceitar mais trabalho (não tanto assim :P) ao invés de contratar gente despreparada que vai gerar mais trabalho do que já temos.
foi por isso que falei que sou velho chato. Com certeza não é motivo pra desanimar. Essa imagem de desanimado minha é mesmo uma imagem. Apesar dos pesares, ainda tenho energia pra tentar promover um monte de coisa bacana.
Quanto a equipes boas, sei que elas existem. Não em tantos lugares, mas também não estão em extinção. Só não consegui unir o útil ao agradável ainda ($$$). Infelizmente empresas ruins pagam mais, afinal sempre precisam de gente pra resolver problemas, né?
Acho que cheguei só um pouco atrasado kkk, mesmo recebendo no twitter o link uma refatoração de um código legado não me permitiu participar :D
@ldaniel interessante a discussão, a única refatoração que gostei foi a do @juanplopes e agora minhas respostas.
1- Sim, no mínimo, quando preciso testar códigos assim geralmente verifico a cobertura de código para garantir que passei por todo lugar.
2 - Essa depende mesmo, testo métodos privados mas ai tenho que concordar com o @juanplopes talvez tenha um probleminha em precisar testar esses caras :)
3 - Cara não sei, acho que LoC não vai ajudar a dizer que ficou mais complexo, muita linha de código simples é melhor que pouca linha de código complexo :D. E ai depende do "feeling" que o @vquaiato disse. Agora pensando... e se além de LoC considerar número de testes? Acho que nesse exemplo o número de testes para o código refatorado acabaria maior do que com o código sem refatorar.
4 - Classe
Mais uma vez parabéns, sensacional a discussão aqui, todos foram geniais
@ElemarJR @giggio
Fazendo um gancho com métricas, essa dissonância entre TCO e "o que o cliente precisa" pode ser atenuada se observarmos as métricas certas. Pense bem, tirar métricas através de ferramentas é algo muito barato (sob o ponto de vista de custo, tempo e esforço), então, chegar no balanço que o Giggio comentou é outro benefício oriundo de métricas.