Jenkis Meetup / Vídeo da palestra de Liquibase

Em primeiro lugar, peço desculpas aos meus leitores (todos os 5 deles) por não ter postado nada nos últimos 5 meses.
Acontece que o FreedomSponsors tem tomado praticamente todo o tempo que eu estou no computador e não estou trabalhando no meu emprego primário :-).

Há 15 dias participei de um evento muito legal, o Jenkins Meetup São Paulo – um encontro pra falar de Jenkins e tecnologias relacionadas.
Lá conheci o Kohsuke Kawaguchi pessoalmente! Pra quem não sabe:

* Kohsuke Kawaguchi: criador do Jenkins (antigamente conhecido como Hudson)
* Jenkins: Um excelente servidor de integração contínua usado por milhares de empresas no mundo todo.

Tomei cerveja com o cara, rá! 🙂

O evento foi muito legal, as palestras foram muito boas. A que eu mais gostei foi a do próprio Kohsuke.
O Kohsuke gravou todas as palestras e está tudo no Youtube (uhuuuu!), veja:
http://t.co/QO9EAmps

Eu tb dei aquela minha palestrinha de liquibase, e agora tem o videozinho dela aqui pra vcs:

Anúncios

Configurando o liquibase num projeto pequeno usando maven

Oba, código pronto!

Olha eu gosto de escrever sobre essas coisas, tecnologia e tal.
Mas faz tempo q não atualizo o blog por falta de tempo.
Peço desculpas à multidão dos meus 5 seguidores por isso 🙂

Anyway, eu preparei um exemplinho aqui de como estruturar um projeto pequeno, usando maven e liquibase.
(É bom quando a gente tá procurando na internet como fazer alguma coisa e tem o código pronto né? Eu gosto!)

Estou assumindo que vc leu os posts anteriores e possui um conhecimento básico de maven (sabe o que é um pom, já fez alguns “clean install” na vida, etc).
Se não, veja:
1) Esse link; e
2) esse outro link; e

O código tá lá no bitbucket. Se vc tem um clientezinho GIT, clone o repositório:

git clone https://bitbucket.org/tonylampada/tonylampada_examples.git

Se não, vai lá e clica em “get source” e baixa o zip.

Lá dentro tem uma pasta LIQUIBASE/exemplo.

A estrutura é mais ou menos assim:

exemplo
-- pom.xml //pom que agrega os 2 sub-módulos
-- exemplo-liquibase
---- liquivai.vat
---- mostrabanco.bat
---- pom.xml
---- src/main/resources
------ master.xml
------ exemplo
-------- 1.0
---------- master.xml
---------- (outros changelogs)
-------- 1.1
---------- master.xml
---------- (outros changelogs)
-- exemplo-web
---- pom.xml
---- src //faz de conta que isso aqui é uma aplicação web

Os xmls estão numa estrutura tal que os patches são executados na ordem cronológica das versões.
A hora que vc for criar sua estrutura, sugiro não inventar muita moda não: segue essa mesma receitinha aí que deve dar certo. E lembre-se de não renomear os arquivos e pastas, conforme mencionado no post anterior.

Esse módulo maven “exemplo-liquibase” é um cara que se vc fizer um “clean install” nele, ele vai simplesmente gerar um jar com esse monte de xml dentro. Não é esse o maior objetivo da vida dele.

Tem outra coisa bem mais interessante que ele sabe fazer: aplicar esse conjunto de changelogs no banco.
Pra isso vc deve rodar o seguinte comando (no diretorio exemplo-liquibase)

mvn clean package liquibase:update 

Ou simplesmente roda esse “liquivai.bat” que tem aí.

O que torna isso possível é a configuração do pom dele, através do uso do liquibase-maven-plugin.
A parte relevante do pom é a seguinte:

  <plugin>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-maven-plugin</artifactId>
    <version>2.0.1</version>
    <configuration>
      <changeLogFile>master.xml</changeLogFile>
      <driver>org.hsqldb.jdbcDriver</driver>
      <url>jdbc:hsqldb:file:banquinho;shutdown=true</url>
      <username>sa</username>
      <password></password>
      <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
      <verbose>true</verbose>
    </configuration>
  </plugin>

