O custo do qual o YAGNI nunca falou
(newsletter.kentbeck.com)- YAGNI não é uma simples regra de economia do tipo “não escreva código que ainda não é necessário”, mas um princípio sobre o custo de antecipar a estrutura com base em suposições antes de a necessidade estar confirmada
- O centro do problema não é o design em si, mas quando fazer o design; estruturar cedo demais pode ser tão arriscado quanto estruturar tarde demais
- Estruturas antecipadas geram ao mesmo tempo o custo da opcionalidade, ao fechar possibilidades antes da chegada de novas informações, e o custo de NPV, ao antecipar gastos e adiar retornos
- Mesmo que o custo de gerar código se aproxime de zero, o YAGNI não desaparece; na verdade, a geração barata pode tornar ainda mais fácil criar frameworks baseados em suposições
- A conclusão de construir apenas quando necessário não vem do custo de escrever código, mas do fato de que o valor da opcionalidade não usada e do dinheiro não gasto ainda permanece
YAGNI não proíbe design
- YAGNI é a sigla de “You Aren’t Gonna Need It” e não é uma desculpa para nunca projetar algo que não seja estritamente necessário
- Se algo for necessário, pode ser construído; o ponto central, porém, é o timing
- O ponto de partida é uma história recorrente em projetos: diante da ideia de que “daqui a 3 semanas uma implementação simples não vai bastar, então quero fazer algo mais complexo agora”, a resposta repetida era “You aren’t going to need it”
- Esse princípio considera arriscado tanto criar estrutura cedo demais quanto tarde demais
O problema não é o custo de escrever código, mas a estrutura baseada em suposições
- Uma interpretação comum vê o YAGNI como uma regra de economia: “não escreva código que ainda não é necessário porque ele é caro”
- Mas o alvo do YAGNI não é o custo de produção do código, e sim a estrutura especulativa (speculative structure) criada antes de uma funcionalidade realmente exigir isso
- Esse tipo de estrutura gera dois tipos de custo, em momentos e por razões diferentes
Primeiro custo: opcionalidade
- Quando você cria uma estrutura antes de a funcionalidade chegar, acaba se comprometendo com base em suposições sobre requisitos que ainda não conhece
- As funcionalidades para as quais você tentou se preparar de antemão geralmente diferem das que de fato chegam, e o resultado é pagar duas vezes
- o custo de contornar uma estrutura com formato errado
- o custo de desmontar essa estrutura depois
- O problema não se resume a dizer apenas que “é difícil prever”
- Mesmo que a suposição esteja correta, ainda há perda, porque você abre mão da opção de não se comprometer antes e construir a estrutura certa mais tarde
- Esperar não é preguiça; é manter um ativo chamado opcionalidade
Segundo custo: NPV
- Assim como o dinheiro tem valor no tempo, funcionalidades também têm valor temporal
- Se você cria agora a estrutura para uma funcionalidade que só será necessária daqui a 3 meses, antecipa o custo e atrasa o lançamento da funcionalidade que de fato gera receita
- Esse custo existe mesmo quando a suposição está correta
- Nem uma previsão perfeita muda a ordem entre custo e retorno; a perda surge do intervalo em que os gastos são colocados antes dos ganhos
- O custo da opcionalidade trata do problema “não se comprometa antes que a informação chegue”, e o custo de NPV trata do problema “não pague antes de precisar”
- Mesmo a objeção de que “depois vai ficar caro demais corrigir” pode ser, por si só, outra previsão
Mesmo com geração de código barata, o YAGNI continua válido
- Em nenhum dos dois custos entra o custo de digitar código
- Se o custo de escrever código cair para quase zero, a interpretação econômica do YAGNI — “como o código ficou barato, então tudo bem construir antes” — desmorona
- Mas como o YAGNI não é uma regra de economia, a geração barata de código não o invalida
- O custo da opcionalidade não surge da quantidade de esforço, mas do compromisso que fecha escolhas futuras
- O custo de NPV não surge do preço de produção, mas do timing do fluxo de caixa
- Geração gratuita não enfraquece o YAGNI; ao contrário, pode facilitar ainda mais a criação de frameworks baseados em suposições
- A estrutura gerada continua produzindo os dois custos e, por não ter sido escrita diretamente, pode até ser menos compreendida
- A conclusão não é “espere porque código é caro”, mas sim: como a opcionalidade e o dinheiro valem mais quando ainda não foram usados, construa quando for necessário
1 comentários
Comentários no Hacker News
Acho que o custo de mudar a estrutura também caiu
Graças à IA, diminuiu o custo de reforçar o comportamento com testes antes de mudar a estrutura, e o custo de implementar migrações sem downtime também caiu
Um dos grandes motivos de Rust ter ganhado atenção, mesmo antes da IA, era justamente o baixo custo de mudar a estrutura interna da aplicação, e agora isso é ainda mais verdadeiro
O custo de oportunidade de não conseguir mudar a estrutura com segurança aumentou bastante, e hoje a principal otimização é a capacidade de alterar partes grandes do código e do produto de forma rápida e segura
Só que, antes da IA, mudanças estruturais levavam muito mais tempo; então dá até para argumentar que o valor do que se está tentando otimizar agora, na verdade, caiu
Ainda é valioso, mas talvez um pouco menos do que antes
Com o aumento dos testes gerados por IA e frágeis, o custo de mudar a estrutura ficou maior do que antes
Organizar um conjunto de testes para verificar a essência do problema, e não decisões acidentais de design, ainda é algo em que a IA não é boa
Mas também ficou fácil demais acabar com um conjunto de testes frágil mais ou menos 75% pronto
Muita gente se satisfaz em tratar como melhoria objetiva a passagem de “alguns testes comuns e frágeis escritos por humanos” para “muitos testes comuns e frágeis escritos por IA”
Concordo totalmente com usar a ferramenta dessa forma, mas isso não significa que dê para deixar de se preocupar com construir um castelo de cartas errado cedo demais
Ainda é bem difícil projetar um contrato de testes perfeito que resista a refatorações
É como se algo antigo tivesse voltado a ser novo
Da eficiência contextual de abordagens como DDD ou Clean Architecture até itens como esse, a IA não cria novos trade-offs; ela funciona como um amplificador
Aumenta a produtividade das equipes que fazem direito e também aumenta a dívida das equipes com padrões fracos de qualidade de design e arquitetura
O único ganho é não precisar pensar profundamente
Pensar a fundo não custa tanto tempo nem esforço assim, então vão acabar ficando para trás de quem usa a IA do mesmo jeito, mas pensa o suficiente para não produzir esse tipo de movimento em falso
Kent Beck compara código ainda não escrito a uma opção financeira de comprar código por um certo preço
Mas isso é só uma metáfora, e começa a ficar estranho quando levada longe demais
Se nenhum código foi escrito, então as opções são infinitas? Mesmo sem tempo investido ainda, isso não parece correto
Isso também pode virar justificativa para ficar na fase de planejamento e adiar indefinidamente a escrita de código para não fixar nada
Ainda assim, se a metáfora funcionar, o custo pode estar na leitura do código
Código não escrito não precisa ser lido e, se você usa agentes de codificação, também não polui o contexto com detalhes irrelevantes
Código ainda não escrito também não precisa ser testado, e testes ainda não escritos também não consomem tempo de execução
Por isso, é bom manter o projeto o menor possível, e adiar funcionalidades pode retardar ao máximo o crescimento da base de código
Isso também significa que executar código de outras pessoas pode evitar muitos custos
Se for possível usar uma API padrão, não é preciso entender a implementação em detalhes nem executar seus testes, embora adicionar dependências tenha seus riscos
Código não escrito não tem valor
Para que o código escrito hoje gere valor, ele precisa resolver uma solicitação ou problema de hoje, ou estar inclinado a tornar algo mais fácil amanhã
Assumir dívida técnica com uma solução gambiarra ou desperdiçar tempo com algo que contraria o YAGNI não gera valor
O importante não é o código não escrito, mas o código que será escrito daqui para frente e seu propósito
É preciso fazer o trade-off certo entre resolver o ticket ou tarefa de hoje e não dar um tiro no próprio pé no futuro
Escrever código é um compromisso, e o valor de hoje é visível, mas o valor de amanhã é mais uma estimativa
Ainda assim, sempre haverá um custo a pagar depois, então acabamos estimando o que pode vir a ser necessário para tentar minimizar esse custo
Acho que não se fala o bastante sobre as boas práticas na era da IA, mas há um ponto que Kent deixa passar completamente
Há bastante valor em descobrir mais rápido quais funcionalidades são necessárias
Criar uma estrutura especulativa pode funcionar como um mecanismo de pressão para fixar requisitos e, pelo menos, começar a revelar como as falhas acontecem
Como isso pode sair mais caro do que esperar, não se deve fazer isso para a maioria dos requisitos, mas às vezes pode ser a melhor opção
O custo de construir a coisa errada agora é muito menor, e por isso o cálculo em torno do YAGNI muda
Ainda assim, continua sendo necessário fazer esse cálculo, e agora cada equipe precisa descobrir por si mesma como isso mudou para ela
Se não descartar, isso vira um mecanismo de pressão para produzir um resultado bagunçado
YAGNI é o problema de construir algo que não está nos requisitos atuais, antecipando uma futura mudança de requisitos
Isso é diferente de concretizar os requisitos e restrições atuais
Essas coisas vêm principalmente de conversas com stakeholders, usuários e clientes, além de recursos e restrições e capacidades de engenharia
Protótipos têm valor quando servem para conversar com stakeholders, criar um modelo de gestão do projeto ou fazer pesquisa de engenharia
Fora disso, está tudo invertido
A abordagem de ver o software em execução como um ativo está correta
Só que o custo de executá-lo e recriá-lo caiu bastante
O custo que não caiu é o de quebrar a cadeia de confiança sobre resultados previsíveis
Uma versão específica de um software em execução acumulou confiança ao longo do tempo, e reescrevê-lo do zero zera esse capital no momento do lançamento
Em algum momento, passei a pensar diferente
Adio os concretos com YAGNI e escrevo, na medida do possível, uma versão abstrata
Criar um
UserStore? Isso parece o mais simples, mas talvez não seja necessário um formato específico deUserEntão crio um
Storeque guarda qualquer coisa que possa ser armazenadaSe você não estiver acostumado, isso parece overengineering e uma salada de genéricos, mas, paradoxalmente, é a forma que menos promete a qualquer implementação concreta
Não só você criou uma interface
UserStoreque provavelmente não seria necessária, como também acabou criando uma abstração generalizada deStoreque com certeza não era necessáriaNa tentativa de não se comprometer com uma implementação concreta, você implementou camadas pegajosas desnecessárias e com grande chance de nunca virem a ser necessárias
Se for uma abstração que não se baseia em necessidade real, mesmo que ela venha a ser necessária depois, é bem provável que tenha sido feita de forma errada
No fim, o UserStore provavelmente será necessário, então era isso que deveria ter sido feito primeiro
Não concordo com a frase “não é uma afirmação de que prever é difícil, como se um arquiteto mais afiado pudesse evitar isso”
Essa afirmação só se sustenta se partir da premissa de que prever é difícil
Se você prepara com antecedência a base para uma funcionalidade muito provável e tudo se encaixa, consegue lançar mais rápido
Também não há garantia de que a equipe necessariamente vá crescer ou sequer se manter, então me parece pior comemorar a contenção do que correr em cima da hora para fazer YAGNI caber no prazo
Recentemente precisei me desvencilhar funcionalmente de um codebase cheio de YAGNI acumulado, e foi um trabalho enorme, mesmo com agente
Em um sistema distribuído, como saber o que realmente está em uso? Teve coisas que eu deixei passar e coisas que o agente deixou passar, e o processo inteiro demorou mais do que precisava
Não foi só um porteamento 1:1; aproveitamos como oportunidade para simplificar, então era preciso entender completamente como o sistema antigo funcionava
Até coisas que na prática não eram usadas entravam no escopo do que precisava ser entendido, se não fosse possível identificá-las como tal
No fim, acho que tudo se resume a explorar o problema e implementar a solução
Sempre há custo em resolver o problema errado, e também há custo em implementar uma solução ruim para algo que nem era necessário
Às vezes o desenvolvimento de software pode descambar para um simples tentativa e erro, em vez de pensar em quais estratégias e conjuntos de problemas explorar
Há casos em que explorar um problema mais a fundo em certa direção do que o estritamente necessário ajuda no longo prazo, mas implementar soluções sem propósito nunca é algo bom
Acho que o que Kent Beck realmente critica é a postura de implementar algo “vai que” só porque talvez seja necessário no futuro
Foi dito que “se você cria a estrutura antes de a funcionalidade chegar, está se comprometendo com um palpite”, mas eu diria que há palpite dos dois lados
A funcionalidade pode ter alta probabilidade de chegar, mas não é certo
Se você não criar a estrutura agora, haverá custo de refatoração; se criar cedo demais e a funcionalidade nunca vier, terá desperdiçado esforço
Quais são os custos, probabilidades e trade-offs entre essas possibilidades? Claro que depende da situação
O próprio YAGNI é uma grande generalização deliberada
No fim, depende do contexto
Em qualquer um dos lados, muitas vezes há muito palpite e explicação gestual, e isso se parece com o problema de estimativas de trabalho confiáveis
Alguns desenvolvedores, que não lidam bem com um mundo incerto, tentam encontrar regras em preto e branco para tudo
https://www.sebastiansylvan.com/post/the-perils-of-future-co...
Em resumo, ele fica do lado do YAGNI
Nunca vi nada nos textos de Kent Beck que parecesse útil para uma empresa de chips
Numa empresa de chips, muitas pessoas precisam trabalhar por muito tempo, o cliente não pode ver nada até estar pronto, e, para ganhar dinheiro, é preciso vender em escala de milhões de unidades
Hardware tem restrições fortes
Aliás, foi justamente para escapar desse tipo de restrição que o software foi inventado
É verdade que muitas empresas de chips não compartilham o trabalho em andamento, mas é possível — e isso de fato acontece — compartilhar simulações, protótipos e amostras de engenharia
Claro que normalmente isso vale para clientes grandes
Percepções de um setor em que o custo de mudança é relativamente baixo não se aplicam facilmente a um setor em que o custo de mudança é alto, e o inverso também costuma ser verdadeiro
Como as empresas de chips planejam projetos assim? Usam ágil, cascata ou algum framework diferente do setor de software?