Para não falarem que sou extremista ou radical defensor das práticas ágeis (acho que estou muito longe disto, na verdade) vou dizer uma coisa: eu particularmente considero a aplicação de TDD + YAGNI uma prática tanto arriscada, que pode resultar em um design inadequado e deveria ser praticada apenas por times com experiência suficiente para tal.
O Test Driven Development inverte o fluxo “natural” do desenvolvimento de software e faz com que o desenvolvedor primeiro crie testes automatizados que atendam os requisitos para depois criar o código necessário para que os testes parem de falhar. Uma das vantagens desta técnica seria a aplicação do YAGNI de forma natural.
O conceito de YAGNI (You Aint Gonna Need It) afirma que você deve implementar somente o que é realmente necessário agora para atender os requisitos. Isto faz com que o esforço seja concentrado nos requisitos mais importantes e que o código seja sempre o mínimo possível.
Os conceitos são bonitos e atraentes, mas existem alguns riscos. Veja um exemplo, estou construindo uma API de operações matemáticas, então faço um teste unitário:
Qual o menor código possível que atende meu requisito?
Tudo bem, você achou ridículo o exemplo mas não me xingue ainda... Porque o exemplo é ridículo? Porque é óbvio que o resultado desejado era a soma dos dois parâmetros e ninguém faria algo tão estúpido. O problema é que não há nada no teste que afirme este fato tão óbvio e, na verdade estamos inferindo o requisito porque conhecemos o assunto. Agora pense, não existem milhares de situações que pode ser similares a esta, mas que os requisitos não sejam tão óbvios?
O ponto aqui é que se os testes não cobrirem 100% das alternativas possíveis o seu código YAGNI pode ficar incompleto. O problema é que muitas destas alternativas podem aparecer para você durante a construção do código e não antes. Por isto é necessário um processo de identificação e solução de pequenos problemas ao longo do desenvolvimento, micro-iterações importantes para atingir um código completo, seguro e simples.
Steve McConnell em Code Complete, fala que o design de software é um processo complexo, por isto são necessários diferentes níveis de abstração para facilitar o entendimento do problema. Conseguir transitar entre estas abstrações é uma habilidade importante para elaborar um bom design de software.
É como um entender um caminho em um mapa, como carioca morando em Sampa eu me tornei um expert em mapas. As vezes é necessário olhar o mapa sob uma perspectiva macro, ver todo o caminho, que bairros e principais avenidas você vai passar. Mas também é necessário olhá-lo sob a perspectiva micro, quais as ruas próximas, se o local fica no início ou no fim do quarteirão, à direita ou à esquerda.
Na construção de software estas perspectivas também são importantes, as vezes devemos analisar os detalhes da implementação de um método, as vezes temos subir alguns níveis de abstração e entender em qual camada ficará esta classe ou com que outras classes ela poderá interagir.
Todas as vezes que tentei aplicar TDD me senti empurrado para tomar decisões de design de forma inconsciente, prematura e sob a perspectiva micro. Sinceramente não me senti bem, não foi uma experiência confortável, nem agradável.
Não estou afirmando que a técnica é ruim ou que é essencialmente falha, mas acho que é preciso tomar cuidado com algumas armadilhas que podem surgir e saber como lidar com elas. Com certeza ainda pretendo fazer novas tentativas com TDD quando tiver outras oportunidades, talvez mude de opinião.