domingo, 2 de janeiro de 2011

Meu ambiente de desenvolvimento

O pessoal da comunidade Ruby começou com a ideia de cada um descrever seu ambiente de trabalho em seu blog e passar a bola para outros desenvolvedores. O Rafael Rosa me colocou na brincadeira e estou aqui postando o meu ambiente. É uma boa pra ver se eu animo a postar no blog de novo, desde o começo de outubro que eu não posto.

Máquina / Sistema operacional

Atualmente tenho trabalhado em um Mac mini, com sistema operacional Mac OS X (Snow Leopard). Na verdade aonde estou estamos usando estações de pareamento, são 3 Mac minis, cada um com dois teclados, dois mouses e dois monitores, e 5 ou 6 desenvolvedores se revezam para usá-los.

Ainda não estou muito certo se prefiro linux ou mac para trabalhar, sempre usei linux no serviço (geralmente ubuntu) e um macbook em casa. Ultimamente estou com mac no serviço também, a experiência está sendo boa apesar de que por algum estranho motivo eu me sinto mais rápido num linux. Talvez eu goste mais do terminal do linux.

Terminal

Como um desenvolvedor ruby tenho a felicidade de ter um terminal aberto 100 % do meu tempo. Não conseguiria trabalhar com um sistema operacional sem um bom terminal.

Geralmente deixo uma aba aberta para o log do servidor local e outra para uso genérico. Quando estou no Linux costumo usar o Vim dentro do terminal como editor de textos, então uma aba fica reservada pra ele. No mac acabo usando o Mvim, porque no terminal do mac o vim não fica muito legal.

Editor

Como já falei no tópico acima, meu editor de textos é o Vim. Já uso ele há uns 2 ou 3 anos e não o troco por nenhum outro. Lógico que em algumas linguagens uma IDE é totalmente necessária, mas como trabalho com Ruby um bom editor de textos (como o Vim) é mais do que suficiente.

Não uso o vim porque me acho mais rápido com ele ou algo do gênero. Uso ele por afinidade mesmo. Me sinto muito bem com ele, gosto de aprender comandos novos com frequência e acho ele uma ideia genial. E, lógico, depois de um tempo usando me tornei mais rápido nele do que nos outros.

Navegador

Para desenvolver eu sou 100% Firefox, não consigo usar outro. Para uso pessoal eu já passei por vários, opera, firefox, chrome e já faz uns meses que estou testando o Safari, tenho gostado.

Outros

Bom, é isso, dentre outras coisas uso o Git para versionamento, algumas ferramentas que desenvolvo para uso pessoal para facilitar algumas tarefas, como o Donald para solucionar conflitos no Git. Uma boa ferramenta de integração contínua é essencial em uma equipe também. Não escuto música enquanto desenvolvo, mas converso bastante.

Passo a bola pro Ricardo Bernardelli, Renato Nitta e para o Maurício de Amorim.

Abraços.

quinta-feira, 14 de outubro de 2010

Como sua equipe lida com open source?

Nos últimos anos tenho a felicidade de ter trabalhado em equipes que utilizam open source para o desenvolvimento de suas aplicações e percebo entre as equipes algumas diferenças em como elas lidam com isso.

Acredito que a partir do momento que você escolhe usar tecnologias abertas, a forma com que sua equipe lida com esse tipo de desenvolvimento pode ser crucial para a produtividade e o bom andamento do projeto.

Vou listar alguns comportamentos diferentes que você pode ter:

Acho que Open source é como qualquer outro tipo de software (Rendimento baixo)

  • Você, ou sua equipe, acha que open source é como qualquer outra forma de desenvolvimento, a única diferença é que ela na maioria das vezes é gratuita.
  • Você acha que ela está lá para te servir, que basta instalar, rodar, e tudo tem que funcionar como as outras tecnologias fechadas que você está acostumado.
  • Você reclama quando encontra falhas na aplicação, e começa a usar isso de desculpa para o desempenho da equipe.


Pode até ser que, em projetos maiores e mais consolidados (ubuntu, mysql, apache), um comportamento como esse não seja tão prejudicial. Mas pensando em projetos menores, como o próprio Rails ou Ruby Gems, uma postura como essa não lhe trará um bom aproveitamento.

Sou apenas um usuário (Rendimento médio)

  • Você entende melhor como funciona o desenvolvimento open source, mas se posiciona como mero usuário.
  • Se algo não atende suas necessidades você procura por outras soluções ou espera até que o problema seja resolvido.
  • Você acha que é um grande problema quando algo assim acontece, pois sabe que vai impactar o rendimento da equipe.


Esse tipo de comportamento é um pouco melhor do que o anterior. Equipes em que trabalhei que agiam assim com o tempo até acabavam sendo mais críticas para escolher entre as tecnologias (vendo se o projeto ainda é ativo, reputação, etc) e acabavam contornando problemas, mas ainda eram meros usuários e de vez em quando travavam por problemas não tão complexos.

Eu contribuo (Rendimento alto)

  • Sua equipe sabe o que é open source e lida com ele com espírito de contribuição
  • Se algo não atende suas necessidades você:
    • faz um fork do projeto e manda um patch pra equipe que o está desenvolvendo
    • cria um outro projeto open que faça o que você precisa (se os que existem são muito diferentes da sua necessidade)
    • entra em contato com com a equipe que o desenvolve e reporta o seu problema (e se possível os ajuda a solucioná-lo)
  • Fica feliz quando encontra um bug pois ve uma oportunidade de contribuir

Essa situação eu encontrei na equipe que trabalho no momento (Gonow) e foi surpreendente ver o quão mais produtivo é trabalhar assim.

Contribuindo para os projetos você atende suas necessidades de uma forma mais rápida. Você não precisa esperar até que alguém resolva seu problema, o código está lá e equipes open source são extremamente receptivas e gratas por receber patches e novas features (portanto que sigam o foco do projeto e as regras para contribuição).

Contribuir não é tão difícil quanto parece, você vai melhorando aos poucos, vai entendendo melhor como cada projeto funciona, aumenta sua capacidade de ler e entender código dos outros, ve bons exemplos de códigos, entre outros. Só há ganhos.

Você até treina seu inglês!

Lançar projetos open source também é de grande valia. Em um projeto la na Gonow precisávamos de algo que calculasse distâncias entre lugares. Existiam gems que calculavam mas todas faziam isso a partir do raio, nenhuma fazia a partir da rota. Desenvolvemos nossa solução para isso (o go_maps) e o lançamos como open source. Tivemos ganhos logo nos primeiros dias, pois alguns usuários reclamaram de coisas que ele não tinha e percebemos que íamos precisar também. Tivemos esse feedback antes mesmo de lançar nosso projeto aos clientes.

Portanto fica a dica, se você pretende usar open source, saiba que não é apenas um software gratuito, ele é um software colaborativo. Incentive sua equipe a contribuir, se possível dediquem tempo do próprio projeto para isso, uma vez que esses softwares são parte do projeto.

É isso pessoal, nos vemos amanhã no Rails Rumble!

quarta-feira, 15 de setembro de 2010

Ser ágil não é fácil

Trabalhando há algum tempo em equipes ágeis percebo que algumas coisas não são muito bem compreendidas por quem decide formar uma equipe assim, em boa parte dos casos.

O que mais percebo em conversas, ou até em algumas equipes que já trabalhei, é que o desenvolvimento ágil muitas vezes é visto pelo gerente como uma ferramenta para motivar a equipe. O pessoal fica feliz porque está trabalhando com tendências da moda, cola post it aqui e ali e tudo flui bem, tudo continua como era antes, apenas diminuímos a documentação.

Se você pensa assim, saiba que pode está cometendo um grande erro e você pode levar seu projeto a falência.

Ser ágil não é fácil como parece, os princípios por trás do manifesto exigem uma equipe muito bem preparada.

Nossa maior prioridade é satisfazer o cliente, através da entrega adiantada e contínua de software de valor.

O principal objetivo de ser ágil é entregar valor ao cliente, entregar cedo e de forma contínua. Para chegar a esse objetivo você não vai desenvolver em módulos, fazer toda uma parte do sistema e depois ir pra outra e não voltar nessa parte tão cedo. Você vai desenvolver um todo, que funciona, de forma simples, e ficar melhorando todas as partes desse todo incrementalmente e continuamente.

Isso exige muito mais maturidade da equipe do que um projeto não ágil. Para fazer essa refatoração contínua (e criação de novas funcionalidades) nesse sistema em constante mudança você vai precisar de, no mínimo, uma ótima cobertura de testes.

Aceitar mudanças de requisitos, mesmo no fim do desenvolvimento. Processos ágeis se adequam a mudanças, para que o cliente possa tirar vantagens competitivas.

Se entrega de valor já faz com que você tenha que adaptar e evoluir um código constantemente, imagine mudanças de requisitos no meio do projeto.

Aonde eu quero chegar

O que eu estou querendo dizer com isso tudo é que desenvolvimento ágil na minha opinião é a maneira mais eficaz de se desenvolver software, se a decisão for minha eu com certeza vou optar por ela. Mas que fique bem claro que essa é uma forma de desenvolvimento difícil, que exige bastante da equipe.

Chegaria até a dizer que não é viável criar uma equipe ágil apenas com desenvolvedores inexperientes. Digo isso porque geralmente uma equipe iniciante, para atender à demanda flexível de um projeto ágil, acaba criando código difícil de lidar e, em pouco tempo, gera um projeto engessado, difícil de expandir.

Lembre-se:

Processos ágeis promovem um ambiente sustentável. Os patrocinadores, desenvolvedores e usuários, devem ser capazes de manter indefinidamente, passos constantes.

A velocidade de entrega de valor deve ser constante, mesmo após um tempo de projeto.

Se você gerente, deseja formar uma equipe ágil, tome essa decisão de forma consciente, sabendo que há muito trabalho à fazer, que será preciso investir na equipe. Que desenvolvimento ágil exige muito comprometimento por parte de todos e aponta falhas.

Uma vez que você sabe que desenvolvimento ágil não é mais fácil ou mais tranquilo que os demais e mesmo assim deseja adotá-lo, querendo obter o real valor dele, seja bem vindo =)

quarta-feira, 25 de agosto de 2010

Porque eu escrevo os testes antes

Depois de um bom tempo insistindo, e com a ajuda também do pessoal da minha equipe que sempre incentivou (exigiu? =P) essa prática, hoje em dia já se tornou natural para mim escrever o teste antes da implementação.

Minha ideia ao escrever esse post é lhe dizer alguns motivos de porque eu acho que você também deveria fazer isso.

#1 Uma prática de design

Já dizia o pessoal do livro The Rspec Book que BDD não é uma prática de testes e sim uma prática de design. Usamos o BDD para nos guiar sobre o que deve ser criado para satisfazer as necessidades de nosso produto, sem desperdícios. Se você escreve o teste depois você não se beneficia disso.

#2 Não se influenciar pela implementação

Quando você escreve o teste você deve se concentrar no que você espera de sua aplicação, sem pensar em como você vai implementar isso. Se você pensar na implementação antes ou, pior, você implementar antes do teste, você pode criar um teste influenciado pela implementação. O que pode fazer com que sua implementação tenha mais coisa do que o necessário e, ainda, pode fazer com que seu teste saiba demais sobre a implementação e a engesse.

Você deveria poder trocar a implementação sem precisar alterar o teste (refatoração), se seu teste sabe demais sobre a implementação, talvez você não consiga fazer isso e tenha grandes prejuízos (todo mundo tem que ficar alterando os testes a cada mudança).

Escrever o teste antes vai deixar seu código mais enxuto e sem desperdícios, focando apenas no necessário.

#3 Mais fácil se certificar que o teste funciona

Quando você escreve o teste antes você faz com que ele falhe e, após a implementação, ele deve passar. Isso já é uma prova de que seu teste está cobrindo o que você está criando. Se você escreve o teste depois você não tem tanta certeza sobre essa cobertura e, se você não tem o costume de retroceder a parte implementada pra ver se o teste realmente falha, talvez você crie testes que não cubram nada de novo.

#4 Não ter o "dever" de escrever o teste depois

Se você escreveu antes você usufruiu de diversos benefícios e no final já estava com tudo pronto, o teste realmente se mostra parte do desenvolvimento (o que ele é de fato). Se você não o fez, no final de tudo você ainda tem que escrever os testes, o que pode parecer um fardo (tudo está "pronto" e eu tenho que "perder" tempo com o teste).

Concluindo

Se sua equipe escreve testes depois, eu lhe digo que vale muito a pena tentar mudar essa ordem. Diria até que não faz sentido escrever o teste depois.

Não é tão difícil assim obter essa prática, basta um pouco de disciplina e, se precisar de ajuda, dicas sobre como testar determinada coisa, me coloco a disposição pra conversar. Não sou nenhum expert de testes, mas gosto bastante de falar sobre o assunto, me adiciona no gtalk, e-mail, twitter ou o que você preferir que eu tento ajudar com o que eu puder.

Até o próximo post!

sábado, 21 de agosto de 2010

Identificando specs lentos com rspec

Recentemente em um projeto me passaram a tarefa de diminuir o tempo de execução dos testes, para facilitar a integração contínua.

Lembrei então de um comando do rspec que havíamos usado em um projeto anterior que facilita muito a tarefa de identificar quais são os testes críticos.

Não sei quantos conhecem (eu não conhecia há dois projetos atrás), mas um dos formatos do rspec é o "profile", que mostra no final dos testes um top 10 dos mais lentos e o tempo que cada um demorou.

Basta executar assim:

spec spec/ -f profile

ou, em sua versão resumida:

spec spec/ -f o

O resultado é similar ao abaixo:

Profiling enabled.
..................

Top 10 slowest examples:
0.0011200 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with --vim argument should call vim
0.0010110 Donald::MergeTool with $EDITOR variable not seted up with no unmerged files should send an error message
0.0009800 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with no arguments should call vim
0.0009340 Donald::MergeTool with $EDITOR variable as mate with unmerged files with no arguments should call mate
0.0009170 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with --gvim argument should call gvim
0.0008920 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with -g argument should call gvim
0.0008910 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with -t argument should call textmate
0.0008830 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with --mvim argument should call mvim
0.0008820 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with --textmate argument should call textmate
0.0008750 Donald::MergeTool with $EDITOR variable not seted up with unmerged files with -m argument should call mvim

Finished in 0.026412 seconds

18 examples, 0 failures

quinta-feira, 3 de junho de 2010

Como eu uso o GIT

Recentemente vi um post do Yehuda Katz sobre como ele usa o GIT e me empolguei para escrever também.

Antes de mais nada, gostaria de dar os devidos créditos ao @rogerio_augusto porque foi ele quem me ensinou boa parte dos itens que vou escrever hoje.

Pull e Push

Bom, vou começar pelo mais básico, enviar e receber commits do repositório remoto.

Geralmente o pessoal acredita que a fórmula é essa:

  • commito alguma coisa
  • faço pull para ver se tem alguma coisa no remoto
  • faço push para enviar para ele

Isso até que funciona, mas eu prefiro uma outra abordagem que deixa a árvore dos commits mais organizada.

O pull nada mais é do que um fetch (baixa objetos e referências do remoto) mais merge (junta duas ou mais histórias).

Imagine que você baixou tudo que estava no remoto e fez um commit. Enquanto você trabalhava outra pessoa enviou alguma coisa para o remoto que você não tem. Se você fizer um pull, a árvore dos commits ficará assim:

Para não ter que criar um repositório remoto para o exemplo, eu fiz o merge entre dois repositórios locais, mas o resultado é o mesmo.

Com o merge ele mostra que os commits foram feitos em paralelo, mas as vezes essa informação não é tão relevante, e vale mais a pena deixar a árvore organizada. Por isso quando eu vou fazer isso, ao invés de eu dar um pull pro remoto, eu faço um rebase. Por exemplo:


git fetch
git rebase origin/master

Ou, mais fácil de usar

git pull --rebase origin master

Com o rebase o git faz um stash de seus commits que ainda não existem no remoto, baixa os branchs do remoto e recoloca seus commits após o que veio dele. Ficaria assim:


Viram? O git passou o seu commit para depois dos commits que vieram do repositório (apesar deles poderem ter ocorrido antes) e assim manteve a árvore organizada e fácil de entender.

Tem gente que fala que assim você perde a real história dos commits, mas eu discordo. Para casos em que a história é relevante eu uso o pull normal para deixar registrado o que aconteceu (por exemplo, estou criando uma aplicação a partir de uma outra open source e a original foi atualizada, aí sim eu faço um pull para mostrar que são commits de equipes diferentes que andaram em paralelo), mas para os casos de commits dentro da mesma equipe trabalhando numa mesma tarefa, acho que vale mais a árvore se manter legível.

Merge com a opção squash

Geralmente eu uso branchs para separar as histórias do projeto que estão sendo criadas, sendo assim, imagine a seguinte situação:

Você criou um branch a partir do master para fazer sua história. Nesse seu branch você fez 23 commits, que foram totalmente úteis para você se organizar, integrar mais facilmente, etc.

Então você terminou sua história e vai fazer o merge para mandar sua nova funcionalidade para o remoto, então você faz:

git checkout master
git pull origin master (imaginando que você não commitou nada no seu master, senão o mais correto é git pull --rebase)
git checkout seu_branch
git rebase master
git checkout master
git merge seu_branch
git push origin master

Com isso você atualizou seu master, integrou as atualizações do master com o seu branch, fez o merge do seu branch no master e aí sim enviou os seus 23 commits.

Até aí você fez tudo perfeitamente, mas existe uma outra ferramenta que pode te ajudar a se organizar, a opção --squash do merge.
Quando você faz o merge com --squash (git merge seu_branch --squash) o git leva as atualizações pro seu branch mas como se elas não tivessem sido commitadas. Aí você faz um único commit e manda só ele para o master.

Para quem está olhando o master, faz mais sentido ver um commit por funcionalidade, enquanto para quem está desenvolvendo a funcionalidade os 23 commits são úteis. Então agindo assim a gente ajuda todo mundo. Ficaria assim:

git checkout master
git pull origin master
git checkout seu_branch
git rebase master
git checkout master
git merge seu_branch --squash
git add .
git commit -am "Minha funcionalidade"
git push origin master

Opção stash do git merge

Uma outra ferramenta útil é o stash. Imagine que você vai fazer uma atualização pequena no seu projeto e resolveu fazer no branch master mesmo (para aplicações que dividem o projeto em dois remotos - produção e desenvolvimento - eu acho isso totalmente normal). Só que enquanto você está fazendo você percebe que será uma atualização grande e seria mais interessante fazer isso num branch a parte (ou alguém te chamou para fazer outra coisa e você não vai poder continuar aquilo no momento). Então você está com algumas modificações sem commitar mas você não vai poder commitar no master, porque não está concluído, etc.

Para isso você pode usar o stash, você faria assim:

git stash (o git guarda todas as suas modificações em um lugar a parte e seu branch atual passa a ficar como se ninguém tivesse mexido - você pode usar git status para confirmar)
git checkout -b seu_branch_novo
git stash apply (suas modificações voltam a aparecer, pode confirmar com git status)
git commit -am "Primeiro commit da minha funcionalidade"
git stash clear (apaga tudo o que estava no stash)
git checkout master

Com isso você fez como se tivesse começado desde o início em um branch novo.

Você consegue também criar e gerenciar mais de um stash, mas geralmente um só satisfaz minha necessidade.

Opção track do git branch

Vamos a mais uma situação. Seu colega começou uma funcionalidade em um branch a parte e depois de alguns dias pediu para você ajudar ele nessa tarefa.

Você tem que trabalhar nesse mesmo branch dele, então você decide criar o branch dele localmente. Você faz:

git branch branch_dele
git checkout branch_dele (você poderia ter usado git checkout -b branch_dele e fazer tudo de uma vez)
git pull origin branch_dele

CUIDADO!

Quando você fez o primeiro comando você criou um novo branch a partir do que você tem em seu branch atual. E pode ser que você tem coisas nele que não deveriam estar no branch do seu colega, pode ser até conflitante com as tarefas dele e além disso o merge vai bagunçar legal a árvore dele. Se você fizer um rebase pode até piorar mais porque você terá que dar um --force para reescrever a história dele e ele terá que se atualizar. Bom, enfim, essa situação pode gerar resultados não esperados.

Para isso o mais indicado seria:

git fetch
git branch branch_dele --track origin/branch_dele
git checkout branch_dele

Com a opção --track o git vai criar o branch a partir do que tem no brach remoto que você informou, sem olhar para o que tem no seu branch atual. Sendo assim você ficará com o branch idêntico ao do seu colega, o que é o ideal para essa situação.

O git fetch antes é importante porque o --squash vai criar o branch com as informações que estiverem no repositório local, não no remoto. Faça o fetch para atualizar o repositório local antes.

Voltando commits

Outra coisa útil é conseguir voltar atrás em algo que está fazendo. Por exemplo:

Você começou uma tarefa e percebe que está indo na direção errada e deseja começar de novo.

Se você ainda não fez nenhum commit basta usar:

git reset --hard

E você voltará ao estado do último commit efetuado.

Se você já tinha commitado algo, então você pode fazer:

git reset --hard id_do_seu_commit

Exemplo:

git reset --hard dbeeeb0369b5bad0f776fc5ae16c5500bd808156

Com o reset você consegue ir para trás e para frente. Para pegar o id de um commit que está a frente você pode usar:

git reflog

Acabou

Bom, tentei lembrar de algumas situações que são comuns em um projeto. Espero ter ajudado e gostaria da opinião de vocês. Pode ser que existam outras abordagens melhores, então estou aberto para discutir e aprender.

Qualquer dúvida podem entrar em contato comigo (comentário, gtalk, twitter, email etc).

Abraços.

sábado, 1 de maio de 2010

Usando nested attributes com single table inheritance

Esses dias no serviço precisamos usar single table inheritance em alguns models do nosso projeto.

Por exemplo, eu tenho a classe:




E duas outras que herdam dela:



Esses models utilizam a mesma tabela no banco de dados (questions) que contém uma coluna "type" que diz se a questão é uma MultipleChoiceQuestion ou é uma SingleChoiceQuestion.

O problema é que ao usar esses models em um formulário aninhado com accepts_nested_attributes, por mais que eu colocasse um hidden_field dizendo qual era o type da question, o Rails não salvava ele (o campo ficava nulo no banco).

Procurando pela internet encontrei uma solução que achei interessante e resolveu meu problema. Foi uma resposta que o KandadaBoggu deu no fórum stackoverflow.

O atributo type é protegido por padrão em classes que herdam de ActiveRecord::Base, para solucionar o problema podemos sobrescrever o método que define isso na nossa classe question: