segunda-feira, 6 de janeiro de 2014

Git rebase vs merge: diferenças e qual escolher em cada situação

Olá, pessoal!

Muito se discute sobre qual é a melhor maneira de realizar o merge das alterações de uma branch para outra com o Git. Como opções, temos o merge e rebase. De fato, ambas tem sua utilidade, mas quando devemos usar uma solução em detrimento da outra?

Primeiro, vamos explicar o que cada uma delas faz.

Comecemos com um exemplo simples. Imagine que você está fazendo commits na branch master com outras pessoas da sua equipe. Para simplificar o cenário1, vocês estão usando o Github para sincronizar todos os commits da branch master, usando-o como repositório principal.

Sendo assim, digamos que existem os seguintes commits neste momento no master do Github.


M1<---M2<---M3


(Podem notar que representei os commits como um grafo não-cíclico, pois é desta maneira que o Git organiza os commits internamente.)

Após fazer o clone do repositório, você faz mais 2 commits locais (C1 e C2). Na sua máquina, o repositório ficará parecido com o seguinte:


                  .---C1<---C2
                 /
M1<---M2<---M3<-´



Contudo, no Gibhub, outros desenvolvedores já fizeram o push de mais 3 commits no master enquanto você tem ainda seus 2 commits que estão apenas no seu repositório local. No master do Github, teremos então a seguinte situação:


M1<---M2<---M3<---M4<---M5<---M6


Agora chegamos ao ponto crucial: como suas alterações farão merge com as alterações dos outros desenvolvedores?

Com o git merge, o Git vai gerar um novo commit que concentra as alterações que ocorreram frutos deste merge dos commits C1 e C2 com os commits M4, M5 e M6.

Se o usuário fizer um git fetch2, irá receber as alterações e terá no seu repositório a seguinte situação:


                  .---C1<---C2
                 /
M1<---M2<---M3<-´---M4<---M5<---M6


Ao fazer o merge, obterá o commit M7, resultado deste merge:


                  .----C1<---C2<----.
                 /                   \
M1<---M2<---M3<-´---M4<---M5<---M6<---`M7



Como podem notar, o conceito é simples. Agora, o que ocorre quando fazemos um rebase?

O git rebase é uma espécie de merge também, mas usa uma lógica diferente. Ao invés de gerar um novo commit, ele reaplica cada um dos commits da branch local "em cima" do último commit da branch remota.

Ou seja, se temos 2 commits (C1 e C2), eles serão aplicados a partir do commit M7.

Sendo assim, ao realizar o rebase, temos a situação:


                                            .----C1’<---C2’
                                           /
M1<---M2<---M3<-´---M4<---M5<---M6<---M7<-´


Notem que chamei o C1 e C2 agora de C1’ e C2’. Motivo? Após o rebase, estes commits não são os mesmos commits originais. Serão commits totalmente novos, até mesmo com o SHA (identificador único e hash) diferentes.

No entanto, o conteúdo destes commits serão muitos similares aos dos originais. Mas, por qual motivo são similares e não exatamente iguais?

Ao realizar o merge de cada commit local (C1 e C2), ocorrerão merges e até conflitos com os commits remotos (M4, M5 e M6), que podem modificar o conteúdo original dos arquivos envolvidos nos commits C1 e C2. O resultado deste merge e resolução de conflitos, portanto, serão o C1’ e C2’.

Entendendo as diferenças entre as 2 opções, quando usar rebase ou merge?


O rebase é muito útil para não “sujar” o histórico do repositório. Se não fizer o rebase, o git quase sempre3 vai criar um novo commit para o merge dos commits locais com os commits remotos. Este commit extra pode parecer inofensivo, mas torna-se um pequeno estorvo ao visualizar e entender a árvore de commits do git entre as várias branches.

No mais, tirando ocasiões especiais, não recomendo o uso do rebase. Como rebase faz um merge para cada um dos commits da branch, o número de merges necessários é mais alto assim como o de conflitos (se ocorrer), e este trabalho aumenta a cada novo commit na branch. Se ainda não estiver convencido que esta opção é custosa, recomendo a leitura deste comentário no StackOverFlow.

Outro contra do rebase, para algumas pessoas, é que quando vocês faz um rebase você está "mentindo" sobre o que está acontecendo no histórico do seu repositório.

Mas o problema mais grave com o rebase não são estes. O rebase, muitas vezes por falta de conhecimento, é usado em branches remotas nos quais há desenvolvedores trabalhando ativamente nelas. Esta é a receita para o desastre. Porém, se ninguém está trabalhando na branch com commits pendentes, o rebase pode ser feito na branch remota de maneira segura.

Depois destas considerações, na maioria dos casos é recomendado usar o merge: único momento de merge de alterações, único momento para resolver conflitos, não há risco de afetar o trabalho de outras pessoas, preserva a ordem real dos eventos no histórico mas com o efeito colateral de poluí-lo um pouco.

O rebase é interessante apenas para ser aplicado no seu repositório local, onde temos normalmente poucos commits e apenas o próprio usuário realiza commits nela. No restante das situações, avalie com muito cuidado e tenha completo entendimento do que irá fazer.

Esta última dica pode ser estendida, inclusive, para qualquer coisa que pretenda fazer com o Git: o entendimento da árvore de commits dele, ao meu ver, é essencial para entendimento e resolução de problemas. Deixe a decoreba de comandos do git para depois.


1. No git não há o conceito de repositório central. O que acontece é que a equipe acaba elegendo um dos repositórios para concentrar as alterações que podem gerar versões do sistema. Quando utiliza-se o Github, o intuito acaba sendo dele virar este repositório.
2. Ao fazer git pull, o git por trás dos bastidores faz exatamente um git fetch (recebe os novos commits do repositório remoto) e em seguida faz o git merge (merge dos commits remotos com os commits locais).
3. Quando é possível fazer um fast-forward.


terça-feira, 12 de novembro de 2013

Boas práticas ao criar testes com Selenium

Olá, pessoal!

Publiquei mais um texto no blog da Dextra: Boas práticas ao criar testes com Selenium:
Testes automatizados são um assunto de suma importância para um sistema. Uma das ferramentas de grande destaque nesta área, quando estamos falando de sistemas web, é o Selenium
É importante ter o entendimento de como usar da melhor maneira o Selenium, evitando trilhar caminhos perigosos no seu uso e extraindo o que há de melhor dele. Sendo assim, este texto segue com algumas dicas de utilização do Selenium que podem poupar o desenvolvedor de alguns problemas. 
Mas, de quais problemas estamos falando?
 Espero que gostem!

quarta-feira, 30 de outubro de 2013

Textos de minha autoria no blog da Dextra!

Olá, pessoal!

Agora também estou publicando textos no blog da Dextra! Já foram 2 publicados!

O primeiro fala de uma característica (ou bug?) do Hibernate ao realizar mapeamentos @OneToOne em determinadas condições. Importante leitura para todo desenvolvedor que usa Hibernate!

O segundo publicado foi o primeiro de uma série de vários textos com dicas de uso do Hibernate, que vão das simples até as mais avançadas. A maioria deles serão publicados no blog da Dextra, mas deixarei o link de cada um deles aqui no blog também.

Espero que gostem!

quarta-feira, 23 de outubro de 2013

Comparando Enumerators no Java

Olá, pessoal!

Estou aqui novamente, agora para falar dos Enumerators no Java.

Trabalhando com Enumerators, é normal precisarmos comparar um Enumerator com outro Enumerator. Existem 2 formas de fazer isto:
  • usando equals();
  • usando == ;

O uso que vejo mais comum é do equals(). Acredito que seja mais usado pelo fato do equals() ser adotado em qualquer comparação de objeto, pois o uso do == realiza a comparação de endereço na memória (que é o que raramente precisamos na aplicação).

Tão raro mas... faz todo sentido no caso dos Enumerators! Com uma vantagem: previne NullPointerException. Como isto é possível? Vamos aos exemplos.

Vamos pensar em um Enumerator chamado TipoDocumento, que contém as seguintes constantes: CPF e CNPJ:

public enum TipoDocumento {
    CPF, CNPJ;
}

Digamos que uma classe Pessoa tenha um atributo TipoDocumento:

public class Pessoa {
     private TipoDocumento tipoDocumento;
     //getters e setters
}




Dado 2 instâncias de Pessoa (vamos chamá-las de pessoa1 e pessoa2), podemos comparar seus tipos de documento com equals():

if(pessoa1.getTipoDocumento().equals(pessoa2.getTipoDocumento()) {
     //...
}

ou usando == :

if(pessoa1.getTipoDocumento() == pessoa2.getTipoDocumento()) {
     //...
}

Reforçando, as 2 soluções são corretas!

Contudo, a segunda solução tem a vantagem de não estar tão sujeita a um NullPointerException quanto na primeira, pois se o getTipoDocumento da pessoa 1 estiver nulo na primeira solução, ocorrerá um NullPointerException.

A segunda solução também funciona pois o CPF e CNPJ são constantes do TipoDocumento, portanto, terão sempre o mesmo endereço na memória da aplicação. Por esta razão, podemos utilizar o operador == para comparação. Uma última vantagem da segunda abordagem é que ela deixa o código mais legível, o que é sempre bem-vindo.

Enfim, uma dica simples, mas útil =).

sábado, 7 de setembro de 2013

10 livros para desenvolvedores Java experientes

Olá pessoal!

Saiu no blog Java Code Geeks um post sobre os melhores 10 livros para desenvolvedores Java experientes. Vale conferir, mesmo que você não se considere tão experiente assim!

Já tive a oportunidade de conferir o Effective Java e Clean Code! Vocês ouvirão eu falar destes livros aqui algumas vezes. Outro livro que também está na lista do blog, e que será minha próxima leitura, é o Refactoring: Improving the Design of Existing Code, do Martin Fowler.

Tudo a ver com qualidade!

sexta-feira, 30 de agosto de 2013

Mão na massa! Como retornar coleções em métodos?

Depois dos primeiros textos sobre qualidade, que focavam mais no aspecto comportamental com relação ao novato, equipe, chefe, cliente e empresa, vamos agora começar a falar de código!

Como estamos começando o assunto no blog, de início não vou partir para nada mais avançado em termos de qualidade de código, como design patterns. Vou começar a demonstrar pequenas práticas, com foco na linguagem Java, que podem ser facilmente incorporadas no código produzido diariamente pelo desenvolvedor. Em princípio, acredito que serão muito úteis para os desenvolvedores novatos.

Um bom programador deve dominar o conhecimento de práticas simples na construção de código. Livros como Code Complete, Clean Code e Effective Java exploram muito bem estas práticas, só que este conteúdo parece não alcançar grande parte dos desenvolvedores, pois observo que pouco se fala e se exercita qualidade de código no Brasil, ainda mais quando falamos da parte de testes. Obviamente, teste é primordial para conquista de qualidade, mas ele sozinho está longe de resolver seu problema de qualidade. Como diria Steve McConnell, no livro Code Complete:

[...] trying to improve software quality by increasing the amount of testing is like trying to lose weight by weighing yourself more often.

Sendo assim, vamos para uma pequena e simples prática:

  • Ao invés de retornar null para métodos que retornam uma coleção (um List, por exemplo), retornar uma coleção vazia;

Vamos a um exemplo. Imagine que você tem o seguinte método:

public List<String> consultarListaDeAlgumaCoisa() {
       //metodo fazendo alguma logica para retornar alguma coisa
       return null;
}

Evite fazer isto! Se ocorre algo na lógica do seu código onde a lista poderá vir sem elementos, retorne uma lista vazia ao invés de null:


public List<String> consultarListaDeAlgumaCoisa() {
       //metodo fazendo alguma logica para retornar alguma coisa
       return Collections.emptyList();
}

Assim, quando alguém chamar este método, não precisará verificar se a lista está nula ou não. Isto significará menos código a ser escrito, lido e chances menores de NullPointerException na aplicação.

É claro, existem raras ocasiões que o retorno de uma lista nula pode significar algo na lógica da aplicação, diferente de uma coleção vazia. Mas temos que admitir que este tipo de situação é exceção, e não regra.

sábado, 24 de agosto de 2013

A empresa e a qualidade de código - Parte Final

Enfim, usei todo este discurso para chegar ao ponto que gostaria. Um software de qualidade vai além do código-fonte, pois envolve também a satisfação do cliente. E se a empresa busca qualidade em seus softwares, ela vai ter que flexibilizar o processo de desenvolvimento de alguma forma e seus empregados precisam estar dispostos e preparados para isto.

Este tipo de movimento começa desde a fase de proposta do projeto pela equipe comercial da empresa. Os clientes em potencial também precisam entender como é o processo de desenvolvimento de software. Alguns deles, infelizmente, entendem que software é um produto simples que você pergunta quanto é, paga e ele atenderá as suas necessidades ao final. Mas a realidade não é esta. O cliente precisa estar envolvido do começo ao fim do desenvolvimento. Só assim ele terá um produto, na visão dele, com qualidade: regras de negócio contempladas, sem bugs preocupantes, com os ajustes que ele gostaria e com a aprovação do usuário final do produto.

E a empresa pode fazer algo com relação a qualidade do código em si? Claro! Se sua empresa incentiva a busca pela qualidade, já temos meio caminho andado, pois este incentivo abrirá portas para que a equipe faça refatorações no código, use de pair programming e code review, dedique tempo para testes e integração contínua, dentre outras ações voltadas a qualidade que poderiam ser barradas em algumas empresas por pura ignorância.

A empresa pode ir um pouco além e capacitar as pessoas, incentivando membros mais experientes a realizarem palestras e workshops sobre qualidade de software, envolvendo assuntos que iremos tratar aqui no blog.

Bem, chega ao fim esta "série" sobre qualidade. Meu intuito foi demonstrar o envolvimento na qualidade do software pelos diferentes papéis na empresa. Como podem ter notado, gosto de conversar sobre assuntos comportamentais e organizacionais, então creio que vamos ver com alguma frequência este tipo de texto por aqui, mas o foco daqui em diante será código e mais código!