terça-feira, 31 de dezembro de 2013

Uma retrospectiva pessoal dos últimos 3 anos!

O ano de 2013 foi um ano realmente produtivo pra mim. Concluí projetos pendentes, iniciei novos projetos, atingi objetivos profissionais, encarei novos desafios, e isso tudo é devido fortemente de uma mudança de atitude que ocorreu ao final de 2011.

Há algum tempo recebi um conselho que considero o mais importante que já recebi: "não invista demais em sua mente se esquecendo de cuidar também do seu corpo, pois um não vive sem o outro". Na época eu passava por alguns problemas de saúde devido basicamente ao estresse e ao sedentarismo, e esse conselho me fez abrir os olhos e perceber que de fato eu estava investindo demais em qualificação e deixando de lado o lazer, a saúde e o bem estar, algo considerado normal nos dias de hoje. Isso foi em meados de 2009, e de lá pra cá passei a dar muito mais atenção ao meu bem estar e principalmente à minha saúde.

Decidi, a partir de então, estudar menos, deixar as coisas evoluírem mais devagar, usar um pouco o produto de tanto investimento antes de investir mais. No entanto, isso me fez perceber que a qualidade do investimento que eu havia feito até então não era bem o que eu esperava. Ainda me sentia despreparado, ainda havia dúvidas que eu precisava responder para me sentir maduro profissionalmente.

Como então equilibrar as coisas? Voltar a estudar, produzir mais e melhor, e ao mesmo tempo não passar pelo estresse absurdo em que vivia?

Primeiramente, mudei de emprego, pois vinha de lá a grande fonte de estresse pelo qual eu passava. Mais que isso, pedi demissão sem nem mesmo ter outro emprego. Decidi ter férias prolongadas, esquecer completamente os compromissos e as consequências, passei dois meses apenas descansado, e por fim retomei os estudos.

Fiz uma breve pesquisa de mercado para entender o que eu precisaria aprender de novo, em que eu precisaria me reciclar, escolhi alguns livros, e passei a me dedicar a eles. Isso aconteceu em meados de 2011.

Era hora de voltar à realidade. Pesquisei um pouco a respeito de algumas empresas de Belo Horizonte onde eu gostaria de trabalhar e fiz contato. Destas, nenhuma oportunidade surgiu, ao menos não nas semanas iniciais, mas para minha surpresa recebi uma ligação de uma outra empresa, até então desconhecida por mim, que havia recebido uma indicação de alguém para me contactar, pois eu seria a pessoa certa para resolver o problema pelo qual eles passavam naquele momento. Fiz contato numa quinta feira, e no dia seguinte já havia sido contratado. Parecia mesmo urgente o problema rs.

Em um novo ambiente, livre do estresse de antes, encontrei espaço para buscar os novos desafios que precisava para me manter motivado.

Em paralelo a isso, passei a investir mais em saúde, o sedentarismo crônico ficou para trás, embora sempre passe de vez em quando para dar um oi. E a ideia de pesquisar o que eu previsava aprender, em que eu precisava investir, foi um ótima ideia. Aprendi muito com os 6 primeiros livros que escolhi, e vi que esse seria um ótimo caminho, muito mais produtivo que a participação em cursos e a leitura de blogs e mais blogs, os quais eram minha principal fonte de informação até então.

Apenas no segundo semestre de 2011 li 6 livros, um recorde para quem era acostumando a ler não mais que 1 ou 2 livros por ano, se muito. Em 2012 a produção já foi mais baixa em números, mas não em valor. Ao final do mesmo ano, diante do desafio de produzir um trabalho de conclusão de curso que demandaria bastante pesquisa, decidi tornar a leitura uma atividade mais frequente, e decidi que faria isso sem prejudicar as demais atividades do meu dia a dia. Qual a solução? Não mais que míseros 30 minutos de leitura diária, antes de dormir, principalmente. Com essa estratégia, consegui concluir, supreendentemente, 15 livros técnicos no ano de 2013. Isso enquanto fazia um curso de pós graduação, que me exigia em média 1 hora por dia de dedicação.

Hoje tenho tempo para me dedicar ao meu bem estar, e em 2014 talvez invista menos em qualificação, mas certamente com muito mais qualidade que nos anos anteriores a 2011. Ao que tudo indica, será um bom ano.


sexta-feira, 20 de dezembro de 2013

Conclusão do Curso de Especialista em Estratégias em Arquitetura de Software

Desenvolvo software desde 1998, profissionalmente desde 2002, mas apenas em 2008 identifiquei a área de Arquitetura de Software como aquela que mais me desperta interesse. Naquela época, ainda era uma área bastante marginalizada, ainda sendo um pouco ainda hoje. Afinal, quantas empresas possuem o cargo Arquiteto de Software? Quantas se quer sabem o que seria um Arquiteto de Software? Infelizmente ainda não parecem ser a maioria. Por conta disso, ainda é baixa a oferta de cursos de qualificação específica na área.

Ainda em 2008, soube da criação em Belo Horizonte do IGTI, uma instituição que passava a oferecer o primeiro curso de Estratégias em Arquitetura de Software do país, sendo uma das poucas a oferecer tal curso ainda hoje. Me interessei bastante, mas por inúmeros motivos não me matriculei. Até que, em 2012 soube que o mesmo curso passou a ser oferecido na modalidade de Ensino à Distância e com isso seria bem mais fácil conseguir o tempo necessário para me dedicar às aulas. Não havia mais desculpas. Fiz o curso e gostei bastante. Aprendi muito, ratifiquei boa parte do conhecimento que adquiri na prática ao longo dos anos, fiz bons contatos profissionais e bons amigos.

Desde os primeiros meses de curso, já falávamos a respeito do trabalho de conclusão, e aproveitei a oportunidade para estudar mais a fundo dois temas que muito me agradam, e que visivelmente contribuem para a melhoria da qualidade do meu trabalho e para o aumento de minha produtividade, que são as já bem conhecidas Boas Práticas para Desenvolvimento de Software e as nem tão conhecidas Especificações por Exemplo.

Desde meu primeiro contato com Especificação por Exemplo, então conhecidas por mim apenas como Behavior-Driven Development, já notei o quanto a técnica tornava mais simples a definição do que deve ser feito e como o trabalho deve ser divido, implementado e testado. Tendo uma boa técnica para verificar continuamente a qualidade do trabalho, fica bem mais fácil refatorar continuamente o código e aplicar continuamente as tão famosas Boas Práticas.

O resultado desse trabalho está disponível neste link. Confiram.

quarta-feira, 27 de novembro de 2013

A sindrome do arquiteto astronauta

Desde que iniciei minhas aventuras com programação, na época que não fazia mais que encontrar novos meios de se usar a função SE no Excel 5, sempre gostei de pensar, inventar, descobrir e usar coisas novas. Cada novidade que eu podia experimentar, que podia tornar o código que eu escrevia mais agradável, me trazia satisfação. Não me bastava saber como criar uma tela de cadastro, isso já no Delphi, eu queria sempre buscar uma nova maneira, mais eficiente, de fazer isso, e ainda hoje tenho esse impeto pela inovação e pela maior eficiência. Arquitetura de Software sempre foi a minha praia dentre as várias que um desenvolvedor de software pode escolher. E claro, quem experimenta coisas novas erra muito mais que acerta. É necessário muita experimentação até se conseguir maturidade para identificar as chances de sucesso do próximo experimento, portanto coleciono muitas decisões equivocadas, das quais me orgulho pois cada uma delas contribuiu para os acertos seguintes.

Em meados de 2008, num período em que eu já me considerava capaz de criar soluções mais elaboradas, e já estudava com mais profundidade temas como padrões de projeto e boas práticas de desenvolvimento, me lembro estar construindo uma solução que, obviamente, tendia à perfeição. No entanto cada uma das imperfeições ainda existentes me incomoda absurdamente e cada dia eu gastava mais tempo tentando me livrar delas. Foi nesse período que alguém me disse: "você está sofrendo da síndrome do arquiteto astronauta, não deixe a busca pela perfeição te impedir de fazer um bom trabalho". Minha primeira reação foi pensar que aquilo simplesmente não fazia sentido, seja lá o que fosse a tal síndrome, já que buscando a perfeição, obviamente eu acabaria fazendo um bom trabalho. No entanto, na mesma época eu também passava por um processo de amadurecimento profissional forçado em que decidi aceitar toda crítica recebida independente de concordar ou não, buscando tirar algo positivo dela. Era mesmo possível que eu estivesse buscando algo desnecessário, ao menos para aquele contexto?