O que acontece na hora que vc roda esse comando é o seguinte:

Na hora do “package”, uma das coisas que o maven faz é copiar o conteúdo do src/main/resources pra dentro de target/classes.

O diretório target/classes é que é levado em consideração pelo liquibase-plugin na hora de buscar os arquivos changelog.
Ou seja, essa configuração “master.xml” se refere ao caminho do arquivo relativo a esse diretorio, blz?

Aí, depois do goal “package”, o liquibase:update aplica os patches no banco.
Que banco? Esse banco “banquinho” aí, que vai ser criado em HSQL no seu disco a primeira vez que vc rodar.
Por isso o plugin precisa tb da dependência do driver do hsql.

<dependency>
  <groupId>org.hsqldb</groupId>
  <artifactId>hsqldb</artifactId>
  <version>2.2.8</version>
</dependency>

Pra quem não conhece, o hsql é um banco de dados “pure java” que pode rodar em memoria ou persistido no disco.
É muito bom pra fazer testes em ambiente de desenvolvimento, e há quem use até em produção tb.

Depois de rodar o liquibase:update, tem um outro “mostrabanco.bat” que vc pode rodar – esse cara executa um utilitário do hsql que abre uma tela de administracao do banco. Vc pode executar comandos SQL aí (dá uma olhada aí no bat e no pom pra entender como que eu configurei isso – é bem simples)

Se vc usa outro banco diferente desse (provavelmente é o caso né…), vc já deve saber o que fazer: colocar a dependência pro driver do seu banco e mexer nas configurações de url, driver, usuario e senha.

Mais detalhes sobre a configuração do plugin no maven, lá no site do liquibase.

Ah, já acabou?

É, esse post era pra ser curtinho mesmo.
O que interessa mesmo é o código pronto que tá lá pra vc baixar.
Essa estrutura simples aí deve ser suficiente pra uma aplicação pequena.

Se vc precisa disso pra uma aplicação maiorzinha, com diferentes módulos, que lidam com partes diferentes do banco, e tem diferentes equipes desenvolvendo, então a solução pra vc precisa de uma estrutura mais complexa…
Cada módulo vai ter que ter o seu jarzinho de xmls liquibase, e depois vc vai ter que dar um jeito de juntar tudo e executar no banco. Mas isso já é assunto pro próximo post…

É isso aí.
Bubble sorte!

[ ]’s
O Lâmpada

Não perca nos próximos posts:

  • Estendendo a estrutura anterior para um projeto grande e modular
  • O que fazer quando houver dependências cíclicas entre os módulos no nível do BD
  • Incluindo testes para os patches liquibase no processo de integração contínua
  • Use o liquibase para reduzir drasticamente o tempo de deploy de novas versões em um ambiente de produção

Evoluindo o banco de dados com o liquibase


 

O que é

 

O liquibase é uma ferramenta concebida pra resolver O Problema do Versionamento do Banco de Dados (estou assumindo que vc leu esse post, antes deste!).

A idéia é dar uma melhorada no processo descrito no post anterior, de modo acabar com o “patch hell” desenhado lá.

