Boas Práticas na Utilização de Controle de Versão

Controle de versões representa a possibilidade de compartilhar arquivos, armazenar todas alterações e resolver os conflitos de edição. A sua utilização é fundamental durante todo o ciclo de desenvolvimento, armazenando artefatos de especificações até o código fonte.

Com o controle de versão é possível que diversos desenvolvedores editem o mesmo documento e todas alterações sejam armazenadas para que o histórico seja mantido e conflitos nas edições solucionados. Outra função muito importante do controle de versões é armazenar todas alterações que um documento sofreu e permitir a recuperação de qualquer uma das versões anteriores.

Embora a utilização de controle de versões possa facilitar a vida dos desenvolvedoes, a falta de organização e padronização pode levar a mais problemas do que soluções.

A seguir serão apresentadas algumas boas práticas referentes a utilização de ferramentas de controle de versão, mais especificamente o Subversion, retiradas de algumas das referências citadas ao final do documento.

Usar um layout de repositório adequado

Há alguns pontos a considerar quando se está configurando um repositório do Subversion. O primeiro deles é se será criado um único repositório para vários projetos, ou um repositório para cada projeto, ou então alguma forma de combinação das duas opções. Há prós e contras em cada uma das opções.

Há benefícios ao se utilizar um único repositório para vários projetos, e o mais óbvio deles é evitar a duplicidade de manutenção:

  • Um único backup a ser feito periodicamente;
  • Um único ponto a exportar e carregar quando há uma nova versão do Subversion que é imcompatível com as anteriores;
  • Facilidade para mover dados entre projetos, e sem perder o histórico destes dados (mover dados entre repositórios diferentes causa a perda do histórico dos dados).

A contrapartida de se utilizar um único repositório para vários projetos é que:

Diferentes projetos podem ter diferentes requisitos de autorização e autenticação;
O Subversion utiliza números de revisão que são globais no repositório; isso faz com que mudanças em projetos distintos aumentem o número de revisão de projetos não relacionados.

O meio termo seria ter alguns repositórios criados, e cada um deles conter projetos relacionados. Desta forma, os projetos relacionados podem compartilhar dados facilmente, e caso novas revisões sejam criadas no repositório, elas estarão relacionadas a projetos que estão, de alguma forma, também relacionados.

Além da organização dos repositórios em si, há ainda a questão da organização dentro dos repositórios. Há várias maneiras de organizar o repositório de um projeto, pois cada divisão existente não é nada mais do que um diretório, e o Subversion não impede a criação de diretórios. Embora exista esta flexibilidade, o projeto Subversion recomenda, oficialmente, a idéia de um “projeto raiz“ que representa o ponto principal de um projeto. Um repositório pode conter um ou mais “projetos raiz“, e o “projeto raiz“ possui exatamente três subdiretórios:

/trunk –  o diretório onde o desenvolvimento principal do projeto ocorre, ou seja, onde sempre está a versão mais recente dos artefatos oficiais;
/branches – o diretório onde pode-se criar ramificações da linha principal de desenvolvimento (trunk);
/tags – o diretório onde se pode armazenar ramificações da linha principal que são criadas, e talvez destruídas, mas nunca modificadas.

Um repositório poderia se parecer com o seguinte:


calc/
----trunk/
----tags/
----branches/
calendar/
----trunk/
----tags/
----branches/
spreadsheet/
----trunk/
----tags/
----branches/

Se há múltiplos projetos em um mesmo repositório, é possível organizá-los em grupos dentro do repositório, colocando projetos com objetivos similares em subdiretórios, como a seguir:

</p>
utils/
----calc/
--------trunk/
--------tags/
--------branches/
----calendar/
--------trunk/
--------tags/
--------branches/
…
office/
----spreadsheet/
--------trunk/
--------tags/
--------branches/

Faça o commit de conjuntos lógicos de mudanças

É importante ficar bem claro que o objetivo principal de um sistema de controle de versão não é o de realizar backup dos dados versionados, mas manter o controle de versões lógicas de modificações feitas nos artefatos.

Sendo assim, realizar um commit apenas ao final do dia, com a idéia de que se está salvando o trabalho feito não é nada bom se este trabalho não estiver logicamente completo. Agindo assim só se está realizando um backup diário do trabalho.

Uma operação de commit deve ter um significado maior: ela deve refletir um trabalho completo para corrigir um erro, a incorporação completa de uma nova funcionalidade ao sistema, ou alguma tarefa particular, mas sempre por completo. Uma operação de commit irá gerar um identificador globalmente reconhecido dentro do repositório, que identifica este trabalho por completo, como se fosse um nome. Este identificador poderá ser utilizado, posteriormente, para identificar uma correção em uma ferramenta de acompanhamento de solicitações,  ou então ser passado como parâmetros a comandos do Subversion quando se deseja desfazer uma mudança, ou então copiá-la de um branch a outro.

Utilizar ferramentas de acompanhamento de solicitações de maneira adequada

Ao utilizar ferramentas de acompanhamento de solicitações (correção de bugs, novas funcionalidades, etc…) faça links cruzados entre o número da solicitação aberta e do commit, ou commits que a atendem.

Uma maneira de fazer isso é incluir o número da solicitação que se está atendendo no comentário do commit, e adicionando o número da revisão, resultante do commit, nos comentários que se faz em uma solicitação, para explicar seu andamento ou seu encerramento.

Acompanhar operações de merge manualmente

Ao realizar um commit depois de uma operação de merge, escreva uma mensagem descritiva explicando o que foi integrado, como por exemplo:

Realização da integração das versões 3490:4120 do /branches/algumbranch para /trunk.

Saber quando criar branches

O debate sobre quando criar branches, ou não, é um detabe antigo, e realmente não há uma palavra final sobre isso, pois cada equipe ou projeto pode assumir uma política diferente. A seguir são descritas as três políticas mais comuns de criação de branches:

Nunca criar branches

Eesta é a política utilizada por projetos que acabaram de nascer, e que ainda não têm código executável.
Os usuários realizam o commit das mudanças diárias diretamente no /trunk.

Eventualmente o /trunk pode parar de funcionar (não compila, não passa em testes unitários), quando alguém realiza o commit de mudanças complexas.

Prós:
* É uma política fácil de seguir.
* Novos desenvolvedores têm uma barreira menor para iniciar.
* Ninguém precisa saber como criar branches e realizar integrações (merge).
Contras:
* Desenvolvimento caótico.
* O código pode se tornar instável a qualquer momento.

Sempre utilizar branches

Esta é a política utilizada por projetos que favorecem um forte gerenciamento e supervisão.
Cada usuário cria e trabalha em um branch privado para cada tarefa de codificação.

Quando a codificação está completa, alguém (o autor original do código, um colega ou o gerente) revisa todas as modificações no branch as integra ao /trunk.

Prós:
* Há a garantia de que o /trunk sempre estará consistente e estável.
* Nenhum desenvolvedor precisa manter em sua máquina, por muito tempo, modificações incompatíveis com o /trunk.
Contras:
* Os desenvolvedores ficam artificialmente isolados uns dos outros, possivelmente criando mais conflitos de integração que o necessário. Requer que os desenvolvedores façam integrações com maior frequência, para diminuir os conflitos.

Criar branches quando necessário

Esta é a prática utilizada pelo projeto Subversion.

Os usuários realizam o commit das mudanças diárias diretamente no /trunk.

Regra #1: o /trunk tem que compilar e passar nos testes de regressão sempre!
Regra #2: uma operação de commit de um conjunto de modificações não pode ser tão grande a ponto de desencorajar a revisão por parte de um colega.
Regra #3: se as regras #1 e #2 entrarem em conflito (exemplo: é impossível realizar uma série de commits sem corromper o /trunk) então o desenvolvedor deve criar um /branch e realizar uma série de commits de pequenos conjuntos de modificações lá. Isso permite que seja feita a revisão por parte de um colega, sem desestabilizar o /trunk.

Pros:
* Tem-se a garantia de que o /trunk está estável todo o tempo.
Contras:
* Adiciona algumas obrigações ao trabalho diário dos usuários: eles devem compilar e testar o código antes de cada commit realizado.

Tenha cuidado ao realizar a atualização de sua área de trabalho

Alguns cuidados, apesar de muito básicos, são ignorados por quem está começando a utilizar um sistema de controle de versão. Vejo constantemente amigos reclamando de estagiários que fazem uma verdadeira bagunça no código versionado, sobrescrevendo atualizações de colegas, criando com isso muitos problemas.

Ao invés de realizar um commit direto em algum artefato, é adequado verificar se há atualizações pendentes para o mesmo. Plugins dificilmente permitem que se realize um commit em um código que possui uma versão mais nova no servidor, mas ainda assim é uma boa prática.

Quando não há conflitos nos arquivos é difícil fazer alguma besteira. Os problemas surgem com eles.

Os plugins das IDEs permitem que se faça a verificação dos conflitos, e a solução deles, passo-a-passo. Ainda assim muitas pessoas (acreditem) ignoram os conflitos e simplesmente forçam um commit de suas atualizações, “matando” as modificações de seus colegas. Acredite, se você não quer ter sua mãe escumungada pelos seus colegas, seja cauteloso. Ao solucionar conflitos, tenha paciência e copie para sua versão as atualizações pertinentes feitas pelos seus colegas. Quando o trecho em conflito refletir uma mesma mudança feita por você e outra pessoa, verifique com a pessoa o que ela acha melhor manter na versão, principalmente se você não conseguir entender a codificação dela.

Só faça o commit quando você tiver certeza de que as mudanças feitas pelos outros colegas de trabalho foram contempladas em sua versão. Se você não conseguir resolver os conflitos, chame uma pessoa mais experiente.

Quando você não tiver uma pessoa disponível para lhe ajudar a resolver os conflitos, é preferível copiar seu código para um arquivo de backup, aceitar todas as atualizações do servidor (sobrescrevendo as suas) e depois realizar novamente as suas alterações sobre a nova versão. Isso porque você certamente entenderá melhor as suas modificações do que a de seus colegas. Esta é uma prática que eu uso às vezes (quando há conflitos demais), e vários colegas me dizem também usar a mesma prática. Pelo menos se garante que nenhum trabalho alheio será estragado.

Quando tiver novas dicas para este post eu o atualizarei.

Está dada a dica!

Referências

http://svn.apache.org/repos/asf/subversion/trunk/doc/user/svn-best-practices.html

http://svnbook.red-bean.com/

http://www.iovene.com/21

3 comentários sobre “Boas Práticas na Utilização de Controle de Versão

  1. Ótimo post. Deveria entrar pra utilidade pública.

    Ultimamente eu tenho visto as coisas mais improváveis acontecerem quando se trata de versionamento. É cada coisa.

    Isto de fazer um backup e dar um override em tudo tem sido uma opção melhor onde trabalho do que ficar toda vez ressuscitando versão.

  2. Só pra contrariar. Os padrões de POG diz justamente o contrário de tudo isso:

    O POGer, por definição, evita o uso soluções padrão e no controle de versão isto não é diferente. Porque utilizar CVS, SVN e Git enquanto você pode utilizar o bom e velho .zip ? Ferramentas mais sofísticadas que Winzip, ou Winrar são para iniciantes. POGer bom tira conflito na hora do merge só no olho.

    A técnica para se manter a ordem dos arquivos é a seguinte. Cria-se o arquivo backup.zip com todo o código fonte do projeto. Durante o tempo de vida do projeto, independente de qual seja este tempo de vida, 2 ou 3 backups são suficientes. Para isto, crie os arquivos backup1.zip e backup_ultimo.zip. Se as coisas ficarem (um pouco) fora de controle crie arquivos backup_ultimo_ultimo.zip e, se necessário vá incrementando com a palavra “ultimo” a cada novo backup. Se as coisas ficarem realmente feias o próximo arquivo deve se chamar backup_ultimo_ultimo_agora_eh_verdade.zip. Não se esqueça de sempre aumentar o nome do arquivo a cada novo backup. Isto facilita encontrar a última versão.

  3. Olá, Luiz!! Muito bom seu post.

    Gostaria de compartilhar um cenário para saber sua opinião e assim melhorar nosso fluxo de trabalho. Atualmente adotamos a ideia de sempre utilizar branches. É criado um branch por tarefa a ser desenvolvida no sprint e as tarefas são desenvolvidas de forma isolada. Contudo, quando o final do sprint chega e alguma das tarefas não tiveram os resultados de testes satisfatórios esse branch acaba entrando no merge para a publicação. Paliativamente, deixamos essa funcionalidade desabilitada no sistema para não gerar erros na aplicação.

    Existe uma maneira de fazer o merge apenas dos branches que foram finalizados corretamente e continuar trabalhando neste mesmo branch que não foi finalizado posteriormente? Qual o impacto disso?

    Muito obrigada!!

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s