O arquiteto astronauta é aquele que vive no mundo da lua, fora da realidade, buscando soluções de outro mundo, vidrado demais em padrões e mais padrões, buscando sempre a solução mais perfeita possível sob o ponto de vista técnico. Para ele, se a solução pode ser melhorada, ela deve ser melhorada, e deve ser melhorada de imediato. Se uma tecnologia nova e interessante surge, ele vai querer usá-la, ainda que não tenha real necessidade. Se um novo padrão está bombando e se propõe a resolver o problema que ele enfrenta, ele irá querer usar tal padrão a qualquer custo. Assim cada vez mais novas tecnologias e padrões vão sendo agregadas a suas soluções, e dai começam a surgir as evidências de que sua super complexa, moderna e foda arquitetura, não é tão perfeita assim.

"Simples é melhor". "Mantenha isso simples, estúpido". "Você não vai precisar disso". São princípios listados entre as boas praticas que um bom arquiteto deve ter a obrigação de seguir, mas que normalmente são ignoradas pelo arquiteto astronauta. Hoje sei bem disso. Caso as ignore, suas soluções acabarão lotadas de tecnologias e padrões que tornarão seu código mais complicado de ser compreendido e modificado, a execução do software ficará mais lenta, dadas as camadas e mais camadas de abstração e linhas de execução, você mesmo ficará sobrecarregado em ter que gerenciar aquilo tudo.

Pra encerrar a história: Tenha sempre em mente a sua necessidade atual. Necessidades futuras mudarão, e suas soluções previamente implementadas podem se quer vir a atendê-las, quanto menos da melhor forma. "Você não vai precisar disso". Mantenha as suas soluções simples. Evite abstrações demais, que não se justifiquem na prática. E quanto aos princípios SOLID, lembre-se que eles existem para lhe orientar no processo de refatoração e melhoria de código, não no processo de construção. Evite otimizações antecipadas, antes de ter uma versão menos otimizada do código já funcionando, ou ainda, antes de identificar que tais otimizações são realmente necessárias. E o principal, evite criar situações apenas para lhe permitir usar um determinado padrão ou tecnologia. Não é por que programação distribuída lhe oferece diversas vantagens em diversos cenários que você deve executar cada procedimento em uma thread separada. Não é por que MongoDB é legal que você deve descartar o bom e velho Oracle sem pensar duas vezes.

segunda-feira, 11 de novembro de 2013

Apague o seu código para torná-lo melhor

Menos é mais. Essa é uma máxima um pouco banal, mas as vezes é verdadeira. Uma das melhorias que tenho feito em nossa base de código nos últimos anos tem sido remover pedaços dela.
Escrevemos o software seguindo dogmas de XP, incluindo YAGNI (que quer dizer, You Aren’t Gonna Need It, Você Não Vai Precisar Disso). Mas sendo nossa natureza humana como é, inevitavelmente somos fracos em alguns pontos.
Observei que o produto estava levando muito tempo para executar certas tarefas, tarefas simples que deveria ser quase instantâneas. Isso por que estavam sobre-implementadas – enfeitada com firulas que não eram necessárias, mas que naquele momento pareciam ser uma boa ideia.
Então simplifiquei o código, melhorei sua performance, e reduzi o nível de entropia global do código simplesmente removendo as funcionalidades desnecessárias da base de código. Prestativamente, meus testes unitários me disseram que eu não havia quebrado nada durante essa operação. Uma experiência simples e plenamente satisfatória.
Mas por que o código desnecessário acabou lá em primeiro lugar? Por que um programador sentiu a necessidade de escrevê-lo, e como isso passou pelos processos de revisão de código e programação em pares? Quase certamente algo do tipo:
- Era um coisa legal de se escrever e o programador queria escrevê-la. (Dica: Escreva código porque agrega valor, não porque você que agrada);
- Alguém imaginou que isso seria necessário no futuro, então sentiu que seria melhor escrever isso já agora. (Dica: Isso não é. YAGNI. Se você não precisa disso agora, não escreva isso agora);
- Não parecia que o “extra” era tão grande assim, então seria mais fácil implementar isso agora do que ir até o cliente para ver se isso era realmente necessário. (Dica: Sempre leva mais tempo escrever e manter código extra. E o cliente na verdade é bem acessível. Uma pequena porção de código extra vira uma bola de neve com o tempo e se torna uma grande porção de código a ser mantido);
- O programador inventou requisitos extras que não estavam nem documentados nem discutidos para justificar a funcionalidade extra. O requisito era na verdade falso. (Dica: Programadores não definem requisitos, o cliente define);
No que você está trabalhando agora? Isso é realmente necessário?
Pete Goodliffe
97 Things Every Programmer Should Know: Collective Wisdom from the Experts, Kevlin Henney, O'Reilly Media, Fevereiro de 2010, pág. 78

segunda-feira, 4 de novembro de 2013

Aprendizado Contínuo

Vivemos em tempos interessantes. À medida que o desenvolvimento é distribuído pelo mundo, percebemos que há muitas pessoas capazes de fazer o nosso trabalho. É preciso continuar aprendendo para se manter no mercado. Caso contrário, nos tornamos dinossauros, presos no mesmo trabalho, até que, um dia, deixamos de ser necessários ou temos nosso trabalho terceirizado para uma fonte mais barata de recursos.
Sendo assim, o que fazer a respeito? Alguns empregadores são generosos o suficiente para fornecer treinando para ampliar nosso conjunto de habilidades. Outros podem não ser capazes de dispor do tempo ou dinheiro para nos oferecer qualquer formação. Por segurança, precisamos assumir a responsabilidade sobre nossa própria educação.
Aqui está uma lista de dicas de como se manter atualizado. Muitas destas podem ser obtidas de graça na Internet:
• Leia livros, revistas, blogs, feeds do Twitter e sites. Se você quiser se aprofundar em um assunto, considere se juntar a uma lista de discussão ou de notícias.
• Se você realmente quiser ficar imerso em uma tecnologia, coloque a mão na massa – escreva algum código.
• Sempre tente trabalhar com pessoas mais experientes; ser o melhor pode paralizar seu aprendizado. Embora você possa aprender com qualquer pessoa, aprenderá muito mais com alguém mais inteligente ou mais experiente que você. Se não conseguir encontrar um mentor onde trabalha, considere seguir outros caminhos.
• Use mentores virtuais. Procure autores e desenvolvedores na Web que você goste de ler. Assine seus blogs.
• Conheça os frameworks e bibliotecas que você usa. Saber como algo funciona faz com que você saiba como usá-lo melhor. Se forem código aberto, melhor ainda. Use o depurador para percorrer o código para ver o que está acontecendo por baixo dos panos. Você vai começar a ver códigos escritos e revisados por pessoas realmente inteligentes.
• Sempre que você cometer um erro, corrigir um bug, ou tiver um problema, tente entender o que aconteceu. É provável que alguém já tenha passado pelo
mesmo problema e tenha postado na web. o Google é bastante útil nesse momento.
• Uma boa maneira de aprender algo é ensinar ou falar a respeito. Quando as pessoas lhe ouvem e lhe fazem perguntas, você fica mais motivado
a aprender. Tente ensinar algo a seus colegas de trabalho, em um grupo de usuários, ou em uma conferência local.
• Faça parte ou inicie um grupo de estudos ou um grupo de usuários para uma linguagem, disciplina ou tecnologia em que esteja interessado.
• Vá a conferências. E se não puder ir, muitas conferências disponibilizam suas palestras online gratuitamente.
• Vai fazer uma longa viagem? Ouça a um podcast.
• Você costuma executar alguma ferramenta de análise estática de código, ou verifica os warnings na sua IDE? Entenda o que eles estão relatando e por quê.
• Siga os conselhos do “The Pragmatic Programmer”* e aprenda uma nova linguagem a cada ano. Ou ao menos aprenda uma nova tecnologia ou ferramenta. Esses estudos podem lhe dar novas idéias, que você pode usar com suas tecnologias atuais.
• Nem tudo o que estudar tem de ser sobre tecnologia. Entenda o domínio em que está trabalhando, assim você poderá entender melhor os requisitos e
ajudar a resolver problemas de negócio. Aprender a ser mais produtivo, como trabalhar melhor, é outra boa dica.
• Volte para a escola. Faça um novo curso.
Seria bom ter a capacidade que tem o Neo, em Matrix, e simplesmente baixar as informações que precisamos para nossos cérebros. Mas não temos, por isso temos que assumir um compromisso mais longo de aprendizado. Você não precisa gastar cada hora acordado estudando. Um curto período, digamos uma vez por semana, já é melhor que nada. Há (e deve sempre haver) uma vida além do trabalho.
A tecnologia muda rapidamente. Não fique para trás.
Clint Shank
97 Things Every Programmer Should Know: Collective Wisdom from the Experts, Kevlin Henney, O'Reilly Media, Fevereiro de 2010, pág. 36.

