Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Relação entre testes, design e métricas
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
@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

Não gosto do resultado. Acho que você acabou acrescentando complexidade desnecessária.
Eu provavelmente não criaria uma hierarquia de classes, mas separaria alguns métodos. Vou forkar e te aviso o resultado.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@giggio

Bacana vc participar! Mas o código não é meu, e a relação que quero fazer aqui é entre Métricas, Testes e Design. =)

@vquaiato

This comment has been minimized.

Copy link

@vquaiato vquaiato commented Apr 11, 2012

Depois responderei com mais calma... eu não faria esse refactoring (talvez algum outro). Mas alguns pontos:

  • e a complexidade extra adicionada para decidir quais classes filhas serão usadas? Qual métrica te revela isso? Pois a resolução de qual classe filha usar vai ter que estar em algum lugar: um componente extra, um SL, uma decisão a mais em alguma camada, etc, etc
  • 5 testes talvez seja o mínimo pelos cenários de saída do método, mas existem outras combinações e outras formas de testar. Mesmo testando "unidade" hoje eu costumo utilizar unidades de negócio e não de "engenharia". Claro que acabam existindo ambos no projeto, mas tenho procurado seguir na linha de negócio
  • sobre os testes de métodos privados, como já discuti muito no DNA, existem cenários onde são aplicáveis, principalmente se sua interface pública é muito simples e seus métodos privados englobam algoritmos mais complexos. Honestamente hoje não me preocupo com isso se minha API não é pública e é apenas de consumo dentro do próprio projeto
  • sobre o código mais complexo, através das métricas, é complicado dizer. depende bastante do feeling de quem está lendo/escrevendo. Para alguns o código refatorado é melhor, mais extensível, manutenível, testável, etc, etc, etc. Mas ler o primeiro código é way better que o segundo. Depende então do que as métricas significam para quem trabalha nesse projeto
@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

Pre: Não concordo com o refactoring que você fez. Ele resolveu apenas um dos problemas que o código original tinha. Minha versão no final.

  1. Sim, no mínimo. Entretanto existem outros pontos que aumentam a complexidade ciclomática sem ela aparecer nas métricas. Um Math.Max, é um exemplo.
  2. Membros privados, numa classe coesa devem ser sempre utilizados pelo conjunto de testes. Isto é, se um método público não utiliza um membro privado, há um problema de coesão ai, não? E se o membro privado faz operações suficientes para precisar ser testado, provavelmente você precise isolá-lo.
  3. Não utilizo métricas somente para verificar se um código ficou ou não mais complexo. As métricas ajudam, mas isso é um fator muito subjetivo. O LOC é um dos fatores que mais enganam nesse sentido, que geralmente levam a um código menor, porém geralmente mais complexo.
  4. Classe. Se você tem métodos distintos na classe que precisam ser testados isoladamente, provavelmente eles deveriam ser outra classe para manter a responsabilidade única.

Minha versão (nem sei se compila, estou no Linux):

public static class ParameterContracts
{
    public static void MustBeBetween(this decimal value, decimal lo, decimal hi) 
    {
        if (value < lo || value > hi)
            throw new ArgumentOutOfRangeException(string.Format("Hours must be between {0} and {1}.", lo, hi));
    }
}

public class WageCalculatorForContractor
{
    public decimal Calculate(decimal hours, decimal rate) 
    {
        hours.MustBeBetween(0, 80);

        return hours * rate;
    }
}

public class WageCalculatorForEmployee
{
    public decimal Calculate(decimal hours, decimal rate)
    {
        hours.MustBeBetween(0, 80);

        var regularWage = Math.Min(hours, 40) * rate;
        var overtimeWage = Math.Max(hours - 40, 0) * rate * 1.5m;

        return regularWage + overtimeWage;
    }
}
@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@vquaiato Já te falei, cara, se você testa "unidade" de negócio, isso não te dá feedback nenhum sobre design, então isso não pode ser chamado de TDD.

@vquaiato

This comment has been minimized.

Copy link

@vquaiato vquaiato commented Apr 11, 2012

eu não falei de TDD, falei?

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@vquaiato

Comentando cada ponto:

  • "e a complexidade extra adicionada para decidir quais classes filhas serão usadas? Qual métrica te revela isso?"
    Sim, é exatamente esse o tipo de questionamento que quero exercitar. No exemplo que usei o cara seguiu (na visão dele) o que o Clean Code diz. O design final ficou ruim, fato. Como as métricas poderiam auxiliar nisso?
  • Mas a CC se mantém em qualquer um dos casos, certo? A relação "número de CC x número de asserts" parece fazer sentido?
  • Então métricas de análise de instabilidade de assemblies resolveria, é isso? (vide http://www.leandrodaniel.com/post/Code-metrics-(parte-5)-A-importancia-do-contexto.aspx)
  • Concordo, contexto sempre!
@mauricioaniche

This comment has been minimized.

Copy link

@mauricioaniche mauricioaniche commented Apr 11, 2012

Oi Leandro,

Eu faria o refactoring. Só não entendi bem ali o método estático, mas generalizando o refactoring, vc fez uso de polimorfismo.

Sobre a sua tabela de métricas. IMHO, não faz muito sentido dizer que a CC de uma classe é a soma da CC dos métodos. Você não tem dois métodos sendo executados ao mesmo tempo, então não tem pq considerar esse valor. CC é uma métrica no nível de método. Talvez uma boa maneira de falar a CC de uma classe seja pegar a maior CC dentre os métodos dela. Se você analisar método a método, perceberá que a classe antes do refactoring tem CC maior.

  1. Sim, concordo que CC = N, então N é o número mínimo de testes que vc deve ter. Só não testaria métodos com CC=1 e cujo código apenas repassa a invocação de método pra alguma outra classe, ou cujo método seja um getter/setter. Nesses casos, não testaria.

  2. Sim. Em uma classe coesa, os métodos privados no fundo servem pra diminuir a CC e aumentar a legibilidade dos métodos públicos. Eu não quero testar COMO uma classe faz, mas sim O QUE ela faz. Não vejo pq então testar um método privado diretamente.

  3. Vc tem que considerar LOC, Fan Out, CC, LCOM, Instability, # de métodos, # de atributos, e assim por diante. Não é fácil; cada uma das métricas atua em um nível diferente. Gosto muito da ideia do Erik Doenerburg (não sei escrever bem). Ele tem o Toxicity Chart, onde ele tem juntar todas as métricas em um gráfico só, e achar em uma métrica maior.

Nos meus estudos de mineração de repositório de dados, uma coisa que me agrada é fazer estatísticas descritivas simples, e ver quem fica fora da média/mediana + desvio padrão, por exemplo. Acho essa uma boa maneira de analisar uma métrica.

  1. IMHO, a unidade em um sistema OO é uma classe. São classes que vc passa de um lado para o outro.
@mauricioaniche

This comment has been minimized.

Copy link

@mauricioaniche mauricioaniche commented Apr 11, 2012

"Mas a CC se mantém em qualquer um dos casos, certo? A relação "número de CC x número de asserts" parece fazer sentido?"

Trabalhando em um artigo exatamente sobre isso. Gostaria de ler?

@vquaiato

This comment has been minimized.

Copy link

@vquaiato vquaiato commented Apr 11, 2012

@ldaniel
Então sobre o primeiro ponto:

O design final ficou ruim, fato. Como as métricas poderiam auxiliar nisso?

Pois é, você tem alguma métrica que te diga que após essa mudanças X novas complexidades (classes novas, componentes novos, etc) foram adicionados? (talvez isso seja bem ligado com as minerações do @mauricioaniche). Esse seria um bom indicador: Após a mudança X tais coisas novas entraram no projeto e que antes não eram necessárias. Não sei se é uma simples análise de CC que vai te revelar isso. (e eu não acho que só o design final ficou ruim, existem novas complexidades além do design que essa refatoração trouxe).

Então métricas de análise de instabilidade de assemblies resolveria, é isso? (vide http://www.leandrodaniel.com/post/Code-metrics-(parte-5)-A-importancia-do-contexto.aspx)

Depende do que isso te revelará de verdade nesse caso. Como eu disse no item anterior, se ele revelar aquela informação ali, então é um bom ponto de partida para decidir isso.

Minha pergunta agora: _por que você está analisando isso a nível de refactoring, que tipo de informação você quer ter com isso? Que tipo de decisão você quer tomar com isso?_

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

Tá aqui meu fork:
http://bit.ly/HxQCg3

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

Parabéns man! Feliz aniversário! =D

  1. Perfeito!
  2. Se um membro público não utiliza um membro privado, é um problemaço, eu diria. Sim, se ele faz muita coisa, provavelmente precisa ser isolado. Então, métricas poderiam auxiliar dando rapidamente algumas contagens (número de métodos privados, por exemplo) e analisando código morto em métodos privados, certo? Se isso for medido paulatinamente no software, pode-se observar uma "pré-disposição" do programador em caminhar para um tipo de design ruim. Concorda?
  3. Esse é um grande risco envolvido no mundo das métricas: equilibrar e entender exatamente qual o contexto. Uma das coisas que gostaria de exercitar com essa discussão é se métricas são "seguras" para serem utilizadas por todo tipo de desenvolvedor (ou uma boa maioria).
  4. Sim, refactoring neles.
@felipero

This comment has been minimized.

Copy link

@felipero felipero commented Apr 11, 2012

São perguntas complexas e cada um terá uma opinião diferente. Todas as opiniões são válidas obviamente.

  1. Se tem 5 CC, devem ter mais testes (considerando 1 assert por teste) porque precisa testar o comportamente no sucesso e na falha, então seriam no mínimo 10 testes. Eu uso o acrônimo do Right B I C E P S para validar os casos de teste que escrevo. Veja aqui e aqui.

  2. Precisa usar o bom senso. Não gosto de mudar a estrutua de encapsulamento somente para poder testar algo. Mas isso provavelmente será necessário para casos de testes complexos. Nesse caso há alguns frameworks que "burlam" essa proteção quando em teste, mas acho isso perigoso. Eu me comformo com isso e testo via métodos públicos mesmo, apesar de não gostar. Pode usar a cobertura de testes para aferir se tudo está testado. Mas não deve haver cobrança e nem usar isso como lei.

  3. LoC não é diretamente proporcional à complexidade. Se considerarmos complexidade de entendimento e leitura do código, que afeta diretamente a manutenção, TCO e produtividade dos desenvolvedores, podemos concluir que isso:

return algo ? outra_coisa : null

é mais complexo para se entender do que isso:

if(algo == true) {
   return outra_coisa
} else {
   return null
}

Note que a opção menos complexa na minha opinião tem mais linhas de código. É visível que o cara precisa de no mínimo 4 testes para testar esse caso. O exemplo abaixo deixa isso óbvio.

  1. Não gosto da ideia de testes de unidade focarem em negócio. Acho que precisam testar o comportamento técnico de uma unidade de código. Eu utilizo "objetos" (classes, traits, structures, enums) como unidade. Testo cada um de seus métodos públicos. Então, tenho uma classe de teste unitário para cada classe da app (com exceção de controllers em aplicações MVC). Isso me dá satisfação suficiente em casos mais triviais.

Acho porém fundamental que esses testes de unidade que faço sejam menos prioritários em relação a testes que verifiquem as "operações de negócio". São testes que asseguram o comportamento de uma ou mais classes, de forma não isolada, para que eu saiba quando uma determinada operação, ação ou funcionalidade do sistema está funcionando. Eu faço isso independente da interface de usuário normalmente. (É aqui que testo os controllers, de forma integrada). Em paralelo, utilizo testes de selenium para verificar se a interface de usuário está se comportanto de acordo. Mas para interfaces, eu gosto muito de testes manuais.

É isso. Espero ter respondido, apesar de achar que depende. :P

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

@ldaniel
Cara, se o problema é ver como métricas vão te ajudar a resolver o problema, eu posso te ajudar: não vão. Nem métricas, nem o livro do Clean Code. Um bom programador conhece métricas, conhece as técnicas de clean code, mas sabe quando utilizá-las e onde. Seguir o livro pra resolver vai produzir um código mecânico. E criatividade é demandada, além dos números e das técnicas.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@mauricioaniche

Estava torcendo muito para que você participasse. =P Sei que és mestre nisso (ou quase, hehehehe)!

CC é uma métrica no nível de método

Sim, é muito melhor observar no nível de método apenas.

Talvez uma boa maneira de falar a CC de uma classe seja pegar a maior CC dentre os métodos dela. Se você analisar método a método, perceberá que a classe antes do refactoring tem CC maior.

Sim, com certeza! Métricas podem enganar com uma facilidade absurda. Por isso, qualquer ferramenta de análise, profiling, métrica e afins devem ser utilizadas como insumo para uma análise criteriosa feita por alguém com bom senso. Fico me perguntando se algum dia conseguiremos codar um algoritmo que implemente o "bom senso".

  1. Show!

  2. Show!

  3. "Fan out" é das antignas, né? Hoje seria "acoplamente eferente" e "acoplamento aferente" (pro "Fan in"), é isso? Vou pesquisar sobre o Erik Dörnenburg, não conheco muita coisa (obrigado pela dica). O Uncle Bob também fez algumas coisas (mais simplórias, imagino) e criou aquele gráfico de instabilidade de assemblies.

Nos meus estudos de mineração de repositório de dados

Eis o motivo de eu querer tanto a sua participação. =D

  1. Ok.
@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@mauricioaniche

Trabalhando em um artigo exatamente sobre isso. Gostaria de ler?

É lógico!!! \o/

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

Ok, vai... respondendo as questões..

  1. Vou na do Aniche, mas acrescento que não testaria nada muito óbvio. Tentar buscar 100% de cobertura não faz sentido.

  2. Essa discussão é velha. Depende. Métodos privados são detalhes de implementação, e podem mudar. Isso não significa que eu, que implementei, não quero saber se funcionam. Então eu diria que você tem que testar no mínimo o método publico que chama o privado, e se a complexidade do privado demandar um teste, faça, oras. Esse negócio de extrair uma classe pra poder testar o método privado é bonito e tudo, mas nem sempre isso faz sentido.

  3. Sendo bem sincero, eu acho que nenhuma métrica resolveria. Trabalhar orientado a um número não garante sucesso. Estamos medindo produtos intermediários? Porque?

  4. Eu tenho uma regra pessoal: considero que um teste é de unidade se ele não acessa nada de infraestrutura, e se não cruza fronteiras lógicas de componentes, o que em .NET significa o Assembly. Eu não isolo classes de domínio umas das outras. E considero isolar classes e métodos em linguagens estáticas insano, porque dá muito trabalho.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@giggio

Métricas ajudam muito, não basta apenas feeling e experiência do desenvolvedor. Só pra citar um exemplo, já utilizei demais (na minha época de consultor) métricas para avaliar sistemas antes de pegarmos para dar manutenção. É possível discutir de forma bem transparente com o cliente sobre a qualidade do software que ele tem nas mãos. Outro exemplo é para avaliação do design de um produto que precisa receber uma feature grande (impacto, previsibilidade no design, análise de tendências, pontos de atenção, código morto).

Os benefício são muitos, métricas ajudam bastante, mas é preciso saber usá-las. O @aniche está fazendo um produto interessantíssimo de análise de código através de mining. Ele consegue falar com muito mais propriedade (muito mais mesmo, hehhe) sobre a utilidade das métricas do que eu. =)

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@giggio

E considero isolar classes e métodos em linguagens estáticas insano, porque dá muito trabalho.

Discordo. O grande discurso a favor do TDD é justamente esse: testar as unidades isoladamente te ajuda a desacoplá-las.

@ElemarJR

This comment has been minimized.

Copy link

@ElemarJR ElemarJR commented Apr 11, 2012

O problema, como eu vejo é que as classes foram criadas para atender apenas um comportamento.

Ao meu ver, esse "desvio" dos indicadores ficará diluído na medida em que esses objetos "ganharem corpo".. o que deverá ocorrer naturalmente.

Não gostei da solução do Juan. Acho que faltou uma classe base.

;)

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

@juanplopes
Juan, então você mantem desacoplado todo seu domínio? Como faria pra testar uma classe de Nota Fiscal, que possui uma coleção de itens? Como isolaria a classe de Notas da classe Item, com uma linguagem estática? O único jeito é com interfaces, e isso pra mim normalmente significa complexidade desnecessária.

@vquaiato

This comment has been minimized.

Copy link

@vquaiato vquaiato commented Apr 11, 2012

@giggio é o famoso INota, IItem, IUser ahsuhaush

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

@vquaiato

Medo, muito medo.

@ElemarJR

This comment has been minimized.

Copy link

@ElemarJR ElemarJR commented Apr 11, 2012

@juanplopes (parabéns para você .. nesta data ..)

Discordo. Tuas duas classes já compartilham a mesma interface pública. Não vejo diferença efetiva (teórica) entre uma classe abstrata e uma interface...

:D

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@felipero

Bem-vindo, man! =)

São perguntas complexas e cada um terá uma opinião diferente. Todas as opiniões são válidas obviamente.

Sim, pra caramba!

  1. Show, valeu pelas indicações, lerei!

  2. Sim, acaba sempre caindo no que comentei no início: um baita "depende". =)

  3. O exemplo do IF ternário é bacana, pois mostra visões diferentes sobre o que não deveria ser complexo: legibilidade? tamanhho? design?

  4. Show!

Sim, testes e métricas são ferramentas muito úteis para atingirmos o produto final, código. E sim, depende mesmo. Mas o que queria com esse gist era exercitar um raciocínio sobre a relação entre métricas, testes e design.

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@ElemarJR

Eu vejo. Classe abstrata é implementação, interface não. Interface não gera dependência de código executável, somente de declaração.

@ElemarJR

This comment has been minimized.

Copy link

@ElemarJR ElemarJR commented Apr 11, 2012

@giggio @vquaiato

Qual o problema de usar as instâncias concretas (das classes concretas), desde que o teste se restrinja a SUT?! Juro que não entendi.

@vquaiato

This comment has been minimized.

Copy link

@vquaiato vquaiato commented Apr 11, 2012

@ElemarJR eu só fiz uma piada :P

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@giggio

Você pode criar interfaces, mas concordo que é overkill. O ponto aqui é manter a inversão de controle e se o seu objeto é a composição de outros, injete os outros e teste isoladamente. Não quero que meu teste dependa do comportamento da nota E dos itens. E isso é fácil de resolver mesmo com uma linguagem estática: use mocks (ai, @vquaiato, você não estava esperando isso?).

Pode haver casos onde você não queira fazer isso, por achar que é exagero, mas sempre se pagará um preço por ter que corrigir os testes da nota fiscal quando o comportamento dos itens mudar.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@giggio @vquaiato @ElemarJR @juanplopes @mauricioaniche @felipero

Caros, muito obrigado pela partipação, todos opinaram sobre as questões que coloquei e todas as considerações são válidas! Vou compilar tudo num post que colocarei na série que estou escrevendo sobre code metrics (claro, com os devidos créditos a cada autor. a menos que alguém se oponha).

Abraços,

Leandro Daniel

P.S.: Sintam-se livres para continuar o debate sobre refactoring ou o que mais surgir. ;)

@vquaiato

This comment has been minimized.

Copy link

@vquaiato vquaiato commented Apr 11, 2012

@ldaniel e all(@giggio @vquaiato @ElemarJR @juanplopes @mauricioaniche @felipero)

manda uma pergunta de DDD aí mano, é mais legal :D

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@vquaiato

Minha pergunta agora: por que você está analisando isso a nível de refactoring, que tipo de informação você quer ter com isso? Que tipo de decisão você quer tomar com isso?

Da mesma forma que você pode obter feedback sobre seu design utilizando testes, você pode fazê-lo usando métricas. O refactoring é quase que uma metáfora usada no cenário para dizer: código é um organismo vivo. Poderia ser adição de features, manutenção corretiva, manutenção evolutiva... qualquer coisa.

@vquaiato

This comment has been minimized.

Copy link

@vquaiato vquaiato commented Apr 11, 2012

@ldaniel sim acho que isso eu entendi... mas meu ponto é: isso é pós-refactoring... vc daria rollback em todo um trabalho após ter sido feito depois de rodar essa análise ou você tem como tirar essas métricas antes de realizar essas coisas?

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

@juanplopes
Pois é, só que mocks e stubs tem comportamento limitado. Eles precisam que um método seja virtual pra que você possa sobrescrevê-lo, e isso gera uma série de problemas de design e desempenho. Eu sei que seria pré-otimização evitar métodos virtuais, mas que existe o problema existe. O que me incomoda mesmo é marcar um método que antes seria private com protected virtual só pra poder testar.
E injeção não resolve, porque o comportamento da classe Item ainda afetaria o resultado dos testes da classe de Nota.
Por esses motivos, eu prefiro testar uma unidade maior. Geralmente, quando estou testando a camada de domínio, os testes são sobre agregações, geralmente focando na raiz.

E @ldaniel, você achou que se você falasse pra pararmos, iriamos parar de discuitir? Você tem ideia das pessoas que estão aqui? Elas só param quando chegar a cerveja! haha...

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

Tinha esquecido que o C# escolheu por colocar os métodos como sealed por padrão. Sempre detestei isso. :)

Mas esse argumento de que callvirt tem performance tão pior que call é historinha do Anders. O JIT é completamente capaz de otimizar essas chamadas. Funciona na JVM, por que não funcionaria na CLR?

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@vquaiato o ideal é ir tirando fotografias ao longo do exercício de desenvolvimento, dá pra "pescar" tendências e manias dos desenvolvedores. eu, não daria rollback no trabalho, apenas refatoraria.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@giggio será? se o github/gist fornecesse cerveja, aí que as discussões seriam longas de verdade (e divertidas, como essa). =D

@mauricioaniche

This comment has been minimized.

Copy link

@mauricioaniche mauricioaniche commented Apr 11, 2012

galera, sem desfocar a discussão, por favor. estou tentando acompanhar, mas estou lendo 300 msgs que nao tem nada a ver com o tema que o leandro quer discutir... tah dificil continuar..

@ElemarJR

This comment has been minimized.

Copy link

@ElemarJR ElemarJR commented Apr 11, 2012

Voltemos ao ponto, então.

Para mim, os indicicadores não estão errados. O código realmente ficou mais difícil de entender e, consequentemente, de manter.

Meu argumento fundamental é que essa "complexidade" diminuirá na medida em que os tipos ficarem mais complexos.

A pergunta que fica é: Fazer assim, de começo, não é antecipação?!

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@ElemarJR

O cenário foi apenas um pretexto para pensar na relação entre métricas, testes e design. Como vimos com a opinião de todos (creio que só o @giggio discordou) existe uma forte relação entre esses três pilares. Tanto métricas quanto testes são ferramentas úteis para chegarmos no objetivo final: código com alto valor.

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

@ldaniel
Putz, se for pra falar de valor, aí é que discordo mesmo. Não vejo nenhuma relação entre métricas, design, e testes com o valor do código. Podemos falar de qualidade, facilidade de manutenção, essas coisas. Valor é outra história...

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@giggio discorra.

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

Você pode fazer um código com belas métricas, com 100% de cobertura de código, belo design, e não entregar o que o cliente queria. Qual o valor desse código? Zero.

@ElemarJR

This comment has been minimized.

Copy link

@ElemarJR ElemarJR commented Apr 11, 2012

@giggio

Medo! Muito medo! Nunca me preocupei em entregar o que o cliente quer. Só entrego o que o cliente precisa.

De qualquer forma, sou quase extremista ao pensar que um código bem escrito e no caminho de atender o que o cliente precisa TEM mais valor do que um outro mal escrito e que já "atende" o cliente.

Por quê? ...

Porque as necessidades do cliente mudam. Se o código não acompanhar ...

Custo Total de Propriedade = Custo para Desenvolver + Custo para manter

Se o código for ruim, o custo tende ao infinito.

:D

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@giggio ah, sim, claro! concordo. não estava pegando tão ao pé da letra. como disse, são ferramentas úteis, mas utilizá-las não garante nada (e nem qualquer técnica, metodologia, framework, etc. garante)

@giggio

This comment has been minimized.

Copy link

@giggio giggio commented Apr 11, 2012

@ElemarJR
Agora eu que tenho medo... Você acha que sabe o que o cliente precisa? Quem diz isso é alguém que fez uma profunda análise de negócio.
Lógico que eu conheço o conceito de TCO, dou aula disso, é um dos tópicos do PSD. Mas deve haver um balanço entre a qualidade do código e as metas de negócio. Dívida técnica é aceitável desde que seja consciente. Não considerar esse caminho é irresponsabilidade do time.
Mas estamos desviando. Com isso acho que deu pro @ldaniel entender o que eu queria dizer.

@mauricioaniche

This comment has been minimized.

Copy link

@mauricioaniche mauricioaniche commented Apr 11, 2012

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@mauricioaniche

