4 pontos por GN⁺ 2023-12-09 | 1 comentários | Compartilhar no WhatsApp
  • A partir da experiência de reescrever o código de redimensionamento de um editor gráfico com foco em eliminar duplicação, e depois ter de revertê-lo, mostra que a obsessão por código limpo pode prejudicar a facilidade real de mudança
  • A implementação existente repetia fórmulas para cada alça de Rectangle, Oval, Header e TextBlock; a nova implementação tentou eliminar a duplicação separando direções e formas para combiná-las
  • Logo após a refatoração, o tamanho do código caiu pela metade e os pontos de mudança ficaram concentrados em um só lugar, mas, quando depois fossem necessários comportamentos especiais por forma e por alça, a abstração poderia acabar atrapalhando
  • O problema maior do que o julgamento técnico foi ter feito o check-in direto no master sem consultar o colega que escreveu o código original, o que pode prejudicar a construção de confiança na equipe
  • “Limpeza” não é um objetivo, mas apenas uma ferramenta para lidar com sistemas complexos; mais importante do que a aparência do código é se a equipe consegue responder a mudanças em conjunto

Repetição no código de redimensionamento do editor gráfico

  • O canvas de um editor gráfico tinha código para redimensionar formas como retângulos e elipses usando pequenas alças nas bordas
  • A implementação funcionava, mas repetia fórmulas para calcular posição e tamanho para cada forma e direção da alça
    • Rectangle tinha métodos como resizeTopLeft, resizeTopRight, resizeBottomLeft, resizeBottomRight
    • Oval tinha métodos como resizeLeft, resizeRight, resizeTop, resizeBottom
    • Header e TextBlock também tinham seus próprios métodos de redimensionamento
  • Quando o usuário pressionava Shift, também era preciso lidar com a preservação da proporção durante o redimensionamento, o que aumentava ainda mais o número de fórmulas

Abstração para eliminar duplicação

  • A repetição parecia ocorrer em dois eixos
    • Repetição entre alças na mesma direção: Oval.resizeLeft() e Header.resizeLeft() são ambas ações de arrastar a alça esquerda
    • Repetição entre métodos dentro da mesma forma: vários métodos de Oval lidam todos com a elipse
  • A nova estrutura dividia o código em Directions e Shapes para tentar combinar comportamentos por direção e comportamentos por forma
    • Directions incluía top, left, bottom, right
    • Shapes incluía Oval, Rectangle
    • createHandle e createBox combinavam alças e formas
  • Nessa abordagem, eram criadas configurações de alças como fourCorners, fourSides, twoSides, usadas para criar Rectangle, Oval, Header e TextBlock

A ilusão logo após a refatoração

  • Depois da refatoração, o tamanho total do código caiu pela metade e a duplicação parecia ter desaparecido
  • A ideia era que, ao mudar o comportamento de uma direção ou forma específica, bastaria alterar um único ponto em vez de vários métodos
  • A alteração foi enviada para o master tarde da noite, com a sensação de ter desatado com elegância o código bagunçado de um colega

O problema revelado no dia seguinte

  • No dia seguinte, o chefe pediu para reverter a alteração; na época, era difícil aceitar, pois o novo código parecia mais limpo do que o anterior
  • Com o tempo, ficou claro que essa decisão estava certa por dois motivos
    • O código foi reescrito e enviado sem conversar com a pessoa que havia escrito o original
    • Em troca da redução da duplicação, foi sacrificada a facilidade de mudar os requisitos
  • Uma equipe de engenharia saudável precisa continuar construindo confiança, e uma grande reescrita do código de um colega pode causar um impacto sério na colaboração

O custo da abstração errada

  • Depois, passaram a ser necessários muitos casos especiais e comportamentos para várias alças de várias formas
  • Para acomodar esses requisitos na nova estrutura de abstração, o código poderia ter ficado várias vezes mais complexo
  • Em contrapartida, a estrutura original “bagunçada” mantinha cada método separado, o que a tornava relativamente fácil de modificar nesses casos
  • Eliminar duplicação de código não é gratuito; ao ganhar uma capacidade, você pode perder outra

Repensando a “limpeza”

  • Muitos desenvolvedores podem passar por uma fase de obsessão por “código limpo” e eliminação de duplicação
  • Quando você não tem confiança no código que escreveu, é fácil atribuir orgulho a coisas mensuráveis, como regras rígidas de lint, convenções de nomenclatura, estrutura de arquivos e ausência de duplicação
  • Quando se aprende a criar abstrações, pode surgir a vontade de aplicá-las sempre que se vê código repetido, acreditando que abstração é uma virtude
  • É preciso pensar profundamente sobre quais resultados reais de engenharia estão ligados à sensação de “limpeza” ou “sujeira”
  • Não importa apenas como o código parece, mas também como ele muda e é modificado junto com as pessoas da equipe

Quando deixar o Clean Code de lado

  • Clean Code não é um objetivo, mas uma tentativa de entender sistemas complexos
  • Ele pode servir como um mecanismo de defesa que ajuda a encontrar uma direção quando não se tem certeza de qual será o impacto de uma mudança na base de código
  • No começo, pode ser prazeroso simplificar código complexo extraindo funções ou refatorando classes
  • Mas não se deve parar aí e virar um “fanático por Clean Code”
  • Deixe o Clean Code servir como guia, mas, em algum momento, é preciso soltá-lo

1 comentários

 
GN⁺ 2023-12-09
Comentários do Hacker News
  • Duplicação de código às vezes pode ser aceitável, mas isso não prova que código limpo seja algo ruim
    Parece que houve um exagero no refactoring, e teria sido mais organizado se fosse só o caso de extrair para uma função “10 linhas de matemática repetida”
    O colega de equipe que não transformou essas 10 linhas em função também não agiu tão bem, então eu teria rejeitado o PR, mas não o teria reescrito por conta própria
    Reescrever o PR de outra pessoa fecha rapidamente qualquer espaço para discussão e é um insulto, além de muitas vezes você nem conhecer o contexto em que aquele código foi escrito
    Se não há padrão de código, o certo é revisar e rejeitar o PR; se deixar fazer commit com a ideia de que “código limpo não importa”, em poucos meses a base de código vira algo em que ninguém quer trabalhar
    O que se deve aprender aqui não é sobre código limpo, mas sobre comunicação e consideração

    • Reescrever diretamente o código funcional de outra pessoa não é apenas um insulto ao autor, mas também um comportamento antiprofissional, por desperdiçar tempo alterando um código que tem outro mantenedor
      Também elimina a chance de o autor original aprender como código de baixa qualidade realmente desperdiça tempo na prática
      Mesmo que a abordagem de código limpo fosse melhor, o ponto principal aqui não é o código, e sim que a pessoa desperdiçou o próprio tempo sem dar um feedback útil
      O melhor teria sido o autor original elevar o padrão do código, enquanto a outra pessoa cuidava do que realmente precisava ajustar. Em outras palavras, a resposta certa era code review
    • A lição parece mais ser “não aplique regras em excesso sem considerar os trade-offs de cada contexto”, e esse é um aprendizado que leva tempo
    • Depois de mais de 20 anos, vi que a ideia de “essas 10 linhas repetidas não foram extraídas para uma função, então isso é péssimo” costuma vir de pessoas focadas nas coisas erradas dentro de uma base de código
      Essas pessoas muitas vezes deixam de resolver o problema real para resolver o que não é problema, e ainda criam problemas futuros
      Numa primeira implementação, geralmente faz sentido não abstrair um trecho tão pequeno quanto essas 10 linhas. Um código que faz exatamente aquilo é fácil de ajustar depois, e abstrair 10 linhas por um benefício ainda não comprovado tem pouco valor
      Clean Code não tem importância nenhuma, e a maior parte das regras ou “princípios” parecidos também não. Na prática, são coisas às quais se apegam pessoas tentando justificar estruturas em colmeia que vão se acumulando com o tempo na base de código
      O resultado tende a ser um código excessivamente abstraído, difícil de mudar, lento e complicado de entender
      Se for preciso algum princípio, basta olhar para a capacidade de expressar com clareza e de forma fácil de alterar a transformação de dados que o programa realmente faz
      A maior parte dos princípios SOLID não ajuda a enxergar o que é essa transformação de dados nem como ela acontece — e às vezes até atrapalha —, então costuma ter pouca utilidade para entender o programa real
    • Acho que o texto original se expressa mal
      Pelo refactoring proposto, aquelas 10 linhas na verdade não eram idênticas; eram apenas parecidas
      A fórmula para redimensionar o canto superior esquerdo de um retângulo e a fórmula para alterar a parte inferior direita de uma elipse são diferentes. Por fora parecem semelhantes, mas em alguns pontos entra + em vez de -, por exemplo
      Nesse caso, a complexidade está na geometria em si, e “abstrair” isso só desloca a sujeira para outro lugar, sem eliminá-la de fato
      Pode até existir alguma abstração matemática esperta, como um sistema numérico especial, mas se isso não estiver embutido na linguagem, o custo de implementar operações especiais sobre inteiros básicos provavelmente será maior que o benefício obtido
  • “Código limpo” precisa de rebranding
    Ao longo de mais de 25 anos de carreira, continuei vendo uma reação interminável contra padrões de design, abstração, remoção de duplicação e coisas do tipo
    Os motivos de sempre são que abstração torna o código mais complexo, que não há tempo para escrever código limpo, ou que no fim tudo é só gosto e opinião
    Mas o objetivo do código limpo é tornar o código mais simples e mais fácil de manter quando os requisitos mudam
    O valor do software está justamente no fato de que ele pode mudar com o tempo. Se não fosse assim, seria muito mais fácil e barato usar circuitos fixos, tanto para implementar quanto para manter
    Se o refactoring não atingiu esse objetivo, e o gestor pode argumentar de forma convincente que isso dificultou a manutenção diante de futuras mudanças de requisitos, então esse código não era “limpo”
    Não ter discutido com o autor original é um problema de procedimento e etiqueta, fora do escopo de código limpo. O fato de alguém agir de forma rude e unilateral não elimina o valor do código limpo

    • Acho que a reação é contra prescrições absolutas demais
      O livro famoso Clean Code apresenta “ausência de duplicação” como condição de um sistema bem projetado, e chama duplicação de arqui-inimiga de trabalho extra, risco extra e complexidade desnecessária
      Por essa definição, remover duplicação vira sinônimo de simplificação, e isso está errado
      Remover duplicação é introduzir dependência. Se essa dependência modela bem o problema, pode ser uma boa abstração e uma simplificação; caso contrário, é só compressão disfarçada de abstração
      Aliás, essa citação vem da menção ao Simple Design de Kent Beck em Clean Code
    • “Código limpo” é usado com dois sentidos
      Um é a sensação subjetiva de que certo código é bom, e o outro é seguir o livro Clean Code e diretrizes relacionadas
      Cada vez mais gente sente que essas duas coisas diferem bastante em muitos casos. Seguir algumas diretrizes do livro pode até produzir um código nada limpo
      Num mundo ideal, todo mundo gostaria de escrever código limpo, mas está diminuindo o número de pessoas que sentem que Clean Code é um bom caminho para atingir esse objetivo
      Em especial, abstração excessiva e remoção de duplicação passaram a ser vistas por muita gente como fontes de código confuso
    • O que aprendi na indústria é que até o código escrito da forma mais elegante não resiste por muito tempo quando encontra os requisitos do produto
      Às vezes a dívida técnica já vem codificada nos próprios requisitos do produto, e não dá para consertar isso com refactoring ou boas práticas; no máximo, dá para reduzir a dor
    • Isso soa como a lógica do “nenhum escocês de verdade”
      O propósito do código limpo e os resultados reais de sua aplicação são bem diferentes. Isso porque, mesmo com os requisitos na frente, as pessoas em geral não conseguem prever muito bem o estado futuro do código
      Código limpo chama a terrível pergunta “e se?”, e isso facilmente vira uma caixa de Pandora que adia lançamentos indefinidamente
      Quase sempre é melhor programar de acordo com o que se sabe, e deixar a arquitetura ser conduzida por alguém com experiência no domínio
    • Para listar uma alternativa: a maior parte das abstrações que criamos é ruim, e mesmo as que achamos boas geralmente também são
      Muitas boas abstrações já estão em bibliotecas. Claro, infelizmente nem sempre é assim
      Más abstrações consomem tempo, e como código também é uma forma de comunicação com outras pessoas da equipe, importa muito mais como os colegas recebem essa comunicação do que o modo como eu acho que ela deveria funcionar
  • Um colega escreveu muito código na base do copiar e colar, e você refatorou depois do commit
    O colega reclamou para o chefe e o chefe brigou com você, levando à conclusão de que da próxima vez é melhor deixar a sujeira na base de código
    Há uma lição a tirar daí, mas isso não significa que copiar e colar seja melhor do que escrever funções
    A refatoração deveria ter sido enviada como uma alteração separada, marcando o autor original para revisão
    Dito isso, o fato de o colega não ter falado diretamente com você e ter escalado para o chefe, e de o chefe ter mandado reverter, é um sinal bem ruim

    • No seu lugar, eu teria mandado uma mensagem privada no Slack para o colega perguntando: “Essas funções repetem as mesmas 10 linhas de matemática; não seria melhor extrair isso para uma função separada?”
      As respostas possíveis seriam algo como “não, porque...” ou, mais raramente, “verdade, eu faço isso amanhã”
      Sem alarde, todo mundo evita problema, e alguém pode até aprender algo
    • Se foi um copiar e colar preguiçoso, então usar funções para lidar com a repetição, ou seja, abstrair, é melhor do ponto de vista de DRY
      Mas o autor acrescentou um contexto importante. Ele trocou a redução de duplicação pela possibilidade de mudança nos requisitos, e depois acabou precisando de muitos casos especiais e comportamentos por tipo de alça de forma
      Isso mostra a diferença entre DRY acidental e DRY essencial
      As bibliotecas A e B muitas vezes parecem consistentes entre si, então o código pode acabar ficando igual, mas isso pode ser só coincidência
      Mesmo que um wrapper para a lib A e um wrapper para a lib B tenham o mesmo código, se estiverem encapsulando coisas totalmente diferentes, abstrair isso piora o resultado
      O mesmo vale para “15”. Se “toda lista deve sempre mostrar 15 itens”, então faz sentido abstrair o 15; mas se for “as 15 músicas do topo da página inicial” e “15 comentários padrão abaixo da música”, juntar esses dois 15 é uma escolha pior
    • O maior sinal de alerta é ter alterado a entrega do colega sem qualquer comunicação
      O segundo sinal de alerta é que nem a própria refatoração era uma boa refatoração. Faltava modularidade, ainda havia acoplamento, e só se reduziu um pouco da duplicação
    • Marcar o autor original também pode causar problema
      Pode soar como: “O código que você escreveu era ruim, então eu reescrevi. Vai reclamar?”
      A melhor solução provavelmente seria pedir explicações no formato de uma “revisão tardia”, sugerir uma solução e torcer para que o autor original não simplesmente ignore
    • No fim do texto havia um ponto importante: em mudanças posteriores na base de código, foi preciso tratar de forma especial as alças de cada forma, e isso era difícil na estrutura refatorada, mas fácil no código repetido
      O ponto central é reconhecer que o código muda no futuro. Os requisitos mudam, e o código relacionado também muda
      Se você cria abstrações que limitam essa capacidade de mudança, acaba prejudicando sua capacidade futura de adaptar a base de código
      Especialmente em abstrações nas quais você já investiu muito tempo e esforço, pode surgir um apego emocional, e você perde ainda mais tempo tentando encaixar a mudança em uma solução elegante
      KISS está certo
  • Esse caso parece ser do tipo “ninguém gosta de sabichão”
    Isso acontece especialmente quando um desenvolvedor júnior ainda não aprendeu que o trabalho não existe só entre ele e o computador, mas entre ele, o computador e outras pessoas
    Uma versão mais limpa provavelmente teria sido bem aceita se houvesse havido uma conversa real com o autor original e um acordo sobre uma abordagem melhor
    O título parece combinar mais com aprendi a trabalhar em equipe do que com “Adeus, código limpo”

    • A expressão “ninguém gosta de sabichão” captura bem o ambiente de desenvolvimento
      Código mais complexo pode ter sido recebido como ameaça e o autor tratado como um concorrente a ser contido pela cadeia de gestão
      Se você é júnior, precisa entender que isso depende muito do contexto. Em muitos lugares de trabalho, simplicidade importa mais do que flexibilidade, evita-se recursos de linguagem confusos, e prefere-se uma forma de trabalhar e linguagens em que se passe bastante tempo diante do teclado
      Em contrapartida, filosofias que valorizam aprendizado contínuo, artesanato de software e reflexão sobre processos entram em choque total com esse ambiente
      Tentar melhorar não agrada quem não quer crescer
      As duas são escolhas de vida válidas. Software tanto pode ser um lugar onde você trabalha 7 horas e vai para casa quanto um mundo de exploração em que só a imaginação e a inteligência impõem limites
      Se você quer descobrir coisas novas, deve tomar cuidado antes de passar décadas com pessoas que têm medo do desconhecido
      Em termos mais curtos: se os incentivos seus e dos seus colegas estão desalinhados, considere sair
    • Acho que o título está bom
      Esse texto merece ser lembrado por muito tempo e já abriu os olhos de muitos desenvolvedores que caem na mesma armadilha
      Mesmo trabalhando sozinho, você pode exagerar no design de abstrações elegantes e acabar preso a elas no futuro
    • Esse tipo de problema costuma ser causado mais por sêniores embriagados pelo poder do que por júniors
  • O problema é que as mudanças foram commitadas direto na branch main sem revisão
    Toda mudança de código deveria passar por revisão de colegas, seja com programação em par ou com um PR tradicional, para impedir que algo assim escale
    O chefe era um chefe ruim. Em vez de repreender, deveria ter percebido que faltava um bom processo
    Em uma equipe com cultura de revisão de código, mesmo que um novato muito empolgado tentasse isso, um colega diria algo como “obrigado, mas isso não” ou “boa ideia, mas vamos fazer assim”
    Separadamente, acho que não se deve refatorar código sem um bom motivo. Até código ruim pode ser código que quase nunca precisará ser alterado
    O melhor momento para refatorar é quando você precisa mudar o comportamento e percebe que a estrutura atual torna isso difícil demais
    Infelizmente, aqui parece que a lição aprendida foi a errada

    • Revisão de código depois do merge também é totalmente aceitável e, em certos aspectos, até melhor
  • Não entendo por que todo mundo só fala sobre o que ele deveria ter feito no lugar. Isso é uma lição perfeitamente válida.
    Em finanças, lidamos o tempo todo com produtos até certo ponto parecidos, mas não completamente iguais. Opções sobre ações e opções de câmbio são ambas opções, têm preço de exercício e são precificadas com alguma versão de Black-Scholes, mas na prática são extremamente diferentes.
    A tentação de alguém que acabou de entrar querer unificar muito comportamento é grande, e muitas vezes acabamos na posição do chefe do post original, dizendo ao desenvolvedor jovem para se acalmar e manter as coisas separadas.
    A ideia não é se despedir de código limpo, e sim que é preciso evitar a abstração excessiva para de fato manter o código limpo.

    • O exemplo de finanças está certo, mas aqui estamos lidando com geometria.
      O que um handle genérico faz são transformações afins, ou seja, translação, rotação, escala e reflexão; se a escala for uniforme, isso é uma transformação de similaridade. Isso é garantido para funcionar com qualquer forma geométrica.
      O problema real é que não se deve abstrair algo que não se conhece bem o suficiente. Isso vale ainda mais quando você não sabe que possibilidades podem surgir no futuro.
      Por exemplo, se você acha que elipses são só círculos e elipses, leva um bom susto quando aparece o primeiro oval “de verdade”.
    • Entendo o ponto do texto, mas o título pode facilmente soar como uma desculpa para justificar código realmente bagunçado.
      Como o autor não faz uma distinção ativa entre código bagunçado e código “não clean”, outras pessoas acabam tentando fazer essa distinção.
    • Fica bem evidente que todo mundo aqui acha que sabe mais do que o autor e os membros do time da época, mesmo sem contexto além de algumas linhas de pseudocódigo.
      DRY tem valor, mas abstração tem custo. É sempre um trade-off.
  • Um dos motivos de eu gostar de linguagens como Haskell é que o maximalismo da abstração fica no nível da linguagem, e não no nível da codificação.
    Todo mundo concorda sobre o que um Applicative deve fazer. O desafio se desloca mais para a direita no pipeline entre ideia e código, ou seja, para encontrar o Applicative adequado ao problema.
    A maioria dos padrões de design orientados a objetos tem correspondentes naturais e bem definidos nesse ambiente, o que ajuda a reduzir abstrações específicas de cada projeto.
    Para iniciantes, o custo inicial de aprender a linguagem é alto, e frequentemente você abre outro código Haskell e encontra umas 12 extensões de compilador que nunca viu antes e agora precisa entender.
    Ainda assim, vejo isso como saldo positivo, porque essas extensões de compilador você pode levar para todos os projetos seguintes.
    Para especialistas, a desvantagem é que parece que é preciso um doutorado para ter noção de quanta RAM o cats vai consumir.

    • Sabemos o que Applicative e Monad fazem porque existem leis matemáticas.
      Se um código usa Applicative ou Monad, essas leis permitem saber como ele se comporta.
      Padrões de design orientados a objetos não têm leis assim. São conceitos ou padrões definidos de forma solta, abertos a interpretação pessoal e customização, mas sem leis concretas que digam exatamente o que são e que comportamento ou saída se deve esperar.
    • Como usuário de Haskell, concordo que a capacidade de abstração da linguagem é muito boa.
      Ainda assim, pelo exemplo do post, só posso dizer que já me feri muitas vezes seguindo o caminho do código limpo.
      Se depois você precisa estudar a abstração para conseguir entendê-la de novo, então essa abstração não foi benéfica.
    • No post original, teria sido melhor se o time usasse mais princípios de orientação a objetos, a menos que o time como um todo rejeitasse isso. Se fosse um time assim, eu provavelmente iria trabalhar em outro lugar.
      O lugar em que orientação a objetos se encaixa de forma mais natural é na implementação de widgets de GUI e editores.
      A regra que unifica as outras regras de orientação a objetos é não pergunte, mande fazer.
      Neste exemplo, os handles seriam compartilhados de uma forma conhecida, e haveria pontos de controle específicos por forma. Se um box handle mudar, você pode chamar updateControlPoints(changedHandle); se um ponto de controle mudar, pode chamar updateBoxHandles(changedControlPoint).
      Outras separações elegantes também seriam possíveis, mas eu faria assim.
    • Podemos até concordar sobre o que um Applicative deve fazer.
      Mas raramente há consenso sobre qual biblioteca de parsing usar, se usar Data.Text.Strict ou Data.Text.Lazy, quanto estilo point-free usar, se usar . ou >>> ou outro combinador, ou quais extensões da linguagem usar.
      Gosto de Haskell, mas ele sofre de problemas parecidos com os de outras linguagens. As pessoas realmente não conseguem chegar a um acordo sobre a melhor forma de fazer as coisas.
    • Dá para ir um passo além e usar Rust com tipos afins.
      Aí você consegue o sistema de tipos do Haskell e desempenho previsível.
  • Bastava extrair para uma função separada apenas a parte matemática repetida e chamá-la a partir da função de redimensionamento.
    A função de redimensionamento em si parecia repetida porque compunha a interface, então o refactoring que eliminou isso foi um erro.
    Mas a matemática executada por essas funções era um alvo totalmente legítimo para organizar melhor o código.

    • Sim. Parece a direção óbvia, e normalmente eu também faria assim.
      No fim, isso gera um monte de funções pequenas e puras, mas isso não é problema porque fica fácil testar e refatorar.
    • Principalmente em refactoring, ao trabalhar com um time geralmente é melhor fazer isso em etapas.
      Há muitas formas e padrões para resolver o mesmo problema, mas desmontar interfaces existentes que outras pessoas usam não é um bom ponto de partida.
      A vantagem da abstração está em poder tornar o interior mais flexível sem prejudicar o restante.
      Um bom primeiro passo é, como foi dito, criar embaixo funções matemáticas mais flexíveis e deixar o resto como está.
      Depois, com o time, dá para conversar mais a fundo sobre como fornecer uma fachada com interface clara enquanto a implementação fica abstraída.
  • O que está sendo descrito aqui parece uma má abstração
    O problema real é que as pessoas projetam olhando só para dois ou três exemplos e não verificam se aquilo é genérico o suficiente para acomodar usos futuros

    • Em especial, uma boa abordagem é abstrair as partes em comum apenas com funções bem simples
      Deve-se abstrair de baixo para cima, não de cima para baixo
      Não é necessário criar um único ponto onde tudo acontece e que lide com combinações 2^n
      Ainda assim, dá para reduzir repetição, mudar em um só lugar e manter a capacidade de fazer alterações sob medida quando necessário
    • O problema do mundo real é que ninguém sabe, no momento em que escreve, que está usando uma má abstração. Se soubesse, não usaria
      Por isso, dizer apenas “evite más abstrações” ou “acomode usos futuros” não basta
      Em geral, ninguém consegue prever o futuro
      Abstrações devem ser usadas com extremo cuidado, mesmo quando parecem corretas. É melhor ser um cético radical
    • O problema da versão “clean” é que ela está totalmente acoplada
      Em código matemático, mexer em uma coisa afeta todo o resto
      Em certo ponto, fica mais fácil deixar algum código duplicado, indicar a semelhança com pistas na estrutura do código ou comentários e, com o tempo, destacar as diferenças centrais nos comentários
      A abordagem de clean code para esse tipo de código não lida bem com requisitos que mudam
      No fim, ela tende a virar um monte de funções helper com vários caminhos de código dependendo do local da chamada
      Às vezes dá para abstrair isso, e, como alguns já devem ter percebido, a frase anterior estava descrevendo polimorfismo orientado a objetos
      Mas como alguém poderia saber com antecedência quais seriam os requisitos exatos? Tentar fazer “clean code” tão cedo assim só termina em más abstrações ou overengineering
      A abordagem correta é não fazer nada, anotar os possíveis problemas e esperar até saber mais sobre o domínio do problema
    • Tenho gostado cada vez mais da ideia de descobrir padrões por meio das mudanças. Ou seja, encontrar padrões em novos recursos e correções de bugs
      Não me preocupo muito com código ligeiramente duplicado, mas fácil de entender, como no exemplo original
      Mas, se eu tiver que corrigir o mesmo problema em três lugares ou adicionar código de um novo recurso em três lugares, aí eu me preocupo
      Nesse momento, começo a pensar se dá para empurrar, de algum jeito, o código que precisa ser alterado ou adicionado para um ponto em comum
    • É isso mesmo
      As pessoas leem sobre design patterns ou DRY e, como ainda estão aprendendo, em geral aplicam errado e depois colocam a culpa no texto original
  • Rob Pike já disse que uma das razões de Go ser do jeito que é foi a ideia de que “uma pequena cópia é melhor que uma pequena dependência”
    Quanto mais eu programo, mais entendo o que isso quer dizer
    Repetição por si só não é o problema. O importante é identificar a unidade que você está lendo agora e talvez vá modificar
    DRY muitas vezes significa adicionar mais uma abstração, e, se essa abstração não for ortogonal o suficiente, agora você precisa considerar duas unidades em vez de uma, o que piora a situação