segunda-feira, 21 de outubro de 2013

Nada substitui o lucro

Se você é um programador apaixonado pelo que faz, já deve ter sofrido bastante ao ver seus chefes, coordenadores, gerentes, ou patrões ignorando questões técnicas de suma importância, para você, como a manutenção de um código fonte de qualidade por exemplo, para viabilizar o cumprimento de um prazo ou algum requisito contratual. Isso é realmente frustrante e deve lhe fazer reclamar bastante pelos corredores e reconsiderar sempre a possibilidade de mudar de emprego, certo?

Não adianta. Onde quer que vamos estamos sempre sujeitos a isso. Você pode até mesmo encontrar uma empresa bacana de se trabalhar, onde os valores técnicos são tão considerados quanto os valores gerenciais, mas se de repente essa empresa entra em crise, rapidamente começaram as demissões, estagiários brotando do chão, agilidade sendo substituída por métodos tradicionais e a qualidade sendo deixada novamente em segundo plano.

Nada substitui o lucro, e isso é algo que você desenvolvedor, por mais bem intencionado que esteja, por mais profissional que seja, e por mais comprometido que se mantenha com a qualidade do código que escreve, não deve deixar de considerar. E não veja isso como algo ruim, ou errado. De fato nada substitui o lucro. Nenhuma empresa é constituída com o objetivo de criar o melhor software se isso não for apenas um meio de levá-la ao lucro. No fim, o que toda empresa busca é o lucro. 

Como então criar software de qualidade se o que interessa à empresa para a qual você trabalha é apenas lucrar? Se um software porcamente feito for mais lucrativo, é isso que a empresa irá lhe demandar, certo? Nem sempre. Cada organização tem seus valores, e muitas já reconhecem que produto de baixa qualidade uma hora ou outra se reverterá em prejuízo. Mas se esse não for o caso da empresa em que você trabalha, não entre numa guerra por interesses que deveriam ser daqueles que lutam contra. Faça sua parte, coloque qualidade no que você faz, na medida em que for possível, e não seja perfeccionista demais, seja mais pragmático. Aprenda também a aceitar que muitas vezes o nível de qualidade necessário será muito inferior ao melhor que você tem capacidade de entregar. Entenda os objetivos do projeto, compartilhe deles, e dentro desse limite, faça o seu melhor.


segunda-feira, 14 de outubro de 2013

5 segredos para um propósito compartilhado

Um dos pré-requisitos para um trabalho em equipe saudável e eficiente é sem dúvida ter um propósito compartilhado pela equipe. Abaixo listo 5 segredos elencados por Christopher Avery para facilitar esse objetivo.
1. Estabelecer um entendimento compartilhado:
Discuta cada tópico, a missão, os entregáveis, e o produto do trabalho de sua equipe, até que possam articular juntos uma descrição clara e comum de seu propósito.
2. Selecione sua equipe por sua motivação, não por suas qualificações.
Se trabalho em equipe é importante para você, olhe para as qualificações apenas após considerar diretrizes, energia, interesse, motivação e entusiasmo, por que é um desejo compartilhado, não taleto, que gera trabalho em equipe.
3. Aceite, de uma vez por todas, que colegas de equipe não precisam gostar uns dos outros.
Encorajar afinidade para uma tarefa compartilhada, não para um com o outro, é a maneira mais rápida e certa de criar forte coesão no grupo. Ao invés de usar exercícios e técnicas que promovam a amizade, faça todos adotarem um foco comum de modo que cada membro da equipe veja boas razões para trabalhar um com o outro.
4. Pare de tentar motivar.
Por que tentar motivar outras pessoas quanto isso é quase impossível? Ao invés disso, use a motivação que já existe nos membros da equipe perguntando a eles sobre suas necessidades e desejos.
5. Determine se seu time está "construído".
Um time "construído" compartilha direcionamento e energia. Para atingir esse status para seu time, coloque sua fundação bem cedo perguntando a você mesmo uma variedade de importantes questões. Qual é a tarefa da equipe? Qual o benefício para cada membro da equipe ao se comprometer com esse trabalho? Há acordos que permitam à equipe operar mais rapidamente e eficientemente? Os membros da equipe compartilham um objetivo comum que os inspire? Você sabe o que cada membro da equipe agrega ao time?

quarta-feira, 9 de outubro de 2013

Você não pode ganhar todas

Muitas foram as vezes em que me frustrei ao tentar convencer a outros, em especial quando eram superiores hierárquicos, a adotarem uma solução proposta por mim. Na maioria das vezes, diante de um problema, o que eu fazia era elaborar a melhor solução que eu fosse capaz, analisava cada detalhe, não deixava nada de fora, e por fim apresentava aos interessados, muitas vezes nem tão interessados assim, um solução única. Obviamente não era uma solução perfeita, mas já era uma solução bastante questionada e analisada.

O resultado disso, na maioria das vezes, era uma rejeição com base em argumentos mal fundamentados, mal pensados, que não consideravam todo o contexto, que dava importância a fatos irrelevantes e desconsiderava os fatos realmente importantes. Obviamente esse tipo de rejeição é bastante revoltante. No entanto, isso é bastante comum de se ver em situações como essa. Mas há justificativa.

Muitas vezes nós de fato não demos os pesos certos aos fatores considerados, ao menos não segundo a ótica dos demais interessados. Outras vezes não temos todo o domínio do contexto, não conhecemos todos os fatos que serão considerados pelos tomadores de decisão. E outras vezes o tomador de decisão simplesmente erra feio mesmo. O fato é que não podemos ganhar todas. Perderemos muitas. Por motivos legítimos ou por motivos idiotas. Mas essa é a realidade.

Diante disso, já há algum tempo mudei minha tática e ao invés de me focar em obter uma única melhor solução para os problemas que me dedico a resolver, passei a elaborar e propor várias. Na maioria das vezes nenhuma delas trará o melhor resultado de imediato, muitas vezes deixaram a desejar, mas todas elas resultarão em uma realidade melhor que simplesmente continuar convivendo com o problema.

E o que observei com essa mudança de comportamento foi que agora vejo boa parte das minhas propostas sendo colocadas em prática, ainda que muitas delas continuem sendo rejeitadas por uma razão ou outra. Diante de problemas que sei ser capaz de ajudar a resolver, vejo pequenos avanços sendo dados, pequenas ideias sendo implementadas e fazendo a diferença.

Se você é uma pessoa pró-ativa como eu e que não consegue ficar inerte diante de um problema passível de solução, não tente elaborar uma solução fantástica que mudará a realidade para sempre. Pense em pequenos avanços, proponha pequenas mudanças, e proponha muitas delas. Se você for mesmo bom no que faz, aquelas que forem aceitas já poderão tornar a realidade um pouco melhor e lhe darão credibilidade para que seja ouvido novamente no futuro.

sábado, 28 de setembro de 2013

O que é um programador profissional?

A característica mais importante de um programador profissional é responsabilidade pessoal. Programadores profissionais assumem responsabilidade por suas carreiras, suas estimativas, seus compromissos com o cronograma, seus erros, e sua criação. Um programador profissional não passa essa responsabilidade para os outros.

  • Se você é um profissional, então você é responsável por sua carreira. Você é responsável por ler e aprender. Você é responsável por estar atualizado com a industria e a tecnologia. Muitos programadores sentem que é responsabilidade de seus empregadores treiná-los. Desculpe, isso está simplesmente errado. Você acha que médicos se comportam assim? Você acha que advogados se comportam assim? Não, eles se treinam em seu próprio tempo, e com seu próprio dinheiro. Eles passam muitas de suas horas vagas lendo artigos e decisões. Eles se mantém atualizados. E assim devemos fazer. O relacionamento entre você e seu empregador é bem descrito no seu contrato de trabalho. Em resumo: seus empregadores se comprometem a lhe pagar, e você se compromete a fazer um bom trabalho.
  • Profissionais assumem responsabilidade pelo código que escrevem. Eles não liberam código a menos que saibam que funciona. Pense sobre isso por um minuto. Como você pode considerar-se um profissional se pretende liberar código sobre o qual não está seguro? Programadores profissionais esperam que o QA não encontre nada por que eles não liberam seu código até que o tenham testado por completo. Claro, QA sempre encontra algum problema, por que ninguém é perfeito. Mas como profissionais, nossa atitude deve ser não deixar nada para o QA encontrar.
  • Profissionais são jogadores de equipe. Eles assumem responsabilidade pela entrega de todo o time, não apenas pelo seu próprio trabalho. Eles ajudam um ao outro, ensinam um ao outro, aprendem um com o outro, e até mesmo substituem um ao outro quando necessário. Quando um colega de equipe está mal, os outros intervêm, sabendo que um dia serão eles que precisaram de ajuda.
  • Profissionais não toleram grandes listas de bugs. Uma lista de bugs enorme é desleixo. Sistemas com milhares de issues em sistemas de issue-tracking são tragédias do descaso. De fato, em muitos projetos, a simples necessidade de um sistema de issue-tracking já é um sintoma de descaso. Apenas sistemas muito grandes deveriam ter uma lista de bugs tão longa que alguma automação seja necessária para gerenciá-la.
  • Profissionais não fazem bagunça. Eles tem orgulho de sua criação. Eles mantém seu código limpo, bem estruturado, e fácil de ler. Eles seguem padrões acordados e boas práticas. Eles nunca, jamais se apressam. Imagine que você está tendo uma experiência fora do corpo assistindo um médico realizando uma cirurgia em seu coração aberto. Esse médico tem uma deadline (no sentido mais literal). Ele deve finalizar antes que a máquina de circulação artificial danifique demais as suas células vermelhas. Como você quer que ele se comporte? Quer que ele se comporte como um típico desenvolvedor de software, correndo e fazendo bagunça? Quer que ele diga, "Voltarei e corrigirei isso depois"? Ou quer que ele se atenha cuidadosamente a suas disciplinas, tomando seu tempo, confiante que sua abordagem é a melhor abordagem que ele pode razoavelmente tomar. Você quer uma bagunça, ou profissionalismo?
Profissionais são responsáveis. Eles assumem responsabilidade por suas carreiras. Eles assumem responsabilidade por fazerem seu código funcionar apropriadamente. Eles assumem responsabilidade pela qualidade de sua criação. Eles não abandonam seus princípios quando as deadlines os assombram. Na verdade, quando a pressão monta, profissionais se atém ainda mais forte às disciplinas que eles sabem serem corretas.

Robert C. Martin (Uncle Bob)

97 Things Every Programmer Should Know: Collective Wisdom from the Experts, Kevlin Henney, O'Reilly Media, Fevereiro de 2010, pág. 134.

quinta-feira, 25 de julho de 2013

Substituindo SVN por Git

Após alguns anos trabalhando com ferramentas de controle de versão centralizado, como StarTeam e Subversion (SVN), e já tendo feito algumas experiências com Mercurial e Git, usando BitBucket e GitHub como repositórios remotos, decidi iniciar um novo projeto usando Git, no entanto configurando meu próprio repositório remoto.

Numa empreitada como esta, três desafios normalmente precisarão ser superados:

  1. Configurar um repositório remoto e um repositório local e colocá-los para conversar educadamente. Essa tarefa é bem fácil e detalho mais adiante.
  2. Convencer os membros da equipe, acostumados com a comodidade do TortoiseSVN e com um processo de commit mais simples, que Git irá facilitar a vida deles, não o contrário. Isso também não será difícil, e há um modo suave de se fazer essa transição, que apresentarei a seguir.
  3. Convencer a empresa a aceitar os riscos de substituir o já conhecido SVN pelo Git, ainda que apenas num primeiro projeto. Isso será um pouco mais difícil, mas há algumas alternativas, caso isso não possa ser feito.

Para solucionar o primeiro problema, basta seguir este simples script:

  • Instale o Git, ou "GitHub for Windows", no servidor que será usado como repositório remoto.
  • No servidor onde o Git foi instalado, escolha uma pasta para hospedar o repositório remoto.
  • No Git Shell, navegue para a pasta escolhida e inicialize o repositório remoto com "git init --bare".
  • Instale o Git, ou "GitHub for Windows", em uma estação de trabalho.
  • Nessa estação de trabalho, escolha uma pasta para hospedar o repositório local.
  • No Git Shell, navegue para a pasta escolhida e inicialize o repositório local com "git init".
  • Monte dentro desse repositório a estrutura de pastas para o projeto.
  • Adicione a estrutura do projeto no git com "git add .".
  • Comite os arquivos recém adicionados no git, com "git commit -m 'Primeiro Commit'"
  • Configure o repositório remoto a ser usado pela estação de trabalho com "git config remote.origin.url endereço_do_repositorio_remoto".
  • Envie seu primeiro commit para o repositório remoto com "git push origin master"
  • Concluído. A partir agora você já poderá usar seu repositório local enviando os commits para o repositório remoto.
Para convencer a equipe a ao menos experimentar o Git, pode ser necessária muita conversa sobre as diferenças entre sistemas de controle de versão centralizados e distribuídos.

Apresentar a eles o "Git Game"  também pode ser útil.

No caso de equipes acostumadas a não usar ferramentas de linha de comando, esta será provavelmente a principal barreira. Para isso há duas ferramentas, em ambiente Windows, que podem ajudar. A primeira seria o TortoiseGit, semelhante ao provavelmente já conhecido TortoiseSVN, e a segunda o "GitHub for Windows". Quando trabalho com SVN, utilizo o TortoiseSVN, no entanto nas experiências que já fiz usando o GitHub usei o "GitHub for Windows", e esta será a ferramenta que usarei neste novo projeto.

Fica claro, pelo que disse acima, que o "GitHub for Windows" pode ser usado sem problema com repositórios remotos que não sejam repositórios do GitHub. Phil Haack, desenvolvedor da ferramenta, já falou sobre isso, no entanto utilizando um outro provedor de repositórios remotos, no caso o CodePlex. No nosso caso, precisamos usar o "GitHub for Windows" com um repositório remoto disponível na nossa intranet.

A configuração desse repositório remoto já foi feita acima, e sua utilização com o "GitHub for Windows", não poderia ser mais simples. Basta arrastar o repositório local, já configurado para enviar os commits ao nosso repositório remoto, para dentro da janela do "GitHub for Windows" e clicar no botão "Publish". A partir dai o botão "Sync" ficará disponível e poderá ser usado para sincronizar o repositório local com o repositório remoto. O uso dessa ferramenta pode tornar a aceitação do Git pela equipe bem mais simples.

Já para o terceiro desafio, o processo de convencer a empresa a experimentar o Git pode ser o mesmo usado com a equipe, ressaltando principalmente os ganhos de produtividade devidos a melhor performance e a maior segurança caso um desastre aconteça com o repositório remoto.

No entanto, caso a empresa se mantenha relutante, nada impede o uso misto de Git e SVN. Para isso pode ser usada a ferramenta "git svn", ou simplesmente usar um repositório local Git, e ter todas as vantagens que esse repositório local oferece, como a possibilidade de fazer vários commits, reverter alguns deles e "reordená-lo" antes de enviar ao repositório remoto, e um repositório remoto SVN, sendo sincronizado com o repositório local do modo tradicional. Ou seja, você trabalha localmente com Git até que seu código esteja em condições de ser compartilhado com a equipe, e nesse momento faz esse compartilhamento através de SVN, normalmente. Obviamente que com essa última abordagem, assim como com o uso de "git svn", muitas das vantagens do Git serão perdidas, mas ao menos você poderá fazer uso de parte delas.

terça-feira, 18 de junho de 2013

Novas Linguagens e Ambientes de Execução

Ultimamente tem sido crescente a discussão sobre JavaScript e linguagens que "compilam" para JavaScript, como CoffeeScript, TypeScript e outras. Já testei algumas dessas linguagens e todas elas, na minha opinião, são incrivelmente superiores em muitos aspectos ao JavaScript.

Outra questão que vejo ser abordada com frequência recentemente é o uso de Node.js, um framework realmente fantástico, performático, incrivelmente escalável e totalmente assíncrono. Mas que no entanto só entende uma linguagem: JavaScript. Obviamente as linguagens que compilam para JavaScript podem ser usadas ao se trabalhar com Node.js, mas nativamente não são suportadas, ainda.

Outro ambiente de execução que tem sido muito bem avaliado é a máquina virtual Erlang. No entanto, a linguagem Erlang em si é monstruosamente horrorosa e até o momento a melhor tentativa de ser executar uma outra linguagem sobre essa máquina virtual tem sido a Elixir.

No caso da gigante JVM, já faz algum tempo que a comunidade Java começou a admitir que a linguagem Java é uma linguagem verbosa demais, improdutiva e deficiente em muitos aspectos. Como resposta a isso surgiram Scala, Groove, Clojure, Kotlin. Todas essas, linguagens que são executadas sobre a JVM, trazendo muitas vantagens quando comparadas com sua matriarca, Java. 

No mundo .NET, diversas linguagens em um único ambiente de execução já é algo que existe desde sua concepção e com certeza parte da inspiração para a criação de novas linguagens executando sobre a JVM e compilando para JavaScript vieram dai.

A competição, a necessidade e principalmente a insatisfação tem levado ao surgimento de ao menos uma nova linguagem a cada ano, no entanto falta a essas linguagens, e principalmente a suas máquinas virtuais, a visão de que para ganhar mercado, a linguagem não precisa ser apenas universal e performática, precisa também ser produtiva, agradável de se usar.

Imaginem poder executar CoffeeScript diretamente sobre o Node.js ou nos navegadores web, ou código Ruby ou C# sobre a máquina virtual Erlang. Certamente o crescimento e a popularidade desses ambientes cresceria bastante.

Isso me lembra o projeto Singularity, mas isso fica para um próximo post.

segunda-feira, 3 de junho de 2013

C#, Java e os Enums - Parte 3

No último post apresentei a implementação Java do State OnOff, usando os Enums do Java 1.5.

Abaixo você pode ver a minha implementação para o padrão State em C#, seguindo o mesmo modelo visto na versão Java.
public abstract partial class OnOff {
    public partial class OnState : OnOff {
        public override string DisplayText {
            get { return "Ligado"; }
        }

        public override OnOff Switch() {
            return OnOff.Off;
        }
    }

    public partial class OffState : OnOff {
        public override string DisplayText {
            get { return "Desligado"; }
        }

        public override OnOff Switch() {
            return OnOff.On;
        }
    }

    public abstract OnOff Switch();
    public abstract string DisplayText { get; }
}
Fiquei surpreso em ter conseguido um código aparentemente tão enxuto e tão próximo da versão Java, no entanto o código acima não está completo. Note o uso do modificador partial na declaração das classes e o uso dos valores OnOff.On e OnOff.Off nas implementações do método Switch. A implementação desses dois valores, que são propriedades estáticas da classe OnOff, foi feita em um arquivo separado, como outra parte da classe OnOff, juntamente com muitos outros truques necessários para tornar esse State quase tão simples de se usar quanto a versão Java, provendo ainda alguns recursos extras bem interessantes.

É fácil notar também que não basta declarar as classes OnState e OffState. Como os valores que essas classes representam são de fato disponibilizados e utilizados?

Neste repositório do GitHub você encontra a versão completa dessa implementação e poderá ver com detalhes como foi implementada essa segunda parte da classe, disponível no arquivo OnOff.Partials.Boilerplate.cs. Note também que há uma classe base genérica denominada State<TState, TValues>, que utilizei para encapsular mais código boilerplate necessário para a implementação.

Com tanto código boilerplate para um exemplo tão simples, essa implementação poderia ser considerada inviável, ou no mínimo muito pouco prática, ainda que um gerador de código possa ser implementado para reconstruir o arquivo .Boilerplate.cs a cada vez que o State for alterado.

Ainda assim achei as opções disponíveis para a implementação bastante interessantes e gostei do exercício.

Não quero fazer disso aqui um manual completo de como usar minha implementação, quero apenas apresentá-la. Se tiver se interessado navegue pelo código no GitHub e acredito que encontrará algumas construções bem interessantes.

Encerro com alguns exemplos de uso desse State :

Uso dos valores On e Off:
[Test]
public void TestOnOffSwitches() {
    OnOff on = OnOff.On;
    OnOff off = OnOff.Off;

    OnOff onSwitched = on.Switch();
    OnOff offSwitched = off.Switch();

    Assert.AreEqual(on, offSwitched);
    Assert.AreEqual(off, onSwitched);
}
Uso do State em um bloco Switch:
[Test]
public void TestSwitchStatement() {
    OnOff on = OnOff.On;
    switch (on.Value){
        case OnOff.Values.On:
            break;
        default:
            Assert.Fail();
            break;
    }
    OnOff off = OnOff.Off;
    switch (off.Value) {
        case OnOff.Values.Off:
            break;
        default:
            Assert.Fail();
            break;
    }
}
Conversão do valor do State para um byte:
[Test]
public void TestCastValueToByte()
{
    byte onValueAsByte = (byte)OnOff.On.Value;
    OnOff.Values onValue = (OnOff.Values)onValueAsByte;
    Assert.AreEqual(OnOff.On.Value, onValue);
}
Enumeração dos valores disponíveis para o State:
[Test]
public void TestStates()
{
    IEnumerable<OnOff> states = OnOff.States;
    Assert.NotNull(states);

    OnOff statesOn = states.SingleOrDefault(s => s == OnOff.On);
    Assert.AreEqual(statesOn, OnOff.On);

    OnOff statesOff = states.SingleOrDefault(s => s == OnOff.Off);
    Assert.AreEqual(statesOff, OnOff.Off);
}

quarta-feira, 29 de maio de 2013

C#, Java e os Enums - Parte 2

No último post falei sobre os enums do C#, os enums do Java, suas origens e algumas de suas peculiaridades. Neste post apresentarei um State específico para usarmos como estudo de caso e farei referência a dois repositórios do GitHub que implementam o padrão State do zero em cada uma dessas linguagens.

Abaixo está definido o State OnOff, o qual usaremos como estudo de caso.
Esse State representa dois estados concretos: ligado e desligado. Cada estado possui uma propriedade (displayText) e um manipulador (swtch).
A implementação foi feita em Java, utilizando os enums introduzidos no Java 1.5.

public enum OnOff {
    ON {
        @Override
        public String displayText() {
            return "Ligado";
        }

        @Override
        public OnOff swtch() {
            return OnOff.OFF;
        }
    },

    OFF {
        @Override
        public String displayText() {
            return "Desligado";
        }

        @Override
        public OnOff swtch() {
            return OnOff.ON;
        }
    };

    public abstract OnOff swtch();
    public abstract String displayText();
}
Essa implementação satisfaz ao padrão state e possui a representação enxuta que apresentei no post anterior mas para isso foram necessárias alterações significativas na linguagem.

A mesma implementação sem o uso dos enums do Java 1.5 poderia ser feita conforme pode ser visto neste repositório no GitHub. Não apresentarei o código completo aqui para não tornar o texto extenso demais.

Visitando o repositório acima você verá que seriam necessários três arquivos, um para o estado abstrato e um para cada estado concreto. Uma implementação bastante trabalhosa.

Implementar esse mesmo State em C# não seria muito diferente e tal implementação pode ser encontrada neste outro repositório. No entanto nesse caso apenas um arquivo seria suficiente, contendo os estados concretos como tipos internos ao estado abstrato.

Além disso, algumas outras peculiaridades diferem a implementação Java da implementação C#. No próximo post falarei um pouco a respeito dessas peculiaridades e apresentarei alguns recursos da linguagem C# que podem tornar essa implementação bem diferenciada.

sexta-feira, 24 de maio de 2013

C#, Java e os Enums - Parte 1

Enums, ou enumerações, são tipos de dados cujo valor é atribuído a exatamente um elemento de um conjunto finito.

Nos velhos tempos do Delphi, enums eram muito conhecidos e usados, sendo declarados conforme abaixo:
type
   TSuit = (Hearts, Diamonds, Clubs, Spades);

var ASuit : TSuit;

ASuit := Hearts;
Uma variável declarada como sendo do tipo TSuit poderia assumir exclusivamente um dos valores determinados em tal conjunto.

Com o advento da linguagem Java, enums não se mostraram presentes, e foram uma das grandes faltas sentidas por aqueles que migraram para essa nova linguagem.

Posteriormente, com o surgimento da linguagem C#, enums foram trazidos de volta à cena, com características bem semelhantes ao antigo enum do Delphi.
enum Suit { Hearts, Diamonds, Clubs, Spades }

Suit suit = Suit.Hearts;
Junto com a simplicidade herdada do Delphi, vieram suas limitações. Não era possível declarar métodos em um enum, nem para o tipo como um todo e nem para seus valores. Não era possível também sobrescrever seus operadores, um importante recurso apresentado como diferencial pela linguagem C#.

A linguagem Java resistiu à introdução dos enums durante algum tempo, mas quando a trouxe, o fez de forma que enums poderiam tanto ser vistos como um simples conjunto de valores:
enum Suit { HEARTS, DIAMONDS, CLUBS, SPADES }

Suit suit = HEARTS;
Quanto uma implementação completa do padrão de projeto State:
enum ShippingMethod {
 FIRST_CLASS {
  public double shippingCost(double weight, double distance) {
   ...
  }
 },
 FED_EX {
  public double shippingCost(double weight, double distance) {
   ...
  }  
 },
 UPS {
  public double shippingCost(double weight, double distance) {
   ...
  }   
 };
 
 public abstract double shippingCost(double weight, double distance);
};
O que possibilita construções bastante elaboradas, sem dúvida algo muito mais rico e útil que os enums do C#.

Implementar o padrão de projeto State em C# é semelhante a implementá-lo em Java, sem fazer o uso de seus enums, claro. No entanto a construçao é tão elaborada que torna seu uso bastante desestimulante, principalmente diante dos simples enums que a linguagem oferece.

Lançando mão de recursos como generics e classes parciais, realizei um implementação um pouco mais elaborada do padrão State em C#, algo que remete bastante ao formato visto nos enums do Java.

Nos próximos posts apresentarei essa implementação e farei algumas comparações entre ela e a implementação java.

sexta-feira, 3 de maio de 2013

Windows Azure e Nuvens Privadas

Não represento de nenhuma maneira a Microsoft, mas gostaria de falar um pouco aqui sobre Windows Azure e Nuvens Privadas.

As vezes quando converso com colegas sobre Windows Azure, ouço os clássicos questionamentos: Nuvem não é segura. Trabalhamos para governo, não podemos colocar software em servidores de uma empresa estadunidense. Para quê vou aprender algo que quase ninguém quer usar e só funciona na nuvem?

O fato é que aplicações implantadas em nuvem podem trazer vantagens que não podem ser ignoradas apenas com base em questionamentos como esses. A plataforma Windows Azure por exemplo tem crescido a cada dia, e cada vez novos recursos são acrescentados. Para um desenvolvedor, ou empresa, que tem o .NET Framework como sua principal plataforma de desenvolvimento, abdicar dessas vantagens pode significar abrir mão de uma grande vantagem competitiva.

Mas e os questionamentos acima? Como ter as vantagens da computação em nuvem sem ter que hospedar as aplicações num ambiente público e controlado por empresas de fora do país?

A alternativa são as nuvens privadas. No caso das plataformas Microsoft, ambientes baseados em Windows System Center 2012 ou Windows Server 2012, bastante semelhantes ao Windows Azure e que tem o modelo de desenvolver de aplicações bastante semelhante, se não idêntico, em muitos casos.

Na próxima vez que estiver considerando alternativas para aplicações robustas e escaláveis em plataforma Microsoft, não deixe de considerar essa opção.

E como desenvolvedor, não ignore o Windows Azure apenas por que a empresa onde trabalha se mostra resistente a ele. Conheça seus benefícios, as alternativas, e quem sabe até os convença a investir na plataforma.

Mais informações: http://msdn.microsoft.com/en-us/library/windowsazure/jj136831.aspx

domingo, 28 de abril de 2013

Dynamic Method Binding

Já não é novidade que a partir da versão 4 do .NET Framework e da linguagem C# é possível declarar objetos com tipagem dinâmica. Veja este link para mais detalhes.

Em C#, quando um objeto é declarado como dynamico compilador nos permite invocar qualquer método a partir desse objeto, sem checar em tempo de compilação se este método existe ou não. Esse recurso é conhecido como Dynamic Method Binding e apenas em tempo de execução o Framework irá informar, através de uma exceção, que o método invocado não existe.

Exemplo: 
dynamic objetoDesconhecido = ObtemObjetoDesconhecidoDeAlgumLugar();
objetoDesconhecido.MetodoQueNaoExiste(); // Compila mas não executa
Não quero discutir aqui o propósito dessa inclusão à linguagem, quero apenas apresentar uma construção bastante interessante permitida com o uso desse recurso.

Imagine escrever um conjunto de métodos privados sobrecarregados, cada um tratando um tipo específico de entrada e delegar a um único método público a função de decidir qual dos métodos privados deve ser executado.

O exemplo abaixo faz isso:
public class Taxonomy
{
    public string GenusFor(IAnimal animal) {
        if (animal is Lion)
            return GenusFor((Lion)animal);
        if (animal is Zebra)
            return GenusFor((Zebra)animal);
        if (animal is Elephant)
            return GenusFor((Elephant)animal);
        return null;
    }
    private string GenusFor(Lion lion) {
        return "Lion";
    }
    private string GenusFor(Zebra zebra) {
        return "Zebra";
    }
    private string GenusFor(Elephant elephant) {
        return "Elephant";
    }
}
O código acima pode parecer desnecessário se considerarmos que o genero do animal poderia ser uma simples propriedade da interface IAnimal, no entanto serve bem para ilustrar a ideia.

Veja como esse mesmo código ficaria se utilizássemos tipagem dinâmica para deixar que o .NET Framework decida em tempo de execução qual a versão do método GenusFor deve ser executada:
public class Taxonomy
{
    public string GenusFor(IAnimal animal) {
        return ((dynamic)this).GenusFor((dynamic)animal);
    }
    private string GenusFor(Lion lion) {
        return "Lion";
    }
    private string GenusFor(Zebra zebra) {
        return "Zebra";
    }
    private string GenusFor(Elephant elephant) {
        return "Elephant";
    }
}

Usando ((dynamic)this), informamos ao compilador que queremos que o this seja tratado como um tipo dinâmico, assim a existência de um método GenusFor com os parâmetros informados não será verificada em tempo de compilação.

Em tempo de execução, o CLR irá procurar por um método chamado GenusFor no objeto this e encontrará 4 sobrecargas, 1 pública e 3 privadas. Nesses casos, o comportamento do CLR é utilizar o método cujos tipos dos parâmetros esperados sejam o mais próximos possíveis dos parâmetros passados. Isso faz com que o método correto seja invocado, conforme o tipo de IAnimal recebido como parâmetro.

Interessante, não? No entanto lembre-se que recursos poderosos como esse devem ser usados com muita cautela. Brinque um pouco com o código disponível no GitHub e veja o impacto positivo e negativo que esse tipo de abordagem pode trazer.

Confira também o uso desse recurso em um dos exemplos citados no livro Implementing Domain-Driven Design, de Vaugh Vernon.

Abraços.

sexta-feira, 26 de abril de 2013

Não posso resolver o seu problema se você só me informa a sua solução.

Imagino que todo desenvolvedor um pouco mais experiente já tenha passado por esse tipo de situação. Seu 'chefe' lhe apresenta um 'problema' e diz que você precisa resolvê-lo urgente. "Precisamos disso para a próxima versão." Algo do tipo:

"Preciso que crie essa tabela no banco, armazene tais valores e altere tal e tal tela para o usuário fazer o cadastro dos dados."
Nesses cenários o 'chefe' sabe bem o que está fazendo, domina a arquitetura do sistema, sabe que basta inserir a tabela no banco e alterar as telas de cadastro, certo?

Normalmente isso não costuma ser verdade. Questões técnicas quase sempre passam despercebidas no momento da elaboração da solução, em maior ou menor grau, e muitas vezes o desenvolvedor não se sente a vontade, ou se quer tem a opção de criticar a solução apresentada pelo 'chefe'. Ou pior, ele implementa tal solução sem perceber que haverá um impacto indesejado, que causará problemas futuros.

O grande problema nisso é o fato de o 'chefe' ter comunicado uma solução para um problema que ele se quer expôs ao desenvolvedor. Duas cabeças pensam melhor que uma. Quanto melhor a equipe conhecer o verdadeiro problema a ser resolvido, maior será a chance de alguém visualizar com antecedência os impactos negativos de se implantar uma ou outra solução.

Não é a toa que metodologias como o Scrum determinam que decisões técnicas ("soluções") sejam tomadas pelos desenvolvedores, cabendo ao Product Owner apenas a tarefa de especificar e priorizar os requisitos ("problemas").

Infelizmente essa é uma realidade que nem sempre podemos mudar. Mas em todo caso, na próxima vez que você receber uma tarefa ("solução") para implementar, pense se você tem total entendimento de qual é o problema que aquela solução se destina a resolver. Se não tiver esse entendimento, tente esclarecer os objetivos com os demais membros do time, com seu 'chefe', e quem mais puder ajudar. Não se acanhe. Quanto melhor você entender o problema, melhor capacitado estará para implementar uma solução, ainda que não seja você quem a defina.

Seja pragmático.

Referência: http://www.acceptancetesting.info/the-book/

segunda-feira, 15 de abril de 2013

O papel do testador no futuro do desenvolvimento de software.

O papel do testador no futuro do desenvolvimento de software é um tema que já havia planejado tratar aqui. No entanto, Igor Abade já o fez com maestria e portanto não vejo nada mais adequado que referenciar o seu texto aqui, ao invés de reescrever a roda.

O testador de software deve ser visto como mais um membro da equipe de desenvolvedores, tão importante quanto os demais, sejam eles programadores, analistas de negócio ou gerentes. Seu trabalho deve ser integrado ao processo e jamais menosprezado.

Aproveito para indicar o livro Specification By Example, do Gojko Adzic, que apresenta entre outros aspectos um ponto de vista semelhante a respeito do papel do testador de software.

Confiram o texto do Igor Abade.

sexta-feira, 5 de abril de 2013

O que constitui um programador pragmático?

Todo desenvolvedor é único, com pontos fortes e fracos, preferências e aversões. Com o tempo, cada um irá criar seu próprio ambiente. Esse ambiente irá refletir a individualidade do programador, assim como seus hobbies, roupas, corte de cabelo. No entanto, se você for um programador pragmático, você irá compartilhar muitas das seguintes características:

  • Adotar cedo e adaptar-se rápido: Você tem um instinto por tecnologias e técnicas, e ama testar coisas novas. Quando diante de uma novidade, você será capaz de compreende-la rápido e integrá-la com o restante de seu conhecimento. Sua confiança nasce de sua experiência.
  • Ser curioso: Você tende a fazer perguntas. Isso é bacana, como você fez? Você teve problemas com essa biblioteca? O que é esse BeOS do qual ouvi falar? Como links simbólicos são implementados? Você é um acumulador de detalhes, cada um podendo afetar alguma decisão anos a frente.
  • Ser um pensador crítico: Você raramente aceita as coisas como dadas sem primeiro entender os fatos. Quando um colega diz "porque é assim que funciona," ou um fornecedor promete a solução para todos os seus problemas, você sente o cheiro de um desafio.
  • Ser realista: Você tenta entender a real natureza de cada problema que enfrenta. Esse realismo lhe dá uma boa ideia de quão diferente as coisas são, e quanto tempo tomarão. Entender pro si próprio que um processo deve ser difícil ou vai tomar um tempo para ser concluído lhe dá estímulo para continuar.
  • Atirar para todos os lados: Você se esforça para se familiarizar com todo tipo de tecnologia e ambiente, e trabalha para se manter atualizado. Embora seu trabalho atual exija que você seja um especialista, você sempre será capaz de seguir para novas áreas e novos desafios.
Deixamos a mais básica característica para o final. Todo programador pragmático a possui:
  • Cuidar da sua criação.
Nós entendemos que não há sentido em desenvolver software a menos que você se preocupe em fazer isso bem feito.
  • Pense! Sobre seu trabalho.
De modo a ser um programador pragmático, nós o desafiamos a pensar sobre o que faz enquanto faz. Isso não é uma análise única de suas práticas atuais, mas sim uma avaliação contínua de toda decisão tomada, a cada dia, em cada desenvolvimento. Nunca entre no piloto automático. Pense constantemente, criticando seu trabalho em tempo real. O velho lema da IBM, PENSE!, é o mantra do programador pragmático.

Se isso soa difícil pra você, então você está exibindo a característica de ser realista. Isso irá tomar boa parte do seu valioso tempo, tempo que provavelmente já está sob tremenda pressão. A recompensa é um envolvimento mais ativo com o trabalho que você ama, um sentimento de domínio sobre um crescente conjunto de assuntos, e prazer em sentir um crescimento contínuo. Ao longo do tempo, seu investimento será pago à medida em que você e sua equipe se tornam mais eficientes, escrevem códigos mais fáceis de manter, e gastam menos tempo em reuniões.

Andrew Hunt e David Thomas, The Pragmatic Programmer: From journeyman to master, Addison Wesley 1999. Pág. xviii

terça-feira, 2 de abril de 2013

Engenheiro de Software ou Arquiteto de Software?

Há alguns dias me perguntaram a diferença entre um Arquiteto de Software e um Engenheiro de Software.

Após uma definição do papel de cada um, para quem é do ramo, pode até não ser difícil entender, mas para quem não é, pode parecer que são apenas duas formas de descrever a mesma função.

À época pedi um tempo para pensar numa forma simples de explicar, e minha resposta veio através de uma analogia com um jogo bem conhecido, um quebra-cabeças, daqueles de encaixar peças para formar uma imagem.



Nessa analogia dizemos que o Engenheiro de Software é aquele que desenha a imagem a ser montada, o que será visto quando o jogo for concluído, enquanto o Arquiteto de Software é aquele que divide a imagem em peças pequenas e encaixáveis, que juntas formam a imagem desejada.

Um Engenheiro de Software tem como objetivo transformar um conjunto de processos de negócio em software; modelar o domínio do negócio de modo que um software possa ser produzido.

Já o Arquiteto de Software tem como objetivo organizar os artefatos modelados pelo Engenheiro de Software de modo a conseguir o melhor encaixe possível entre eles, dados os objetivos do negócio.

E ai? Será que consegui explicar? Ou complicar ainda mais? rs

quinta-feira, 7 de março de 2013

Pragmatismo e TDD

Anteontem, 05 de Março de 2013, Uncle Bob Martin, um dos principais defensores do desenvolvimento guiado por testes (TDD), fez uma crítica ao modo como são vistas as startups, jovens empresas que se aventuram, ousam, e se arriscam na busca do sucesso através da criação de softwares inovadores. 

Como resposta, alguns leitores tacharam o autor de ser muito dogmático a respeito da prática de TDD. E quem o conhece sabe que há alguma verdade nisso.

No dia seguinte, Uncle Bob publicou uma resposta onde apresentou o seu ponto de vista com relação ao pragmatismo vs dogmatismo no que se refere à prática de TDD, apontando situações em que ele é pragmático e não utiliza a técnica.

Segue abaixo, traduzido para nosso idioma:
Então, quando eu não pratico TDD?
  • Não escrevo testes para getters e setters. Fazer isso normalmente é besteira. Tais getters e setters serão indiretamente testados por testes de outros métodos; então não há porquê testá-los diretamente.
  • Não escrevo testes para variáveis membros. Elas também serão testadas indiretamente.
  • Não escrevo testes para funções de uma só linha ou funções que são obviamente triviais. Novamente, elas serão testadas indiretamente.
  • Não escrevo testes para GUIs. GUIs necessitam ajustes. Você tem que acertá-las no lugar alterando tamanho de fonte aqui, valores RGB ali, uma posição XY aqui, a largura de um campo ali. Fazer isso com testes primeiro é estupidez e perda de tempo.
    • No entanto, me certifico que qualquer processamento relevante no código da GUI seja removido para módulos que sejam testáveis. Não permito que código relevante passe sem testes. Portanto meus códigos de GUI são pouco mais que colas e fios que enfiam os dados no seu lugar na tela (Veja artigos sobre MVVM e Model View Presenter).
  • Em geral não escrevo testes para nenhum código que eu tenha que ajustar no lugar por tentativa e erro. Mas separo o código "ajustável" do código que estou mais certo de que escreverei testes.
    • Ocasionalmente, ajusto código no lugar e então escrevo os testes posteriormente.
    • Também ocasionalmente removo código "ajustado" e o reescrevo com testes primeiro.
    • Qual abordagem escolher é uma questão de julgamento.
  • Alguns meses atrás escrevi um programa inteiro de 100 linhas sem nenhum teste.
    • O programa era para uma única execução. Seria usado uma vez e então descartado. (Era para um efeito especial em um de meus vídeos).
    • O programa era todo relacionado à tela. Em essência, era um aplicativo GUI puro. Sendo assim eu tive que ajustar a coisa toda no lugar.
    • Escrevi em Clojure, e portanto tinha REPL! Pude executar o programa à medida em que crescia a partir da REPL, e pude ver os resultados de cada linha de código que escrevia instantaneamente. Isso não era TDD, isso era EDD (Eye Driven Development).
  • Normalmente não escrevo testes para frameworks, bancos de dados, web-servers, ou outra ferramenta de terceiros que supõe-se funciona. Crio mocks para essas coisas e testo o meu código, não o deles.
    • Claro que as vezes testo códigos de terceiros se:
      • Acredito que tenha defeitos.
      • Os resultados são rápidos e previsíveis o bastante a ponto de criar um mock ser um exagero.
Não é tudo dogma.
Essa lista não está completa. Estou certo de que pensarei em outras vezes que não escrevi testes; mas o espirito dessa lista deve estar evidente. Pragmatismo entra em jogo quando fazemos TDD. Não é apenas dogma.
Entretanto, respeito o dogma; há uma razão para isso. Pragmatismo pode as vezes se sobresair; mas: Não escreverei nenhum código de produção relevante sem fazer todo esforço para usar TDD.
Uncle Bob Martin


terça-feira, 5 de março de 2013

Qual é o seu produto de software?

Para muitas empresas, um produto de software é visto como o resultado de um projeto de software, restrito a escopo, prazo e custo predefinidos.

Pensa-se em produto de software como o conjunto de arquivos a serem instalados nos computadores (servidores, desktops, dispositivos, etc) em que o software será utilizado pelo cliente.

Uma vez que tal conjunto de arquivos satisfaça aos requisitos contratados pelo cliente, considera-se que o produto de software apresenta a qualidade esperada.

Okay, mas onde está o problema nisso?

Requisitos de software, e principalmente requisitos informados pelos clientes, nem sempre especificam critérios de qualidade interna, e quando o fazem, fazem de maneira bem superficial, deixando a interpretação a cargo de quem for desenvolver e avaliar o cumprimento de tais requisitos.

À medida que o produto de software vai sendo utilizado, solicitações de mudança começam a surgir, regras são alteradas, novas funcionalidades são incluídas, e uma nova versão do produto de software é distribuída a cada conjunto de alterações negociadas.

Novamente, uma vez que os requisitos apresentados são satisfeitos, a nova versão é aceita e a vida segue.

Eventualmente, algo para de funcionar. "Quem mexeu nisso?", "Isso funcionava na última vez que usei.", "E quando foi isso?", "Não me lembro, mas funcionava."

E ai começa o inferno para o programador. Gerentes cobrando resultados e o desaparecimento dos bugs, prazos apertados ou inexistentes, cliente reclamando, correções feitas sem a devida atenção, novos defeitos sendo introduzidos. Você já deve ter entendido do que estou falando.

Mas o que uma coisa tem a ver com a outra? Atender aos requisitos do cliente e ter o software aprovado com base no cumprimento de tais requisitos é o que causa esse tipo de problema? Não. Mas atender aos requisitos do cliente não deve ser o único objetivo. E ver o produto de software como um conjunto de arquivos executáveis que satisfaça a esses requisitos também não é o mais adequado. Para se evitar esse tipo de "problema inesperado", é necessário investir na qualidade interna do produto, ou seja, naquilo que o cliente não vê, não avalia, não aprova, e que deve ser considerado o verdadeiro produto de software a ser produzido e mantido: o seu código fonte.

A única forma de se evitar que erros sejam introduzidos no software sempre que funcionalidades forem alteradas ou introduzidas é garantir que o código fonte seja mantido em um alto nível de qualidade, seguindo boas práticas de desenvolvimento, utilizando técnicas e disciplinas desenvolvidas especificamente para tal finalidade.

Falta de qualidade interna normalmente não prejudica as primeiras versões do software, não prejudica a sua aprovação, não bloqueia seu pagamento. E com isso investimentos em qualidade interna acabam sendo ignorados, vistos como algo desnecessário, dispensável.

Entregar o software dentro do escopo, prazo e custo é importante, mas manter a saúde do software ao longo de sua vida também é. Lembre-se, o seu verdadeiro produto de software não são os binários entregues, isso se consegue gerar novamente com uma compilação. O seu verdadeiro produto de software são os seus códigos fonte. Invista na qualidade deles, e o resto virá naturalmente.


segunda-feira, 4 de março de 2013

Apresentação

Boa tarde a todos,

Meu nome é Rafael Romão, sou desenvolvedor de software e fotógrafo de natureza, nas horas vagas.

Trabalho desde os 14 anos, hoje tenho 31, e desenvolvo software desde os 17. Iniciei desenvolvendo pequenos programas para a empresa onde trabalhava, uma distribuidora de materiais de construção. Posteriormente passei a "fazer programas para viver". Desenvolvi softwares para a área notarial (cartórios), laboratórios de imunohematologia, hemocentros e atualmente operação de redes de distribuição de energia elétrica.

Programei em Delphi por cerca de 9 anos e em C# por pelo menos uns 6, me especializando no desenvolvimento de aplicações desktop, particularmente WPF.

Há cerca de 6 anos tenho direcionado minha carreira para arquitetura de software e há cerca de 2 anos para metodologias ágeis.

Sou licenciado em Matemática, pós-graduado em Analise de Sistemas e atualmente curso pós-graduação em Estratégias em Arquitetura de Software.

Há anos acompanho blogs de diversos desenvolvedores, e cresci muito em minha carreira com o que aprendi com eles. Até tentei manter um blog uma vez, mas acabei desanimando.

Hoje a realidade é diferente, e sinto que devo reabrir esse espaço.