6 lições que aprendi sobre desenvolvimento de software
Este blog deixou de ser mantido, mas o autor continua escrevendo aqui. Não deixe de assinar o novo feed!
É natural do ser humano aprender com os seus próprios erros. Quem aqui nunca vivenciou uma situação inusitada na qual mesmo tendo a impressão de que algo está errado, acabamos ignorando e seguindo em frente. E assim continua até que, num belo dia, a bomba explode e você pensa consigo mesmo: “eu sabia!”.
Essas situações, embora aparentam ser ruins em um primeiro momento, são excelentes oportunidades para nos ensinar algumas coisas que provavelmente não aprenderíamos de outra forma. Aliás, talvez seja até mais fácil aprender com nossos próprios erros do que ler sobre isso em algum livro ou artigo. O problema é que nem sempre é fácil saber tirar algo de bom em situações problemáticas.
Por isso, tentarei fazer um gancho entre esse assunto e a área de desenvolvimento de software, mostrando algumas das lições em que aprendi errando. Também seria interessante se os leitores que se identificarem com o assunto também compartilhassem algumas lições que aprenderam em períodos turbulentos. Afinal, errar faz bem!
UPDATE: Uma lição importante que havia esquecido de relacionar agora está disponível (lição número 4).
Lição número 1: confie no código e não no documento
Deixe-me contar uma experiência boa que tive na faculdade. Certa vez, um de nossos professores chegou na aula, colocou no projetor um Diagrama de Classes e um Modelo Entidade-Relacionamento e disse para a sala:
Esse é o modelo de um sistema de locadora. Nesses diagramas vocês tem tudo o que é preciso pra fazer isso funcionar. Não quero nem saber se vocês não gostam do cara que está sentado do seu lado, quero que vocês aí se organizem e me entreguem isso funcionando até o final da aula.
Enfim, todo mundo se levantou e começou a andar de um lado para o outro e, alguns momentos depois, tínhamos nos organizado para dar início a programação. Pois bem… depois de algum tempo programando o que estava nos diagramas, o professor se volta para a sala e diz, em tom de sarcasmo:
Vem cá, vocês não encontraram nada estranho, não?
Praticamente todo mundo da sala havia percebido que alguns relacionamentos estavam faltando, outros sobrando, outros com a multiplicidade incorreta, entre outras coisas. Ninguém só não havia dito nada antes por pensar que o diagrama era indefectível. Se isso aconteceu em um “projeto de mentira” com duas dúzias de pessoas, quem imagina o que pode acontecer em projetos com dezenas e dezenas de pessoas envolvidas?
O fato é que os erros foram colocados lá propositalmente e isso acabou me fazendo perceber que nem sempre um documento representa fielmente um sistema. O que sempre representa fielmente um sistema é o seu código-fonte!
Durante um tempo, trabalhei numa empresa onde praticamente tudo o que precisava ser desenvolvido era entregue aos programadores na forma de textos (para fins de documentação, diziam). Para você perceber a gravidade da situação, se eu implementasse exatamente o que estava escrito nas folhas que recebia, a chance do sistema sequer compilar era altíssima. Além do mais, as especificações normalmente vinham incompletas, o que fazia com que nós tivéssemos de nos preocupar não em simplesmente programar, mas também de tentar driblar possíveis falhas na especificação.
Eu confesso que, naquele tempo, a minha vontade era dar um tiro na minha própria cabeça. Mas hoje, olhando para trás, percebo que isso fez com que eu adquirisse algumas habilidades que não seriam desenvolvidas se as especificações chegassem perfeitas em minhas mãos.
Lição número 2: escreva testes automatizados
Novamente remetendo aos tempos da faculdade, eu não poderia ter escolhido um tema melhor para o meu TCC do que o desenvolvimento de um framework. Me lembro que fui alertado dezenas de vezes pelos professores de que tratava-se de algo inusitado e que eu não teria direito de reclamar caso algo saísse errado. Bem ameaçador, não acha?
Tirando o fato de que eu estava sozinho nesse desafio - e de que precisaria ser um Samurai para entregar tudo no prazo - tudo corria bem no começo. O código estava bonito e funcionava bem. O que mais eu poderia querer? No entanto, sem que eu percebesse, um sério problema foi se instalando: a ausência de testes automatizados.
No começo - quando o framework possuía apenas algumas dezenas de classes - era tranqüilo… se eu fizesse alguma bobagem era fácil de encontrar o problema. Mas, conforme o código foi crescendo, eu vi que, lentamente, eu perdia o controle da situação. Enquanto que no começo eu estava livre para fazer o que quisesse (qualquer erro era fácil de corrigir), daquele ponto em diante eu já precisava pensar mil vezes antes fazer alterações, pois o risco em se quebrar algo era muito grande.
Nesse tempo eu já sabia como escrever testes e tudo mais. A minha grande cagada foi pensar que, se poupasse o tempo utilizado na escrita dos testes, eu teria mais tempo para entregar aquilo que prometi no começo do ano. Obviamente eu estava errado.
Escrever testes automatizados (e até mesmo levá-los a outro nível, como acontece com o TDD) é essencial para que as aplicações evoluam de forma consistente. Sem a segurança que tais testes proporcionam, é impossível garantir que o que foi feito hoje continue funcionando amanhã. Não testar o código é comparável a um jogo de tabuleiros, onde, num momento de azar, você acaba caindo numa casa que diz “volte à primeira casa”.
A grande lição que eu tirei disso foi aprender que os testes não servem unicamente para comprovar que tudo está funcionando (pois isso eu já sabia). Eu aprendi que deixar de escrever testes alegando falta de tempo é burrice. Falando nisso, ainda encontramos pessoas que dizem que testes são ruins porque “é mais código para dar manutenção”, ou “escrever testes consome tempo” ou ainda indagações ridículas como “e quem vai testar os testes?”. Não irei responder a essas perguntas, mas posso dizer que são poucas as coisas que fazem tão bem a um software quanto a criação de testes automatizados.
Lição número 3: desenvolva software para o cliente, não para a infraestrutura
Algumas pessoas, ao se iniciar um novo projeto, já tenta desde o início juntar e configurar o framework de injeção de dependências, o framework web, o framework ORM, e por aí vai. A minha pergunta é: pra quê? Confesso que eu mesmo já fiz isso, mas devo dizer que trabalhar dessa forma não faz o menor sentido (a não ser em casos ultra-específicos).
Voltando aos tempos de faculdade, uma outra coisa que eu gostava era que nós montávamos os sistemas sem pensar em bancos de dados, latência da rede, essas coisas. Nosso banco de dados era nada mais que algumas instâncias de LinkedHashSet! Então, considerando que algumas boas práticas foram usadas e que o sistema esteja funcionando em memória, trocar o mecanismo de persistência (ou lidar com qualquer outra questão de infraestrutura) era fácil!
Por exemplo, o caso de uso Alugar Filme não fará nada de diferente caso estejamos persistindo os objetos em um banco de dados em vez da memória. Da mesma forma, o caso de uso Reservar Filme (que envia uma mensagem para o usuário informando quando o filme está disponível) não mudará se trocarmos a implementação atual - que executa…
…para uma outra que utiliza a API JavaMail para enviar uma mensagem via SMTP. O que importa é que os objetos estão sendo armazenados em algum lugar e que o usuário é notificado de algum jeito quando o filme que ele reservou está disponível. Acredite, isso é o bastante durante o desenvolvimento. A grande vantagem em se trabalhar dessa forma é que fica fácil de isolar o código que resolve o problema do cliente do código que resolve o problema da infraestrutura. Conseqüentemente, testar o código de negócio é rápido e fácil, pois não dependemos de recursos externos como conexões de rede, bancos de dados etc.
Infelizmente chegará uma hora onde o código para interação com bancos de dados deverá ser escrito, assim como chegará a hora de implementar o envio de mensagens via SMTP. O erro é tentar fazer isso tudo desde o início! Por exemplo, pra quê perder quarenta minutos toda semana consertando o mapeamento das classes com o banco de dados (em razão de modificação nos requisitos e refatorações no código) se você pode deixar para fazer isso depois, numa cacetada só? Isso também evita que os desenvolvedores se distraiam com tarefas que não sejam importantes no momento… por exemplo, tentando achar uma configuração ideal do pool de conexões ou otimizando SQLs.
Atente ao fato de que eu não estou dizendo que você não deva se importar com bancos de dados, servidores de aplicação etc. O que quero dizer é que você está sendo pago para programar um software que atenda as necessidades do seu cliente, rode ele em um GRID de R$30.000,00 ou num Pentium2 500MHz. Devemos sim criar softwares que aproveitem bem os recursos disponíveis, mas o importante é não deixar que tais restrições de hardware e de software interfiram no desenvolvimento da solução que o software deve proporcionar ao cliente. Afinal, de que adianta um software bonito e rápido se ele não soluciona os problemas que justificaram sua criação?
Lição número 4: seja preguiçoso
Uma coisa interessante que aprendi foi que nem todo programador preguiçoso é um bom programador, mas todo bom programador é preguiçoso! Cuidado com a diferença entre as duas coisas, pois elas existem e são bastante visíveis na prática.
Os bons desenvolvedores possuem um faro aguçado que os permite identificar situações nas quais soluções melhores podem ser adotadas de modo a diminuir esforços, tornando o código (e a aplicação de um modo geral) mais conciso e organizado. Alguns chamam tais situações de cheiros (ou smells), termo este que “pegou” após a publicação do livro Refactoring: Improving the Design of Existing Code, de Martin Fowler.
Para identificar tais situações, vamos começar do básico: o que um desenvolvedor de software mais costuma odiar em seu trabalho? Trabalho repetitivo. Se você precisa fazer uma determinada tarefa e tal tarefa exige a repetição de procedimentos que você suspeita serem supérfluos, então faça um favor para si mesmo: deixe sua preguiça trabalhar por você! Use sua vagabundice interior como um termômetro.
Não sei se você é assim, mas eu costumo aprender as coisas mais rapidamente através de exemplos… então, vamos a eles.
Imagine que você tenha sido selecionado para trabalhar no deployment de uma aplicação. Se você já tentou implantar uma aplicação “no braço”, certamente sabe que, se você for fazer o trabalho manualmente, terá de fazer sempre as mesmas coisas indefinidamente. O deployment de uma aplicação simples poderia ser exemplificada da seguinte forma:
- Organizar as classes compiladas em uma estrutura de diretórios específica;
- Copiar bibliotecas que devem estar disponíveis a nível de servidor (como drivers JDBC) no diretório de bibliotecas do Container;
- Disponibilizar as bibliotecas usadas pela aplicação em um diretório específico;
- Mover os descritores e arquivos de configuração aos locais apropriados;
- Gerar um WAR ou EAR;
- Implantar o WAR ou EAR no Container;
- (Re)iniciar o Container (caso este não suporte hot deployment).
Veja que fazer o deployment de uma aplicação não é assim tão complicado, mas perceba que você terá de fazer essas mesmas tarefas sempre que você precisar rodar a aplicação. Trata-se de algo completamente mecânico e chato.
Neste exemplo, você teria duas opções: continuar fazendo isso manualmente até que a Morte o leve ou criar um script para automatizar essa tarefa para você. Criando um script que faz o deployment automaticamente poupa seu trabalho e seu tempo… e como já dizia o Super Sam:
Time is money, oh yeah!
Quer um exemplo mais voltado à programação? Então tudo bem!
Imagine que você tenha uma aplicação desktop feita em Swing. Imagine também que tal aplicação atualmente contenha 25 formulários com um botão “Fechar”. A conseqüência disso é que, ao se criar um novo formulário, o trecho de código abaixo precisa ser copiado e colado em todos os formulários:
-
-
closeButton.addActionListener(
-
dispose();
-
}
-
}
-
);
-
Pra quê ter de fazer a mesma coisa várias vezes, se você pode criar JFrame com tal funcionalidade embutida? Então, basta que todos os formulários que tenham um botão “Fechar” estendam tal JFrame:
-
-
private JButton closeButton;
-
-
public CloseableJFrame() {
-
-
// cria e configura o botão
-
-
// adiciona o listener
-
closeButton.addActionListener(
-
dispose();
-
}
-
}
-
);
-
-
// adiciona o botão no local apropriado
-
}
-
}
-
-
public MyFrame extends CloseableJFrame {
-
// Este formulário terá o botão "Fechar"
-
}
-
Pegou a idéia? Além de diminuir o trabalho braçal ao se criar um novo formulário, isso com certeza facilitará uma possível mudança, pois melhoramos a qualidade do código-fonte eliminando código duplicado.
Cuidado com os extremos!
Uma coisa importante é não levar essa lição ao extremo (como qualquer outra coisa). Querer achar uma solução mais simples para todos os problemas nem sempre é a melhor saída. Já dizia Albert Einstein:
Faça as coisas o mais simples que você puder, porém não se restrinja às mais simples.
Lição número 5: não tente adivinhar o futuro
Um dos meus professores de faculdade nos contou sobre uma experiência que ele havia feito com outra classe. Ele propôs que os alunos, em grupos, implementassem um sistema qualquer, cuja única restrição era, ao se partir para a fase de implementação, o código deveria refletir exatamente o modelo criado no início do processo, e este, por sua vez, não poderia mais ser alterado! Bem no estilo Waterfall mesmo. Loucura, não? Embora a tarefa não parecesse difícil para os alunos em um primeiro momento, nem é preciso dizer o quanto estavam enganados em relação a isso.
Adivinhem o resultado da experiência? Sistemas que pareciam “faltar uma perna”. Quer dizer, todos os sistemas funcionavam, mas não resolviam os problemas que deveriam. O interessante é que todos os projetos acabaram falhando, apesar de a maioria desses alunos terem escolhido desenvolver algo que conheciam bem (por terem experiências profissionais e tudo mais).
Acredito que, por causa desse medo de falhar que (quase) todos os desenvolvedores de software acabam tendo suas mentes permeadas de pensamentos condicionais - os famosos “e se“. Eu mesmo sofro desse mal às vezes.
Abaixo segue um diálogo hipotético entre um programador (PG) e uma pessoa normal (PN):
PN: E aí programador, tá afim de ir ao cinema hoje?
PG: Hmm, acho que sim.
PN: Ahh legal aí a gente pod…
PG: (interrompendo) Mas que filme vamos ver? Se for de romance eu não vou!
PN: Não, é um filme de ação, o…
PG: (interrompendo) Ah bom. Mas é lançamento? E se a fila estiver grande?
PN: Ah, o filme saiu a umas duas semanas atrás.. não deve ter fila.
PG: Mas e se tiver? O que faremos?
PN: Sei lá, assistiremos outro filme.
PG: Mas, se a fila estiver cheia, que filme assistiremos? Não seria melhor decidir agora?
PN: Ah vai pra #@!&#)*#!! Fica aí na tua casa que eu vou sozinho.
Obviamente foi apenas uma brincadeira,
mas o que deveria ser algo divertido acabou se transformando num verdadeiro inferno.
A dica é: deixe o amanhã para amanhã! Se te chamarem para ir ao cinema, vá ao cinema! Se tiver lotado, procure pensar em uma alternativa, mas apenas caso isso aconteça. Não deixe que a probabilidade de algo sair errado te faça agir defensivamente.
Felizmente, podemos dizer que, graças às metodologias ágeis, essa coisa de se tentar prever o que acontecerá no futuro está perdendo a força. Aliás, não sei como isso chegou a ganhar força um dia, visto que não somos seres com habilidades extra-sensoriais. Mesmo que seu time seja composto de clones de Walter Mercado, Mãe Diná e Jucelino da Luz, a chance de se adivinhar logo no início tudo o que ocorrerá durante o desenvolvimento de um software é mínima. Pensando melhor, diria que é impossível.
Quem aqui não se lembra dos desabamentos de shoppings, onde a Mãe Diná só aparecia depois do ocorrido para dizer que previu o acontecimento? Ou seja, se nem a Mãe Diná consegue prever o futuro, então pra quê perder tempo tentando?
Lição 6: aprenda que sempre…
… há algo a aprender.
Fotos por: A Boy And His Bike, Rufus Gefangenen, R80o (Mark Strozier), tanakawho, silly rabbit!
Tags: desenvolvimento, design, dicas, faculdade, lições, práticas, software

23 de junho de 2007 às 5:14 pm
Leitura agradável
Gostei, especialmente, da parte que você diz que se as especificações chegassem corretamente, você não teria adquirido certas habilidades. Foi uma ótima reflexão. Não é tão comum ver as pessoas valorizando ambientes caóticos (e estressantes) como “trainning fields”. Talvez isso seja a vida…uma longa jornada de treinamento em um mundo caótico.
Ok, chega!
23 de junho de 2007 às 11:41 pm
Não é porque eu consegui tirar algo de bom desse período apocalíptico que isso terá de se repetir indefinidamente!
Sai de ré, satanás!
24 de junho de 2007 às 2:03 pm
Parabéns pelo post.
Completo e perfeito, refletiu muito o meu dia a dia, e acabou me dando uns conselhos que eu fico me dando sempre mas nunca me ouço.
Ex:
Por que ainda faço isso?
ou isso?
Vou imprimir esse post, para sempre ler quando ve ver nessa tal situacao!
[]’s!
AFS
24 de junho de 2007 às 2:41 pm
Muito obrigado pelo comentário, Alexandre!
Aliás, na mesma hora que seu comentário foi feito, eu estava adicionando uma nova lição que havia esquecido de colocar no post: a lição número 4.
Enfim, valeu pelos elogios… que bom que gostou (deu o maior trampo pra escrever).
Abraço!
24 de junho de 2007 às 5:13 pm
Esse post 4 merece uma atenção maior. Pois parece que nós programadores temos os mesmo problemas, não sei se isso é bom ou ruim. Se quisermos melhorar nossos vícios podemos ouvir os outros , que sempre é melhor do que ouvir a nós mesmo(pelo menos , assim é comigo).
Hoje eu estava pensando: “Toda hora que eu alterou meia- dúzia de classes e ficou com preguiça de joga-las no JBoss, eu gero um war novo ant -f build-4.0.0.xml .”
Decidi criar um script, que ao invés de criar nome-do-projeto+data+hora.war, cria se uma pasta com o nome-do-projeto.war(eu uso portlet, então já me facilita) e move para o servidor de aplicação.
E onde isso me pareceu uma coincidência, você falando de deploy, script e deu exemplo de servidor de aplicação.
O que eu tentei tirar o dia para isso, mas acabei tirando o dia para outra coisa, mas logo logo crio esse script.
Pois , o trabalho repetitivo é muito anti-produtivo, quando o mesmo é braçal e pode se tornar automático.
25 de junho de 2007 às 12:39 pm
Meu mantra pessoal. Procuro ser particularmente rigoroso com isso.
25 de junho de 2007 às 1:06 pm
Dando uma pesquisada rápida vi que esse quote é relacionado em diversos materiais sobre Lisp.
Confesso que não entendi o significado dele (os estudos pra certificação tão me fazendo mal…), mas já coloquei uma meia-dúzia de links nos bookmarks pra poder dar uma olhada depois.
25 de junho de 2007 às 6:43 pm
Haha. Bom, acredito que signifique algo como:
“Eu objeto fazer coisas que o computador pode fazer”.
Creio que diz respeito a utilizar, de fato, o computador ao se recusar fazer coisas que este pode fazer com maior eficiência.
Quanto a Lisp, eu não sei, mas eu a li pela primeira vez na página do Paul Graham. A beleza dela se destacou e eu acabei copiando-a para referências futuras
25 de junho de 2007 às 6:55 pm
Aee!
Acreditava que fosse algo nessa linha mesmo, visto que existiam outros quotes que passavam a mesma mensagem…
26 de junho de 2007 às 10:00 am
Ótimo post. Gostei e concordo principalmente com a lição 1.
26 de junho de 2007 às 8:54 pm
Hoje eu usei muito a licao 4.
E sobre a frase inglês acima é realmente muito complexa.
A tradução correta é:
Eu detesto fazer coisas que computadores podem fazer por mim
26 de junho de 2007 às 8:58 pm
Saber usar a vagabundice a nosso favor é um dom! Mas, como qualquer outra coisa, é 10% inspiração 90% transpiração (ou não, afinal, gente preguiçosa não costuma transpirar)
27 de junho de 2007 às 11:03 pm
Só uma correção eu queria: Lição número 3
Usei naquele dia e usei hoje , fiz 12 horas sem pensar nas infra.
Como ja estou com queries montadas e mapeamento na cabeca. Amanha até o meio-dia gero uma versao para homologação.
[]’s
28 de julho de 2007 às 11:25 am
Cara, muito boas as dicas valeu. Eu rí muito com o exemplo do cinema hehehe
28 de julho de 2007 às 11:31 am
E aí Gabriel!
.. sabe o que é pior? É um exemplo mais ou menos real (adivinha quem é o chato na estorinha)?