Mau, agradeceria se você conseguisse os nomes e indicações completas das pessoas citadas, gosto muito do assunto e seria útil pra mim acrescentar essas experiências. Estou tentando, de maneira autodidata, estudar mining (até coloquei algo no github -> https://github.com/ldaniel/PoC_TextMining). Confesso que tenho tido dificuldade por não ter uma boa base de matemática (algo que já pensei resolver fazendo algum tipo de ensino formal/acadêmico).

Sobre buscar valores médios, isso não pode sofrer influência do tipo de sistema avaliado? Por exemplo, se avaliarmos a evolução do desenvolvimento de 100 frameworks, provavelmente, encontraremos em suas métricas várias tendências e comportamentos (consequentemente, um mesmo padrão de valor médio). Mas se levarmos isso para o desenvolvimento de código que trabalhe com parelelismo, essas médias estariam "furadas", não?

@tucaz

This comment has been minimized.

Copy link

@tucaz tucaz commented Apr 11, 2012

Estou chegando tarde na discussão, mas não importa muito mesmo já que não vou agregar valor nenhum.

Tentei ler todos os comentários e acho que pelo lado técnico que o Leandro queria todo mundo estressou bastante os pontos possíveis. Então vou puxar outro lado da discussão.

Confesso que deu preguiça de ler a solução mais elegante (com polimorfismo), mas de fato ela é a mais elegante e deixa a intenção mais clara (pelo fato de ter nomes verbosos) e permitiria os testes mais bacanas. No entanto, pra um cenário de uma aplicação onde essa é a regra de negócio mais complexa que a aplicação tem, acho que é overkill. Como o @vquaiato falou, ficou faltando a parte do código que iria decidir qual estrategia aplicar.

No final, fica uma salada grande de classes pra resolver um problema, que na maioria das vezes nem vai ser resolvido dessa forma.

Acho que nunca vi um cenário como esse onde as regras são resolvidas só com informações que as classes possuem e que permita essa modelagem mais bacana (também, só trabalho com CRUD e Sharepoint então já viu né). Geralmente calculos deste tipo estão espalhados no meio de um monte de código de infra-estrutura, pois existe mais um bucado de variáveis envolvidas que vem de diversas fontes e testes ficam praticamente inviaveis.

De qualquer forma, acho muito bacana a idéia da discussão, apesar desse tipo de discussão ser mais téorica e instrutiva do que prática, já que infelizmente nossos problemas com código vem muito antes disso, em código básico.

TL;DR; Métricas e toda essa discussão de design são bem supimpa, mas no dia-a-dia os problemas são outros.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@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.

@mauricioaniche

This comment has been minimized.

Copy link

@mauricioaniche mauricioaniche commented Apr 11, 2012

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@tucaz

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.

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@mauricioaniche

Hmm, cálculo fiscal complexo. Muita coisa se explica, então. :)

@tucaz

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.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@mauricioaniche

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

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@ldaniel

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?

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

Pior que olhar essas métricas deveria ser o mínimo dentro das obrigações de qualquer desenvolvedor. =\

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

os que eu vejo por aí. =(

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@ldaniel

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.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

Desenvolvedores estão focados na parte e não no toco?

O que tentei dizer é que desenvolvimento é uma atividade com foco distinto de arquitetura.

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@ldaniel

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.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

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.

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@ldaniel

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).

@tucaz

This comment has been minimized.

Copy link

@tucaz tucaz commented Apr 11, 2012

@juanplopes, @mauricioaniche:

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.

@ldaniel:

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.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

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?

@viniciushana

This comment has been minimized.

Copy link

@viniciushana viniciushana commented Apr 11, 2012

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.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

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.

@mauricioaniche

This comment has been minimized.

Copy link

@mauricioaniche mauricioaniche commented Apr 11, 2012

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@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

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@ldaniel

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.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@tucaz @mauricioaniche

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.

@ldaniel

This comment has been minimized.

Copy link
Owner Author

@ldaniel ldaniel commented Apr 11, 2012

@juanplopes

sim, concordo com tudo.

@tucaz

This comment has been minimized.

Copy link

@tucaz tucaz commented Apr 11, 2012

@mauricioaniche:

É "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!

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@tucaz

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.

@ldaniel

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.

@mauricioaniche

This comment has been minimized.

Copy link

@mauricioaniche mauricioaniche commented Apr 11, 2012

@tucaz

This comment has been minimized.

Copy link

@tucaz tucaz commented Apr 11, 2012

@juanplopes,

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.

@mauricioaniche,

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é?

@juanplopes

This comment has been minimized.

Copy link

@juanplopes juanplopes commented Apr 11, 2012

@tucaz

Vem pro Rio. Estamos contratando!

Brincadeira...

Não, é sério. Pode vir. :P

@marcioalthmann

This comment has been minimized.

Copy link

@marcioalthmann marcioalthmann commented Apr 14, 2012

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment