Uma das frases que eu mais odeio, quando converso com alguém muito estudioso mas com pouca prática, é a frase “mas você sabe que, por definição…”. Isso sempre me lembra o velho ditado “na prática, a teoria é outra” e o post de hoje é sobre isso: como eu odeio teoria não aplicada.
Isso é especialmente verdade em programação. Graças à frase “mas por definição, a equipe que testa tem que ser diferente de quem desenvolve” atrasa ou freia completamente Test-Driven Development, assim como várias outras “verdades” são seguidas, metodicamente, desde os primórdios da programação e as pessoas nem sequer sabem se é ainda válido.
Pegando, por exemplo, Design Patterns. Eu não sou contra a idéia geral dos design patterns, apenas questiono (e questiono MUITO mesmo) quando usá-los. É comum ver códigos que são relativamente simples que usam três ou quatro patterns diferentes, sem nenhum motivo aparente.
Já falei sobre isso anteriormente em outros posts ou na minha palestra do FISL sobre código limpo, mas acho importante reforçar: seguir, à risca, todos os padrões (code smells, como chama o Robert Martin) do Clean Code nem sempre traz um código limpo no final. Aliás, minha experiência mostra que abusar de patterns e de regras no código pode levar a um novo tipo de “código spaguetti”, com a clara desvantagem que esse novo modelo é orientado a objetos! Logo, mais importante do que saber quais os patterns, quais as regras, é saber POR QUE se está aplicando aquela regra. A velha história do “know-why” versus o “know-how”.
Claro, não há uma regra simples (saber por que fazer algo não é algo que possa ser ditado por regras, obviamente) mas na minha opinião é algo que falta muito nos designs que eu já vi, e principalmente quando o software está sendo escrito numa linguagem dinâmica tal como Ruby. Isso porque, em uma linguagem dinâmica, normalmente não há um processo de compilação. Muitos dos design patterns são bolados de uma forma que facilite ao compilador lhe mostrar erros (tais como método não implementado em sub-classes) ou foram bolados pensados em linguagens tipo Java ou C++, aonde não há certas facilidades como as de Ruby. Sem um “compile time” tipo o de Java, ou o suporte para classes abstratas, alguns patterns só complicam o design e poderiam ser facilmente re-implementados sem eles, sem descartar o baixo acoplamento. Como citei no começo desse parágrafo, não há uma “regra” para saber o que deve ser feito com patterns e o que não deve, mas o objetivo principal é evitar coisas como:
class AbstractParser def find_element(element) #O que é um "element"? raise "must be implemented", NoMethodError #Qual a vantagem dessa abordagem, ou de nem criar o método? end end class XMLParser def initialize(xml) #O que é o "xml"? Uma String? ou XMLNode? ... end def parse(xml) #Qual a lógica de se criar um "parse", nesse caso? new(xml) end def find_element(element) #código para buscar o elemento. Vale a mesma pergunta que do AbstractParser end end
Ao invés disso, o melhor é abusar mais da idéia de montar um “protocolo”, tal como: “Todas as classes que vão fazer ‘parsing’ de qualquer elemento precisam implementar o método (na classe) ‘parse’ e o método (na instância) ‘find_element'”. Aliás, na minha experiência, classes que são “substantivadas” (To Parse – Parser, To Decorate – Decorator, To Create – Creator, enfim) são na maior parte desnecessárias em Ruby. E claro, para evitar problemas futuros no código, é essencial cobrir os casos com testes automatizados.
Aliás, esse é outro ponto a se pensar: em Ruby, como não há um processo de compilação, e pelo próprio design da linguagem não há uma maneira de fazer um processo de compilação com tantas checagens como C++ ou Java, será que testes automatizados são mesmo opcionais? Eu acredito que não, nem que seja para um teste automatizado simplesmente oferecer uma cobertura C0, ou seja, ele passa por todos os cantos do código, todas as possibilidades, para pelo menos checar a sintaxe, mesmo sem nenhuma checagem de comportamento. Pela minha experiência profissional com Ruby, o tempo “perdido” em escrever testes com RSpec (acredito que com test/unit também, mas como só fui a fundo no RSpec não posso dar certeza) se paga em dias. Mas estou saindo do assunto.
O assunto é, a programação é cheia de “técnicas”: pontos de função, complexidade ciclomática, o famoso cálculo de complexidade “big-O”, UML, Casos de Uso, MVC, Design Patterns, Code Smells, Beans, DSL, TDD e BDD, só para citar alguns. O programador deve, o tempo todo, saber quando aplicar cada um deles, e essa é a parte difícil. Também, muitos programadores quando enfrentam prazos muito curtos, tem o instinto de sentar no computador e programar o mais rápido que podem, e é aí que fica o erro: um design ruim ou na pressa muitas vezes tem que ser jogado fora, antes mesmo do fim do prazo, porque a adição de novas funcionalidades ou a simples correção de um bug é praticamente impossível. Embora seja difícil, quando enfrenta-se um prazo apertado a solução é pensar bastante em como fazer um design no sistema de forma que ele, embora não seja o ideal, também seja minimamente extensível.
Claro que isso não é uma crítica pessoal a ninguém. A visão que temos, o paradigma que nos apresentam tanto em faculdades/universidades como em colegiais técnicos, é de que a programação é algo absolutamente técnico e sem necessidade de pensar muito, que o programa é entregue “mastigado” aos programadores, e que “só falta programar”. Também, vemos no mercado de trabalho excessivas horas-extra, um sindicato muito fraco e propostas de emprego com requisitos “horários flexíveis” e “saber trabalhar sob pressão”. Quem, sob pressão intensa e constante, consegue se concentrar num bom design? Sob pressão constante, como você aprende e se desenvolve, profissionalmente? Com os dias, as noites e finais de semana ocupados, como você relaxa sua mente pra poder abrir espaço para novas idéias, novas linguagens, como você estuda? Com um trabalho que só gasta tempo com designs ruins e sem tempo de melhorá-los, o resultado do trabalho é repetitivo e sem apresentar nenhuma novidade: como você aprende coisas novas, com isso? Tudo isso porque, “na teoria, é assim que se faz software desde XXXX”, ou porque “na teoria, tudo precisa ser controladinho porque não se pode estimar o que você não consegue controlar”, etc etc. E aí tentamos usar outras teorias, como “teoria do Caos”, “sistemas complexos e adaptativos”, para tentar contra-atacar essas “teorias da computação”. Mas, “na teoria”, são todas teorias. Na prática, tem coisas que por melhor que seja a teoria, simplesmente não funcionam. Como invalidar essas “teorias”, e tentar de fato estudar a melhor forma de fazer algo? É possível criar um setor de desenvolvimento que as pessoas se sintam bem em trabalhar, ou mudar a cultura organizacional de várias empresas para mudar essa cabeça?
Eu, particularmente, gostaria mesmo de saber da resposta…
1 Comment
Thiago Vieira · 2011-05-17 at 08:47
Quando eu descobrir eu te aviso, Maurício.
Abraço!
Comments are closed.