Então com o liquibase é assim:

  1. Um patch – que na nomenclatura do liquibase se chama changeset – é uma transformação aplicada num banco de dados (pode ser um CREATE TABLE, um ADD COLUMN, um INSERT, um UPDATE, etc)
  2. Changesets são escritos em XML, numa notação específica do liquibase. O liquibase sabe traduzir esse XML pro SQL de um monte de SGDBs (pra casos mais complexos, vc sempre pode usar a tag <sql></sql> do liquibase, pra fazer patches “nativos”)
  3. Um arquivo XML pode conter vários changesets. Um arquivo desses é o que o liquibase chama de changelog.
  4. Changelogs podem fazer “include” de outros changelogs.
  5. Changelogs não devem ser apagados. A medida que o sistema evolui, o numero de changelogs só aumenta.
  6. ChangeSets são identificados por uma chave única formada por três elementos: O path do changelog, o autor do patch, e um id.
  7. Idealmente, changesets devem ser reversíveis. O liquibase sabe fazer rollback para algumas operações (por exemplo, <createTable>). Para outras, é preciso dizer a ele como fazer o rollback.
  8. Ao aplicar um changelog no banco, os changesets são executados na ordem em que aparecem nos changelogs.
  9. O liquibase armazena numa tabela do banco a chave (path + autor + id) dos changesets que ele já rodou, assim, ele só aplica os changesets que ainda não rodaram.
  10. O liquibase armazena também um checksum dos patches executados, e checa na hora de aplicar se o checksum de patches previamente executados mudou. Nesse caso, o comportamento default é interromper a execução com um erro, mas é possível mudar esse comportamento por changeset.
  11. O liquibase pode “desaplicar” um changelog no banco: ou seja, aplicar o rollback dos changesets na ordem inversa.

 

A grande vantagem disso tudo é que fica fácil_rápido atualizar um banco que está com patch faltando. E essa facilidade_rapidez naturalmente faz com que os desenvolvedores atualizem seus ambientes com maior frequência.

É uma linha de comando: rodou = atualizou (e ele mostra a lista dos patches que ele executou. Ou então dá pau, com uma mensagem de erro tipo assim:

“Erro no changeset 2012-03-20_Apagando_coluna_bolinha.xml:joao:3 – Não deu pra apagar a coluna bolinha pq tem uma foreign key apontando pra ela.”

Esse erro aih ia aparecer na maquina do Zé e dos outros desenvolvedores que pegaram esse patch. É fantástico o efeito positivo que tem no processo de desenvolvimento quando as mensagens de erro tem o nome do responsável provável pelo problema. As coisas tendem a ficar muito menos tempo quebradas.

Resumo da ópera: a inclusão dessa ferramenta no processo de desenvolvimento faz com que os problemas de migração de DB sejam detectados e corrigidos muito mais rápido, antes que eles tenham a chance de sair impactando em outras coisas e causando problemas mais sérios. É exatamente aqui que o prejuízo do patch hell é eliminado.

Bom, isso já dá uma bela idéia do que é o liquibase, e o que dá pra fazer com ele né?
Se vc é como eu, já deve estar pensando nas possibilidades de melhoria que esse negócio pode trazer pra sua empresa, ou projeto 🙂

 

Como que usa

 

Vamos logo pra um exemplo:

Isto aqui é um changelog:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
 
	<changeSet id="1" author="bob">
		<createTable tableName="department">
			<column name="id" type="int">
				<constraints primaryKey="true" nullable="false"/>
			</column>
			<column name="name" type="varchar(50)">
				<constraints nullable="false"/>
			</column>
			<column name="active" type="boolean" defaultValueBoolean="true"/>
		</createTable>
	</changeSet>
 
</databaseChangeLog>

No site do liquibase tem o manual que detalha como fazer changelogs, com vários exemplos.
Vou cobrir só alguns casos aqui. Depois vc vai precisar ver o manual pra se aprofundar.

Esse changeset acima faz um <createTable>. Se vc for lá no manual dele, no final da página diz:
“Automatic Rollback Support: YES”.
Fantástico. Ele sabe que o contrário de <createTable> é <dropTable>.

Já o <dropTable> não:
“Automatic Rollback Support: NO”.

Isso significa que, pra um changeset com <dropTable>, vc precisa dizer como faz o rollback dele.
Isto é, se vc estiver interessado na possibilidade de fazer um “liquibase-rollback” um dia.
Se vc ainda está indeciso se precisa disso ou não, aqui vai uma dica: shit happens!

Anyway, um changeset com instrução pra rollback seria assim:

<changeSet id="2" author="ze">
	<dropTable tableName="pessoa"/>
	<rollback>
		<createTable tableName="pessoa">
			<column name="id" type="int">
				<constraints primaryKey="true" nullable="false"/>
			</column>
			<column name="nome" type="varchar(50)">
				<constraints nullable="false"/>
			</column>
		</createTable>
	</rollback>
</changeSet>

Os dois changesets vistos acima tem só uma instrução cada.
Isso não precisa ser o caso. Vc pode colocar mais de uma instrução no mesmo changeset.
Mas eu recomendo que vc não o faça, por dois motivos:

1) Se vc misturar no mesmo changeset, instruções do tipo Autorollback=YES com Autorollback=NO, o “Autorollback do changeset” é NO, portanto vc precisa fazer o <rollback> do changeset inteiro.

2) O liquibase interpreta o changeset como um a transformação atômica (ou seja: tudo ou nada), e ele tenta forçar que isso seja verdade usando uma transação do banco. Só que é o seguinte amigo: não é todo banco que suporta rollback de CREATE TABLE por exemplo. E aí se o seu patch multi-instrução quebrar no meio – vc ferrou com a atomicidade: o liquibase vai considerar o patch não rodado (sendo que ele foi parcialmente rodado), e da próxima vez vai tentar rodar de novo – tudo de novo. Tá vendo a zica que pode dar né?

Vai por mim: Fica com uma instrução por changeset.
Até dá pra abrir algumas exceções, por exemplo, pra changesets com um monte de INSERTs.
Use o bom senso, e tente estabelecer um consenso entre os membros da equipe que vão precisar criar patches.

A próxima coisa útil pra saber é como fazer include de changelogs. Faz assim:

<?xml version="1.0" encoding="UTF-8"?>
 <databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9
  http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">

	<include file="versao-1.0/master.xml"/>
	<include file="versao-2.0/master.xml"/>

</databaseChangeLog>

Normalmente vc vai querer dividir seus patches em pastas, e aí ajuda se vc fizer puder fazer o include usando o caminho relativo dos changelogs. Faça assim:

<?xml version="1.0" encoding="UTF-8"?>
 <databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9
  http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">

	<include file="2012-02-01_CriaTabelaPessoa.xml" relativeToChangelogFile="true"/>
	<include file="2012-02-15_CriaTabelaTelefone.xml" relativeToChangelogFile="true"/>
	<!-- O valor default de relativeToChangelogFile é false! -->

</databaseChangeLog>

Bom, uma vez que vc tem um changelog (que provavelmente vai incluir outros), vc manda o liquibase aplicar o changelog, via linha de comando.

É só baixar do site e descompactar. Tem lá o script “liquibase” pra linux e o “liquibase.bat” pra windows.
Como ele é feito em Java, vc vai precisar de uma JVM instalada, e também do driver jdbc do seu banco.

A sintaxe da linha de comando (pra linux, neste exemplo) é assim:

liquibase --driver=com.mysql.jdbc.Driver \
     --classpath=/path/to/classes \
     --changeLogFile=com/example/db.changelog.xml \
     --url="jdbc:mysql://localhost/example" \
     --username=user \
     --password=asdf \
     migrate

(Substitua esse “/path/to/classes” pelo jar do driver jdbc do seu banco.)

Bom, o que foi dito até aqui deve ser o suficiente pra vc começar a usar o liquibase de maneira útil no seu projeto.

 

Dicas extras

 

É importante dar algumas dicas “extras”, que poderão te economizar algum tempo no futuro.

PreConditions:

Voce pode criar pré-condições para changesets.

Seja pra fazer validações de “sanidade” antes de aplicar um changeset, ou pra criar um changeset condicional.

Tipos de colunas:

Ao especificar tipos de colunas para tabelas, vc pode usar as contantes da classe java.sql.Types, desse jeito:

<createTable tableName="pessoa">
	<column name="id" type="java.sql.Types.INTEGER">
		<constraints primaryKey="true" nullable="false"/>
	</column>
</createTable>

O liquibase vai converter pro tipo equivalente no seu banco. Isso serve pra deixar o código do seus changesets mais independente do SGBD.

Comentários:

Você pode colocar comentários nos changesets. Esses comentários são gravados na tabela databasechangelog (que é onde o liquibase grava quais patches ele já rodou).

<changeSet id="1" author="ze">
	<!-- Esse comentario vai pra tabela databasechangelog -->
	<comment>Criando a tabela pessoa</comment>
	<createTable tableName="pessoa">
		<column name="id" type="int">
			<constraints primaryKey="true" nullable="false"/>
		</column>
		<column name="nome" type="varchar(50)">
			<constraints nullable="false"/>
		</column>
	</createTable>
</changeSet>

Recomendo fortemente que todos os changesets tenham comentários.
Desse modo fica fácil ver a história do banco fazendo um select na tabela databasechangelog.

Patches Nativos:

Às vezes vc precisa criar um patch “nativo”, feito em SQL mesmo.

Pra isso tem o <sql>.

<changeSet author='jsmith' id='1' runAlways='true'>
	<!-- Um treco desse, só em SQL direto -->
	<sql splitStatements="false">
		DECLARE
			cursor c_newviews is
				select table_name
				from user_tables
				where table_name like 'DATABASECHANGELOG%'
				AND table_name||'_VW' not in
				(select view_name from user_views);
		BEGIN
			FOR r_newviews in c_newviews LOOP
				EXECUTE IMMEDIATE
				'CREATE VIEW ' || r_newviews.table_name || '_VW ' ||
				'AS SELECT * FROM ' || r_newviews.table_name;
			END LOOP;
		END;
	</sql>
</changeSet>

Note o spliStatements=false.
O default é true, e faz com que o liquibase faça um split do comando usando ‘;’ (o que obviamente não ia dar muito certo no comando acima.)

Documentação:

O site do liquibase é muito bem documentado. Tudo que vc precisa tá lá, com exemplo de tudo.

Aliás, deu uma melhorada de uns tempos pra cá. Agora tem até vídeo explicando o código fonte! Melzinho na chupeta né, fala sério!

Não mova nem renomeie!

Cuidado com o seguinte fato: O caminho do arquivo xml (changelog) faz parte do id dos chansets que tem dentro dele. Isso significa que se vc renomear ou mover esse arquivo, vc está alterando esse id (que o liquibase usa pra saber se um patch já foi aplicado ou não). Se vc fizer isso com um arquivo que já tenha rodado em algum ambiente, no próximo liquibase-update ele vai tentar ser aplicado de novo, o que provavelmente vai dar errado!

Patch rodado é “imexível”

A partir do momento que vc comita um changelog no controle de versão, vc teoricamente não tem mais controle de onde e quando esse patch vai ser aplicado. Ou seja, 2 minutos depois do seu commit, esse patch já pode ter sido aplicado no BD local de outro desenvolvedor.

Se depois disso vc por acaso descobrir que tem um erro no seu patch, a pior coisa que vc pode fazer é modificar o patch e comitar de novo. Se vc leu com atenção, deve lembrar que o liquibase gera um checksum dos patches já aplicados e dá pau se tiver diferente. Ou seja, vai dar pau no ambiente dos outros: sacanagem né.

Então, lembre-se dessas duas regras:
1. Teste seu patch no seu banco local antes de comitar. Outro nome pra essa regra é “vergonha na cara” :P.
2. Se vc realmente só percebeu um erro no seu patch depois de comitar, crie outro patch pra corrigir.

A não ser que…

Truque sujo: mudar o id do changeset

Então… na verdade a regra acima pode ser “entortada” dependendo do caso, desde que isso não vá atrapalhar ninguém…

Veja esse exemplo – o Zé está criando um patch pra mudar o servidor de SMTP que a aplicação usa pra mandar emails:

<?xml version="1.0" encoding="UTF-8"?>
<changeSet id="3" author="ze">
	<update tableName="system_configuration">
		<column name="cfg_value" value="smtp.googel.com"/>
		<where>cfg_name='smtp.hostname'</where>
	</update>
</changeSet>

svn commit… OK!

Aih o Zé se toca:

PQP, digitei errado essa %$#%@$# desse endereço %#@$#@@!
Ahhh, já sei, vou trocar o id do patch, o liquibase vai pensar que é um patch novo e vai reaplicar.
Num BD que já rodou, vai rodar de novo… blz. Num BD que não rodou ainda, vai funcionar tb… blz!

<?xml version="1.0" encoding="UTF-8"?>
<changeSet id="3_fixed" author="ze">
	<update tableName="system_configuration">
		<column name="cfg_value" value="smtp.google.com"/>
		<where>cfg_name='smtp.hostname'</where>
	</update>
</changeSet>

Entendeu? Mas pensa bem antes de usar esse truque sujo aih blz?

Atalho de teclado é mão na roda:

Padroniza aí no ambiente de desenvolvimento da equipe: CTRL+ALT+L+U (ou qualquer outra combinação da sua escolha) starta um .bat ou .sh que: 1) baixa a versao mais nova dos patches de controle de versão; e 2) Aplica o liquibase. Esse é o tipo de coisa que vira uma “mão na roda” depois:

“Ih, apareceu um erro reclamando que não existe a coluna cor_favorita_id, peraí, CTRL+ALT+L+U… 10 segundos depois … OK, onde é que eu tava mesmo? Ah sim, testando a tela do cadastro do animal de estimação”

Bugs!

Eu mexi bastante com o liquibase na versão 1.9.5 (a versao estável é a 2.0.3, lançada em outubro de 2011).
Essa versão 1.9.5 tinha uns bugzinhos, mas nada que impeça de usar. Essa versão nova eu não olhei ainda.
De qq jeito o código é aberto, vc pode mexer e até contribuir pro projeto (e é fácil, a arquitetura dele é bem feitinha).

 

Agora TE VIRA NEGO!

 

É isso, agora baixa lá, faz uns testinhos, mostra pra galera da sua equipe.
Tente chegar numa estrutura legal de como organizar os changelogs no controle de versão (aliás no site tem dicas até pra isso).
E se o seu projeto é grande e vc JÁ está vivendo num “patch hell”, pelo amor de Deus, pare de ter prejuízo e comece a usar esse negócio!

Se quiser volte aqui e comente sua experiência, ou até mesmo dúvidas. Tamos aí :-

Bubble sorte!
[ ]’s
O Lâmpada

Não perca nos próximos posts:

  • Configurando o liquibase num projeto pequeno usando maven
  • Estendendo a estrutura anterior para um projeto grande e modular
  • O que fazer quando houver dependências cíclicas entre os módulos no nível do BD
  • Incluindo testes para os patches liquibase no processo de integração contínua
  • Use o liquibase para reduzir drasticamente o tempo de deploy de novas versões em um ambiente de produção

O Problema do Versionamento do Banco de Dados

Database

Muitas aplicações que usam banco de dados – tipicamente sistemas web – sofrem de um mal comum:
O nível de rigidez do processo de controle de versão  – na verdade o nome mais adequado é Gerência de Configuração – de banco de dados é muito mais relaxado do que o processo usado pro resto do código da aplicação.

Atualizar a versão de uma aplicação web (em produção) envolve duas coisas: atualizar o código da aplicação, e atualizar o banco de dados.
A atualização do código é fácil. Vc pega a versão 1.0 do código que tá lá, JOGA FORA, e põe o código da versão 2.0 no lugar.
Com o banco vc não pode chegar dropando as tabelas e criando tudo de novo – ou melhor, até pode: a aplicação vai funcionar -, mas o cliente provavelmente vai ficar meio chateado quando descobrir que tudo que ele cadastrou na versão 1.0… já era.

Por isso, o que se faz é ter um “patch”, ou um conjunto de patches, com os comandos SQL que fazem a migração 1.0 -> 2.0 no BD.

Todo mundo tem lá o seu SVN, CVS, GIT ou qq outra coisa onde armazena o código fonte da aplicação.
Por isso mesmo a solução mais comum pra esse problema é armazenar esses patches em arquivos .SQL no controle de versão, em alguma pastinha que todos os desenvolvedores têm acesso.
Quando um desenvolvedor implementa uma funcionalidade que precisa alterar o banco, ele comita o código e também o .SQL do patch necessário.

A questão é que: sem um controle automatizado_fácil_rápido da versão desses patches, alguns problemas começam a surgir.

Exemplo: João e Zé são dois desenvolvedores trabalhando em funcionalidades distintas do mesmo sistema.

  • O João vai lá e comita uma nova funcinalidade + patch.
  • O Zé não sabe o que o João fez, mas ele deu um “svn update” e o código do João veio junto. Mas ele não pegou os patches, que ficam em outra pasta do SVN.
  • Na hora que o Zé vai fazer um teste de um novo código que ele fez, a aplicação não sobe, pq o mapeamento objeto-relacional está quebrado (faltou uma coluna COR_FAVORITA na tabela PESSOA).
  • O Zé já tá esperto: “Humm, isso aí é patch! Deixa eu ver se tem algum arquivo novo lá na pastinha de patches…”
  • O Zé vai lá dar um “svn update” na pasta de patches (coisa q ele não faz há umas 2 semanas) e tem uns 20 patches novos lá.
  • “POUTZ… dá pra tomar uma Kaiser antes?” – Ele pensa – “é, Kaiser não dá, mas um café vai bem…”
  • Depois do cafezinho, o Zé começa a executar os patches na ordem (sim, pq alguém teve a excelente idéia de usar uma nomenclatura cronológica, tipo: 2012_03_14_FuncionalidadeX.sql, ufa!)
  • No sexto patch que ele executou dá erro.
  • “Ah, que se dane! Vou abrir esse 20 patches no Notepad++ e fazer um CTRL+Find ‘COR_FAVORITA’ (search in all open files = CHECK!). Preciso terminar logo minha implementação!” – Ele pensa.
  • O Zé encontra o patch relevante, executa e tenta subir a aplicação de novo.
  • Agora ele consegue continuar desenvolvendo a funcionalidade dele!
  • VITÓRIA!!!

Viiitória??? Será mesmo? Peraí rapaz… vamo analisar umas coisas aqui…

  • O Zé perdeu pelo menos un 20 minutos nessa brincadeira (sem contar a pausa pro café…).
  • Como ele nem rodou todos os patches que tinham lá, isso significa que tem outros bugs “de tocaia”, só esperando pra pular na cara dele numa hora que ele tiver bem aperreado.
  • E como daqui uma semana ele não vai lembrar quais patches ele rodou e quais não, ele vai perder mais tempo ainda.
  • Não é só ele. Outros desenvolvedores também costumam perder tempo resolvendo esse tipo de coisa. Mesmo os caras mais disciplinados (que não ficam mais de 2 dias sem atualizar o banco), ainda gastam 5-10 minutos quando isso acontece.
  • E não é só o João que dá esse tipo de trabalho pros outros. Da próxima vai ser a vez do Zé fazer o papel do vilão.
  • O nome disso é prejuízo. Ninguém mede, ninguém vê (especialmente a direção/gerência), mas ele está lá.
  • E quanto maior a equipe, mais frequente esse tipo de coisa. Num time de 6-10 pessoas desenvolvendo ativamente, isso é suficiente pra causar um verdadeiro “patch hell”.
  • A causa disso está no processo, não nas pessoas.

Apesar desse cenário caótico, esse processo tem poucas causas de falhas. Na verdade ele é quase bom! O problema é que as poucas falhas que tem realmente são capazes de fazer esse estrago mesmo.
Mas é possível resolver esses problemas se o processo conseguir atender aos seguintes requisitos:

  • 1) Existe uma ordem pré-definida e conhecida na qual os patches devem rodar.
  • 2) Para cada instância de banco, existe um meio fácil_rápido de saber se um dado patch foi rodado ou não.
  • 3) Existe um meio fácil_rápido de executar todos os patches que ainda não rodaram num dado banco

E melhor ainda se:

  • 4) Os desenvolvedores atualizam o BD dos seus ambientes de desenvolvimento com mais frequência.
  • 5) Existe um meio fácil_rápido de “DESAPLICAR” os patches até uma determinada data, ou versão. Ou seja, voltar o banco da versão 2.0 -> 1.0.
  • 6) Os patches não são “amarrados” com um SGBD específico.

Se vc se identificou com essa situação, e sente que precisa urgente dar um jeito nisso (como eu senti há uns 2 anos atrás na P2D), saiba que existe uma ferramentinha muito legal chamada liquibase, e que se vc incorporá-la no teu processo, ela te dá de cara os requisitos 1, 2 e 3.

Normalmente, o 4 acaba vindo como consequência de 3.
E, com um pouquinho de trabalho a mais, vc pode garantir também o 5 e o 6.

É isso aí… aguarde e confie 😉

[ ]’s
O Lâmpada

 

Não perca nos próximos posts:

 

É lógico que vc não precisa esperar eu ter tempo de postar de novo. Vai logo dando uma olhada lá no liquibase!

Lista de boas práticas de codificação


A idéia deste blog é que seja em português.

Este post, excepcionalmente, vai em inglês mesmo, simplesmente pq o conteúdo original está assim.

É que o conteúdo abaixo é uma compilação de boas práticas que eu fiz há um tempo, tirado de várias fontes: o livro Code Complete, o StackOverflow, e uma ou outra coisinha da minha experiência mesmo.

É o tipo da coisa que uma empresa do software pode dar um ctrl+chups e estabelecer como “conjunto de diretrizes” para os programadores, ou então usar como checklist pra atividades de revisão de código.

Segue abaixo parte do conteúdo.
Veja o bicho inteiro aqui.

Enjoy the free lunch 🙂

[ ]’s
O Lâmpada

Best practices for software coding.

Good coding practices help avoiding having a bad design.
There are some important characteristics of a good design that we should strive to achieve:

  • Flexibility – It is easy to change code because it is possible to alter behaviour of a single part of the system, without affecting too many other parts of the system.
  • Robustness – When you make a change, no unexpected parts of the system break.
  • Mobility – It is easy to reuse in another application because it can be easily disentangled from the current application.
  • Readability – It is easy to modify code that is easy to understand.

Coding in General

01 – Self documenting code
Improves: readability

“Any fool can write code that a computer can understand.Good programmers write code that humans can understand.”
— Martin Fowler, Refactoring: Improving the Design of Existing Code

Rather than trying to document how you perform a complex algorithm, try to make the algorithm easier to read by introducing more identifiers. Also, always try to make sure that you are using identifiers that makes the purpose very straightforward. This helps in the future in case the algorithm changes but someone forgets to change the documentation.

Here´s an example that should not be followed:

//Please don't do this
for ( i = 1; i <= num; i++ )
{
meetsCriteria[ i ] = True;
}
for ( i = 2; i <= num / 2; i++ )
{
j = i + i;
while ( j <= num )
{
meetsCriteria[ j ] = False;
j = j + i;
}
}
for ( i = 1; i <= num; i++ )
{
if ( meetsCriteria[ i ] )
{
System.out.println ( i + " meets criteria." );
}
}

Now, what do you think this routine does? It’s unnecessarily cryptic. It’s poorly documented not because it lacks comments, but because it lacks good programming style. The variable names are uninformative, and the layout is crude. Indentation also does not reflect the flow of code. Here’s the same code improved.
Just improving the programming style makes its meaning much clearer

//Yes, do it like this
for ( primeCandidate = 1; primeCandidate <= num; primeCandidate++ ) {
	isPrime[ primeCandidate ] = True;
}
for ( int factor = 2; factor < ( num / 2 ); factor++ ) {
	int factorableNumber = factor + factor;
	while ( factorableNumber <= num ) {
		isPrime[ factorableNumber ] = False;
		factorableNumber = factorableNumber + factor;
	}
}
for ( primeCandidate = 1; primeCandidate <= num; primeCandidate++ ) {
	if ( isPrime[ primeCandidate ] ) {
		System.out.println( primeCandidate + " is prime." );
	}
}

The difference between the two code fragments has nothing to with comments. Neither fragment has any. The second one is much more readable, however, and approaches the Holy Grail of legibility: self-documenting code. Such code relies on good programming style to carry the greater part of the documentation burden.
In code well written like this one, comments are often unneccessary.

(etc etc etc)
Clique no link acima (no começo do post) pra ver o documento inteiro.