Capítulo 0 – O Programador Profissional
Sobre o futuro…
A melhor maneira de prever o futuro é inventá-lo.
– Alan Kay
Este capítulo trata dos conceitos usados no restante do livro, incluindo uma introdução ao que os programadores profissionais fazem. Não é exatamente um capítulo técnico, mas ele contém uma boa parte do que eu gostaria que tivessem me dito quando estava começando e penso que ele pode te ajudar nesta caminhada de Pequeno Padawan a Mestre Jedi.
De amador a profissional
O termo programador é muito sobrecarregado atualmente. Antigamente era uma das mais conhecidas profissões na área da computação, mas hoje existem muitos termos novos, tais como: Gerente de Projeto, Desenvolvedor de Software, Desenvolvedor Back-end, Arquiteto de Software, Desenvolvedor Web, Desenvolvedor Front-end, Engenheiro de Software, Engenheiro de UX, Especialista em Devops, etc – bah, a lista é longa.
Primeiro, é preciso estabelecer uma noção do que é ser programador, suas competências e responsabilidades. Então, na evolução deste livro, vamos tentar entender como programador mais do que um profissional com a habilidade simples de escrever códigos. Com a evolução dos projetos de software, linguagens, plataformas, etc, o papel do programador, o ser programador, também passou por evoluções e recebeu novos significados.
Programar, hoje, é uma atividade coletiva e multidisciplinar. O programador de hoje trabalha em sistemas onde o código-fonte compartilhado com dezenas, às vezes centenas, de outros programdores. Trabalha com sistemas heterogêneos, escritos com mais de uma linguagem, e multiplataformas, que rodam sobre diferentes servidores e ambientes. São sistemas que, embora tenham longos ciclos de vida, são entregues em pequenas partes e em curtos intervalos. Resumindo, muito mudou. Programador não é mais aquele cara que ficava encaixotado na sua baia. Aquele que era especialista em uma determinada linguagem e praticamente proprietário de uma determinada parte do sistema.
Outra questão, bem clara, é que as empresas não precisam de alguém que programe para construir funcionalidades e resolver problemas pontuais. Em vez, e de fato, elas precisam de alguém que programe para resolver uma família de problemas atuais e futuros. Elas precisam do programador que projeta e escreve um código-fonte legível, fácil de entender, sustentável, com boa performance. Precisam de programadores para não um sistema, senão uma família de subsistemas, novos e existentes, que se intercomunicam, rodam em espaços e tempos diferentes.
Há bastante gente ingressando no mercado de desenvolvimento de software. Tem muita gente nova e capacitada. Mas, infelizmente, poucos evoluem e ocupam uma posição de destaque, ou até cargos mais atrativos e com melhores rendimentos. A palavra-chave aqui é responsabilidades. Será que alguém evolui profissionalmente por ter mais responsabilidades? ou por ter mais responsabilidades alguém evolui profissionalmente1? Bem, o fato é que os profissionais adquirem responsabilidades – ou as responsabilidades os procuram.
Não é difícil perceber a diferença entre programadores amadores e profissionais – na verdade, é até bem fácil. O profissional é aquele que realmente se importa, aquele que sempre planeja, aquele que sempre projeta – tudo é friamente calculado. O amador é aquele que faz o mínimo necessário, com pouco ou sem planejamento, sem fazer projeções, sem se importar com o futuro do software que está escrevendo – nem com seu próprio.
Como vais ver no decorrer deste livro, existem muitos exemplos e eles ajudam a explicar conceitos muito abstratos usando uma situação concreta. A seguir, vou apresentar 10 situações (que separei entre tantas outras) as quais colocam lado-a-lado o perfil de um programador profissional e um amador:
- O profissional escreve um código inteligível, sustentável, que seus colegas entendem. Por outro lado, o amador não se preocupa com a legibilidade e codifica como se o código nunca mais fosse ser visto outra vez na vida – nem por ele mesmo.
- O profissional resolve não apenas o problema atual, mas também os problemas futuros da mesma família. O amador, por sua vez, faz o mínimo, resolvendo apenas o problema particular.
-
O profissional cria pedaços reutilizáveis de código, constrói bibliotecas e componentes de software os quais são incluídos e compartilhados entre projetos. No entanto, o amador cria códigos monolíticos, imóveis, e quando precisa reutilizar códigos (lógica) entre projetos, usa o famoso
CTRL+CeCTRL+V, espalhando duplicatas por toda a base de código – pior, por todas as bases de código. - O profissional conversa com seus colegas, difunde o conhecimento, troca experiências e usa o vocabulário técnico adequado para passar suas ideias. Por outro lado, o amador evita se comunicar, não usa o vocabulário compartilhado, alheio, não se importa com princípios, nem padrões ou técnicas de projeto.
- O profissional conhece especificidades da linguagem em que está programando, como detalhes da sintaxe e as bibliotecas disponíveis. O amador, no entanto, resolve tudo usando os construtos mais básicos e primitivos da ferramenta, além de frequentemente escrever um código que resolve um problema que já foi resolvido por alguma biblioteca ou um companheiro, pois julga ser sempre melhor à sua maneira – sofre do efeito Dunning-Kruger2.
- O profissional usa a ferramenta certa para o problema certo. O amador, por sua vez, tenta resolver todos os problemas de todos os tipos sempre com a mesma abordagem3, olha tudo sob o mesmo ângulo.
- O profissional acredita na propriedade coletiva de código, aceita e sugere que seus colegas usem e alterem seu código, sugere e considera sugestões da equipe. Por outro lado, o amador não quer que “mexam” no seu código e muito menos aceita “mexer” no código de terceiros, reproduzindo bordões como “não toquem nesse código que ele é meu” e/ou “esse código não fui eu que fiz”.
- O profissional lê livros técnicos, blogs de especialistas e gurus; está sempre atento às novidades. O amador, bem, não se importa em ler. Quando o amador precisa de uma ideia, ele usa o Google, abrindo primeiro link que aparece4 (geralmente um do Stack Overflow).
- O profissional testa seu código, projeta as entradas e saídas possíveis e se antecipa aos erros do cliente. O amador testa apenas com a especificação que recebeu (se testar), situações não pensadas pela equipe de análise estão fadadas a sorte – ou azar.
- O profissional sabe usar ferramentas de desenvolvimento, conhece teclas de atalho, comandos do terminal e automatiza suas tarefas mais comuns. O amador “chafurda” os menus, ignora comandos do console e reproduz repetitivamente e manualmente suas tarefas.
Sobre o livro
O objetivo deste livro é oferecer os conhecimentos e habilidades para programar profissionalmente. É um compilado de anos de experiência em programação com anos de sala de aula ensinando como programar de forma efetiva e eficaz.
Deste ponto em diante tu vais encontrar dicas do tipo “considere” e “evite”, apresentadas nos seguintes formatos:
Existem diferenças entre programador e desenvolvedor?
É uma dúvida muito comum (e polêmica). Existem muitos argumentos sobre o que é (e o que faz) um Programador e um Desenvolvedor de Software5. Não é minha intenção trazer uma opinião unilateral e impor uma definição. Portanto, é necessário ficar claro que existem duas linhas principais de pensamento a respeito:
1: Uma linha de pensamento é de que Programador e Desenvolvedor são sinônimos, que uma definição ou outra pode ser usada para expressar uma “pessoa que escreve códigos que computadores executam”.
2: A outra linha de pensamento é que Programador e Desenvolvedor são profissões diferentes. O argumento é que Programadores escrevem códigos seguindo uma especificação, enquanto Desenvolvedores especificam, projetam e escrevem programas.
Honestamente, tenho dúvida sobre qual é a melhor definição. Contudo, confesso que na minha carreira vi claramente perfis de profissionais que se encaixam como “só codificares”, que escreviam a partir de uma especificação sem questioná-la ou projetá-la, bem como outros que planejavam, projetavam, codificavam e testavam, exercendo atividades em várias etapas do (longo) processo de desenvolvimento de softwares.
O tema é muito polêmico. Minha sugestão é que ouças e leias outras opiniões sobre Programador/Programação e Desenvolvedor/Desenvolvimento (sem falar de Engenheiros de Software, Cientistas da Computação, Engenheiros de Computação, etc). Por exemplo, podes fazer uma pesquisa a respeito: https://www.google.com.br/search?q=programador+desenvolvedor – deves achar absurdo um livro te jogar para o Google para esclarecer uma dúvida, mas não quero entrar nessa treta!
Então? Sugiro que façamos assim: no decorrer deste livro usarei o termo Programador. Este livro é sobre escrever códigos de alta qualidade, que parece a exata competência de um programador – lógico, na minha humilde opinião.
Importante! Partiremos também do pressuposto que todo Programador planeja, todo Programador faz projeções a respeito do código, isto é, se pressupõe que o Programador projeta e escreve programas, para efeito neste livro.
O preço do amanhã está na habilidade de projetar
Os programas, ou sistemas, costumam ter uma vida loooonga. Eles recebem adições e alterações de funcionalidades durante muitos anos. Dito isso, pense que os programas precisam de um planejamento, para crescer de forma ordenada. Assim como as cidades, eles também podem sofrer de crescimento desordenado. Com essa analogia acredito que consigas imaginar os sintomas e o resultado de um crescimento sem controle, certo? Logo, os programas precisam de um “Plano Diretor”.
Sendo direto, os programas precisam de um planejamento claro, consistente e com o olho no futuro, para permitir que cada mudança não se torne um evento traumático. Isto é, o planejamento existe para permitir que o programa seja fácil de alterar sem perder sua eficiência, estabilidade e outros atributos qualitativos. Em outras palavras, tu deves projetar e escrever códigos com boa qualidade técnica, para que possas alterá-los no futuro sem medo de que ele vá quebrar ao tocar (esta, aliás, é a definição de sistemas robustos, em oposição aos sistemas frágeis).
Programas implementados com pouco (ou nenhum) projeto anterior, tendem a ser instáveis e frágeis. Estes tipos de sistema rejeitam alterações e são difíceis de lidar. Programas que não foram projetados dificultam a submissão de modificações, sem precisar que sejam feitas alterações em cascata e, pior, sem introduzir três erros ao tentar corrigir um – é a Hidra, corte uma cabeça e duas nascem.
O segredo do sucesso das grandes empresas que desenvolvem software de qualidade é fazer boas projeções. Um bom projeto (no sentido de projetar, desenhar, design em inglês) garante uma vida mais saudável ao programa, com mais estabilidade, eficiência e, principalmente, suscetibilidade às alterações (característica qualitativa conhecida como flexibilidade).
Projetar programas refere-se a projetar qualquer “porção” dele. Ou seja, qualquer parte do programa pode ser codificada sem ou com projeções. Ou seja, tu podes codificar apenas para cumprir a especificação, sem te preocupar com o futuro, ou podes dar um minuto a mais de raciocínio quando escreveres classes, atributos, funções, métodos, parâmetros, estruturas de dados e quaisquer algoritmos. Enquadre isto como investimeno e não perda de tempo.
A palavra de ordem é DI-VI-DIR
Projetar envolve fazer escolhas difíceis que influenciam o futuro do programa. A boa notícia é que existem princípios, práticas e padrões que ajudam a fazer estas escolhas. Mas, se reduzires todos os conselhos em um só, qual seria? Ele seria DI-VI-DIR!
Dividir um programa em pequenas partes traz várias vantagens. Por exemplo, permite que as partes sejam reutilizadas, testadas e combinadas para criar partes maiores. Porém, dividir também traz uma desvantagem, que é o aumento de indireção, ou seja, uma funcionalidade vai depender de outra, que vai depender de outra e assim por diante.
O principal pecado dos sistemas mal projetados está em criar programas monolíticos (uma grande peça única). Considere como exemplo concreto um método (ou função), que pode ser projetado ou não. Projetar métodos implica em raciocinar sobre ele, fazendo projeções a respeito da inteligibilidade, estabilidade, flexibilidade e reuso. É bem comum, quando se projeta métodos, perceber que ele precisa ser quebrado em métodos menores, cada um resolvendo um subproblema. Alguém que codifica sem projetar tende a resolver tudo de forma monolítica, resultando em poucas classes e métodos, todos com muito código – ver God Class.
Confesso que conheço muitas pessoas relutantes em dividir o programa. Desde alunos, que estão iniciando, até colegas de trabalho, que programam desde que tudo isto aqui era mato. Todos têm algum argumento falacioso para não separar o programa em partes menores. Bem, é preciso ter em mente que qualquer atividade de engenharia, seja civil, mecânica, etc, trabalha com a ideia de construir em partes e juntá-las para construir partes maiores até ter o produto completo. Embora especialmente diferente, a Engenharia de Software, em essência, tende ao mesmo princípio das outras engenharias, sendo que a única diferença é que as peças são lógicas e não físicas.
O impacto na Qualidade de Software
Todo programa faz o que tem que fazer (embora existam controvérsias :). Cumprir suas funcionalidades sem erros é a obrigação de um programa. É a principal métrica de qualidade percebida pelo usuário final. Entretanto, existem outras métricas de qualidade. Por exemplo, aquelas que aparecem no momento em que o programa precisa passar por alterações.
A Garantia da Qualidade do Software é uma disciplina importante na Engenharia do Software. Existem várias preocupações, muito além da qualidade do código. Entretanto, o código-fonte tem influência direta em tudo, ou seja, escrever código de boa qualidade técnica é um caminho para construir softwares de melhor qualidade.
Pedaços de mau código
Então, se a qualidade do código é tão importante para a qualidade final de um sistema, como podes controlar isso? É esperado que os iniciantes tenham dificuldade de diferenciar um bom de um mau código. Eu sei disso porque (1) já fui iniciante e (2) sou professor atendo alunos nos cursos técnico e superior, que são iniciantes. A métrica básica para um bom código costuma ser funciona => bom e não funciona => ruim, infelizmente.
Contudo, com um pouco de experiência e prática, é fácil identificar um código com baixa qualidade técnica. Isto é possível usando métricas melhores do que simplesmente funciona ou não funciona. Não é incomum um programador experiente abrir seus programas velhos e pensar “como foi que eu escrevi essa m*?!”.
Como uma introdução à codificação com profissionalismo, eu vou apresentar, a seguir, dois exemplos de problemas comuns presentes nos códigos de baixa qualidade técnica. Importante: estes programas funcionam (nunca é para discutir o funcionamento, funcionar é o mínimo), mas têm os seguintes problemas:
O problema dos nomes
Um dos problemas comuns encontrado nos “códigos da vida” é a má escolha de nomes. Considere um encontro com o código a seguir:
A pergunta básica é: o que esse método faz? Estou no paraíso? Que ótimo! Sarcasmo a parte, claro que, com leitura, raciocínio e experimentos, é possível descobrir o que ele faz. No entanto, pense que todo código precisa de manutenção – e a manutenção, a manutenção – que tem um custo conferido pela seguinte fórmula:
custo_manutenção = custo_entender + custo_alterar + custo_testar + custo_implantar
Em geral, classes, métodos e variáveis com nomes obscuros, ou que não compartilham o significado com quem está lendo, acabam aumentando o custo para entender, logo para alterar e, portanto, aumentando o custo de manutenção. Custo é tempo, tempo é dinheiro. Maior custo significa perder dinheiro; resultado: maus nomes custam dinheiro!
Clarificando, este método converte uma data recebido como uma String e no formato português brasileiro dd/mm/aaaa para o formato ISO aaaa-mm-dd. O nome paraiso(String):String significa data para o formato ISO6.
A maneira básica, tradicional, de descobrir o que um método ou função faz é: chama e observa a saída. Por exemplo:
É possível entender o método se ele for invocado e, então, observar a saída. No entanto, se fossem usados nomes melhores, tanto para a classe como para o método, não seriam necessárias experiências para entendê-lo. Classes, métodos e variáveis devem ser projetados para serem entendidos em uma leitura, como uma oração no sentido textual. O objetivo é converter uma data do formato BR para o formato ISO, portanto, veja a mesma funcionalidade codificada de forma mais descritiva:
Para alguns a diferença parece sutil e até irrelevante, mas veja a seguir a chamada do novo método:
É mais expressivo chamar um método nomeado como Data.deBRparaISO("19/08/2014") do que Util.paraiso("19/08/2014"). A regra geral é que classes e métodos devem ser escritos para serem lidos, como uma redação.
O problema das expressões condicionais confusas
Construir e projetar expressões condicionais parece, e geralmente é, uma tarefa trivial. No entanto, escrever condicionais exige atenção à certos detalhes que envolvem performance, clareza, concisão, entre outros cuidados – daria para escrever um livro só sobre isso, acredite. O pior caso é quando as expressões condicionais são mal utilizadas tecnicamente, como no exemplo a seguir:
else no lugar do if.Este caso é um exemplo de ou imperícia ou negligência – o segundo é pior. Programadores Profissionais não escrevem códigos assim – e nem mesmo os iniciantes mais atinados. Claro que o else não é necessário e esse código poderia ser escrito assim:
ifs responsavelmente.O mesmo código poderia ser ainda mais expressivo, aplicando uma refatoração bastante comum, chamada método consulta. O código fica mais inteligível quando a consulta é realizada diretamente no objeto (em vez de codificada em uma expressão). Isto ajuda a dispensar os comentários explicativos. Veja o mesmo código refatorado no exemplo a seguir:
Existem outros exemplos de más utilizações de expressões condicionais, onde elas poderiam ser melhor escritas ou até eliminadas, como no código a seguir:
ifs e elses (https://git.io/JOCL8).O interessante nesse código é que ele funciona! Ele devolve exatamente a quantidade de dias dado um mês e ano. Entretanto, o código é pouco inteligível. Com certeza poderia ser melhor escrito, a começar pelo tamanho – o método todo tem quase 40 linhas.
O mesmo problema pode ser resolvido com o mínimo de if’s. Porém, o curioso é que o mesmo problema resolvido com menos linhas (e menos código) ainda pode parecer estranho, como no código a seguir:
A moral da história é: métodos com poucas linhas (código concisos) não necessariamente são claros (inteligíveis). O código anterior serve para demonstrar que mesmo um código pequeno pode ser confuso. Neste exemplo em particular, algumas boas práticas foram ignoradas, como a convenção de sempre usar chaves {} nos if’s – mesmo naqueles que tenham apenas uma instrução.
A maioria das expressões condicionais podem ser escritas de modo mais simples com o uso de algumas técnicas de refatoração. Por exemplo, com a introdução de uma variável explicativa ou de um método consulta (refatorações serão vistas adiante neste livro no Capítulo 11: Melhorando Códigos Existentes). Na prática, algumas expressões condicionais podem ser até eliminadas. O código a seguir mostra uma abordagem diferente, com o uso de métodos auxiliares e um array de inteiros servindo como um mapa mês => dias para eliminar os testes condicionais longos:
A raiz de todo o mal
A maioria dos códigos difíceis de ler e entender acontecem ou por desconhecimento ou por negligência – o primeiro motivo tem solução. Todos os códigos podem ser esclarecidos com bons nomes e a introdução de código explicativo. Ademais, códigos escritos em pequenas partes são reutilizáveis e combináveis.
Técnicas, Práticas, Princípios, Padrões e Bruxarias
Bom software é estável, flexível, confiável, fácil de alterar e introduzir novas funcionalidades, etc. No entanto, como se atinge este nível de qualidade?
É possível construir software de qualidade se fores um profissional disciplinado, se dominares a linguagem de programação e conheceres algumas técnicas, práticas, princípios e padrões, que são conhecidos e difundidos na comunidade de desenvolvimento de software.
Boas práticas nascem de bons hábitos. As boas práticas são frutos das ações disciplinadas, por exemplo, de estabelecer padronização para a escrita do código, seu formato e estrutura.
Técnicas são soluções especiais, sofisticadas, para problemas recorrentes. São normalmente aprendidas, ou descobertas, com o tempo. Isto é, são fruto da experiência. Algumas dessas técnicas são catalogadas e difundidas na comunidade de desenvolvimento de software.
Às vezes são detalhes óbvios, mas são eles que diferenciam o profissional do amador. Saber as práticas e técnicas é só o começo de uma carreira. A atividade de programar, de desenvolver software, está cheia de mantras, que vão além de escrever código. A profissão exige conhecimentos de planejamento, arquitetura e projeto de sistemas, utilização de princípios e padrões de projeto, realização de testes, depuração e medições – na verdade, até pensei em introduzir tudo isso neste livro, mas ficaram para outros volumes, este aqui é dedicado à codificação.
Existe uma diferença entre conhecer o caminho e percorrer o caminho …
Mesmo que saibas inúmeras técnicas e práticas sofisticadas de programação, só vai fazer diferença se elas forem usadas efetivamente. Não é só para escrever pequenos experimentos. É para programar de verdade.
A dúvida comum é se somos ou não competentes para programar mais sofisticadamente. Não penses ou tentes ser um profissional, saibas que és um – pois se te pagam para programar.
E lembres que maus hábitos são difíceis de perder. Por outro lado, novos e bons hábitos são difíceis de cultivar. Tudo é uma questão de persistência, de força de vontade mesmo. Ninguém é bom no que faz sem treinar.
O que vem a seguir
Após toda esta introdução, eu espero que estejas motivado. Programadores constroem coisas incríveis todos os dias. Obviamente, são os profissionais do presente, mas principalmente do futuro.
O restante deste livro está cheio de conteúdo para programadores – de programador para programador. Orgulhe-se dessa profissão e valorize-a – nós vamos dominar o mundo (risada maligna neste momento).