Normalmente o processo de atualização e manutenção de schema de banco de dados é realizado de maneira manual através da criação de scripts SQL e/ou utilização de softwares que geram estes scripts a partir da comparação de dois bancos de dados. Apesar deste processo funcionar em muitos projetos existentes, ele exige muita intervenção manual, o que pode acabar atrasando o lançamento de uma nova versão do sistema e prejudicando os processos de Integração Contínua do projeto.
Uma maneira interessante de abordar a manutenção de schema de banco de dados é através da utilização de migrations. Neste post irei explanar um pouco sobre migrations e demonstrar uma abordagem de como utilizá-las em projetos .NET através de um ótimo framework, o FluentMigrator
As migrations surgiram como uma alternativa as alterações de banco de dados através da escrita de SQL, bem como uma maneira de evoluir o schema de banco de dados de uma forma gradativa, maneira essa muito válida quando se adota metodologias ágeis. As migrations normalmente são escritas em uma linguagem de programação mais simples para descrever as mudanças no banco de dados, o que normalmente facilita a escrita e a leitura das mesmas.
Esse conceito de migrations foi popularizado pela comunidade Ruby on Rails que o aplica através das Active Record Migrations, de onde o FluentMigrator buscou inspiração.
A abordagem que irei demonstrar neste post é a que tenho utilizado e recomendado atualmente, mas ela é apenas uma das possíveis maneiras de utilização do FluentMigrator, fique a vontade para implementar da maneira que achar mais interessante para os seus projetos.
Para auxiliar este post eu desenvolvi um pequeno projeto de exemplo que pode ser encontrado em github.com/marcelobalexandre/fluentmigrator-poc.
Como é possível verificar na imagem abaixo eu organizei a estrutura do projeto de exemplo da seguinte maneira:
Minha recomendação é criar um projeto separado que conterá as migrations e o nosso Migrator, que no projeto de exemplo chamei de MyProject.Data.Migrations. Basta criar uma Class Library e instalar o Fluent Migrator e o Fluent Migrator Runner utilizando o NuGet.
Para executarmos as migrations podemos utilizar o Command Line Runner (executável do próprio FluentMigrator), o MSBuild, o Rake, ou outros. Eu particularmente prefiro criar um Runner próprio para o projeto, que neste caso chamei de MyProjectMigrator.
Como é possível observar no código abaixo, o MyProjectMigrator é bastante simples e fácil de usar, basta invocar o método Migrate
passando como parâmetros a ação que deseja realizar (Up ou Down), a string de conexão com o banco de dados e o MigratorEnvironment. O MigratorEnvironment é um Enumerable que criei para que você possa executar ações diferentes nas migrations dependendo do ambiente, muito útil para por exemplo, criar dados fakes (seeds) no ambiente de desenvolvimento e realizar ações exclusivas para os testes unitários. É importante observar que neste exemplo o MyProjectMigrator espera uma string de conexão para o SQL Server 2012, se o seu caso é diferente basta alterá-lo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
|
Recomendo a leitura da documentação oficial sobre a criação de migrations que é bem direta e de fácil entendimento, mas basicamente para criamos uma migration é preciso criar uma nova classe que derive da classe abstrata Migration
e implemente dois métodos, o Up
e o Down
. Além disso você devera adicionar o atributo Migration que aceita valores Int64, esse atributo será utilizado para identificar a migration, é baseado nesse atributo que o FluentMigrator vai saber se já rodou ou não a migration.
A classe abstrata Migration
nos fornece diversos métodos para manipulação de banco de dados, o que nos permite criar tabelas, criar colunas, renomear colunas, inserir dados, executar um script SQL, dentre outras tarefas. Para conhecer mais e entender melhor esses métodos recomendo a leitura da documentação oficial sobre Fluent Interface.
Algo para se ter em mente na hora de criar uma migration é que não devemos agrupar muitas alterações no banco de dados em apenas uma migration, se você precisa criar duas tabelas novas por exemplo, faça isso em duas migrations separadas e não em apenas uma. Outra boa prática é em relação aos nomes das migration, adote um padrão claro para que todo o time entenda de maneira fácil o que determinada migration irá fazer apenas olhando o nome da mesma, se sua migration irá criar uma tabela Clients por exemplo, uma boa opção seria CreateClientsTable. Outra dica é utilizar o padrão YYYYMMDDHHMM no atributo identificador da migration, dessa maneira conseguimos saber rapidamente quando foi desenvolvida e diminuímos o risco de dois desenvolvedores criarem uma migration com o mesmo identificador.
Criei alguns exemplos de como uso e organizo migrations no projeto de exemplo, mas lembre-se que mais importante que usar esse ou aquele padrão, é utilizar algum e fazer com que o time todo o siga.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Como visto anteriormente eu criei um Migrator Runner próprio chamado MyProjectMigrator, ele pode ser utilizado para rodar as migrations nos seus projetos de teste, em uma Console Application (como é o caso do projeto de exemplo) ou em um aplicativo com uma interface gráfica amigável. Como é possível observar no projeto de exemplo a utilização do MyProjectMigrator é bastante simples e pode ser customizada, mas se preferir outras maneiras de rodar as migrations, dê uma conferida na ddocumentação oficial sobre Migration Runners.
Após a execução das migrations, como pode ser verificado nas imagens abaixo, o FluentMigrator irá criar uma tabela VersionInfo
no seu banco de dados onde salvará a identificação das migrations que foram executadas, com a data e hora da execução das mesmas bem como sua descrição, que nada mais é do que o nome das classes.
O que gosto nesta abordagem é a facilidade que qualquer desenvolvedor têm de realizar uma alteração no banco de dados, bem como compreender o que determinada migration, criada por outro desenvolvedor, irá realizar. A maneira que o FluentMigrator controla quais migrations foram executadas pelo número identificador, traz mais segurança na hora de atualizar o banco de dados de um cliente, e tudo isso permite que o processo de Integração Contínua funcione de maneira adequada. Ao invés de termos a necessidade de alguém executar algum software que gere um script de alteração de banco de dados, de maneira manual, a cada nova versão do sistema, basta fazer com que um Gerenciador ou Atualizar de Banco de Dados do projeto seja compilado com a versão correta do projeto que contêm as migrations.
Apesar de minha preferência pela utilização de migrations, é importante que você estude e adote a estratégia mais importante para o seu projeto. Se sua opção for utilizar migrations, não fique preso na maneira que eu utilizo, adapte para que funcione melhor para o seu projeto. Enfim, o que gostaria de passar neste post era isso, por favor deixe seu comentário com críticas, dúvidas ou sugestões.
]]>Não tenho a intenção de me aprofundar muito nos processos relacionados a Integração Contínua neste post, a ideia é documentar algumas dicas que encontrei durante a configuração do Jenkins para o nosso cenário atual. Espero que possa ser útil para mais pessoas, boa leitura. ;)
Basicamente, Integração Contínua ou Continuous Integration (CI) é uma prática de desenvolvimento de software considerada por muitos um dos pilares das metodologias ágeis. Ela permite que a equipe encontre e elimine problemas rapidamente, agilizando os processos de build e delivery, melhorando as respostas à falhas e aumentando a qualidade do software.
De maneira simplificada, um processo de integração continua deve monitorar as mudanças de código no seu repositório de controle de versão e realizar os passos necessários para gerar um executável entregável para o cliente. O que normalmente consiste em compilar a aplicação em modo Release, rodar os testes e gerar o executável.
Apesar de no nosso caso termos implementado um processo de Integração Contínua automatizado em um estágio um pouco mais avançado do ciclo de vida do projeto, é muito recomendável adotar essa prática já no início do desenvolvimento, obtendo assim os benefícios da Integração Contínua o mais rápido possível.
O Jenkins é um servidor de Integração Contínua open source, multiplataforma e que pode ser utilizado para automatizar os processos de build e delivery de muitas linguagens, como C#, Ruby, PHP, Java, dentre outras. Um dos pontos principais do Jenkins são os seus inúmeros plugins, que estendem suas funcionalidades e podem ser desenvolvidos por qualquer um. Além disso, seu processo de instalação e configuração é relativamente simples.
Como citado anteriormente, estou trabalhando em uma aplicação desktop para Windows desenvolvida na plataforma .NET, o processo de integração que precisávamos automatizar consistia basicamente nos seguintes passos:
A instalação do Jenkins no Windows é bem simples, basta baixar o instalador no site oficial, executar o mesmo e seguir o passo a passo. Ao final do processo o Jenkins estará instalado como um serviço e será executado automaticamente na inicialização do Windows. Para verificar se tudo ocorreu corretamente acesse o endereço localhost:8080 e veja se o Jenkins está rodando.
Uma dica importante para evitar dores de cabeça é forçar o serviço do Jenkins a ser executado com um usuário administrador do Windows ao invés de Local System que é o padrão. No meu caso específico o Inno Setup não funcionou corretamente como Local System, para evitar esse tipo de problema execute os seguintes passos:
Além do Jenkins você precisará que todos os softwares necessários para seus processos estejam instalados, como por exemplo o MSBuild, o NUnit, o Inno Setup, etc. Como esses softwares são normalmente executados pelo Jenkins através do Command Prompt do Windows é interessante adicioná-los as variáveis de ambiente para que não seja necessário passar o caminho completo do executável, para isso siga os seguintes passos:
Antes de criarmos nosso primeiro job podemos instalar alguns plugins interessantes, para isso acesse o endereço localhost:8080/pluginManager do Jenkins, esta página é bem simples e intuitiva, para instalar novos plugins vá para a aba Available, para removê-los vá para a aba Installed e para atualizá-los vá para a aba Updates.
Logo abaixo segue uma lista de alguns dos plugins que achei interessante e que utilizamos para esse nosso projeto. Existem centenas de outros plugins, fique a vontade para procurar novos.
Uma dica que considero importante, principalmente se você vai permitir acesso externo ao seu servidor Jenkins, é desativar a funcionalidade de Sign up e controlar as permissões dos usuários, para isso siga os seguintes passos:
Como desativamos a permissão de Sign up você precisará criar as contas dos usuários, para isso acesse o endereço localhost:8080/securityRealm. Um detalhe importante aqui é que o Username deve ser o mesmo que foi adicionado no passo anterior para que as permissões funcionem corretamente.
As configurações do Jenkins podem ser acessadas através do endereço localhost:8080/configure, dentre outras coisas, aqui você pode configurar o servidor SMTP de envio de e-mail, o local onde seu Git está instalado (que normalmente é reconhecido automaticamente), as configurações para o Google Cloud Messaging Notification, etc.
Para criar um novo job no Jenkins, acesse o endereço localhost:8080/newJob, digite um nome para o job, marque a opção Freestyle project e clique em OK. Você será levado para a página de configuração do seu job recém criado, está página é muito importante pois é aqui que devemos configurar tudo que o nosso job irá realizar.
A primeira dica aqui é alterar o workspace onde o job será realizado, por padrão ele fica dentro do diretório onde o Jenkins foi instalado, que normalmente é em C:\Program Files (x86)\Jenkins
, o que pode desencadear alguns erros relacionados a permissão de escrita se algum dos softwares utilizados pelo seu job tentar criar algum arquivo em disco. Para alterar a workspace clique no botão Advanced em Advanced Project Options, marque a opção Use custom workspace e informe o caminho do diretório, por exemplo: C:\CI\my-job\workspace
.
Caso tenha instalado o plugin do BitBucket (e consequentemente o do Git), em Source Code Managment selecione a opção Git e configure seu repositório, sua branch e suas credenciais de acesso. Um dica importante aqui é alterar o timeout de clone e fetch do Git, o padrão é de apenas 10 minutos, o que pode não ser suficiente dependendo do tamanho do seu repositório e da velocidade de sua conexão. Até descobrir isso perdi alguns minutos, para você não passar por isso também, clique no botão Add, escolha a opção Advanced Clone Behaviors e informe um tempo que ache razoável para o timeout.
Com o plugin do BitBucket você pode configurar para que seu job ser iniciado assim que um push no repositório seja feito, para isso marque a opção Build when change is pushed to BitBucket. Para essa opção funcionar você precisará adicionar um Hook de POST apontando para seu-ip-publico:8080/bitbucket-hook nas configurações do seu repositório no BitBucket.
Para configurar o build do seu projeto no MSBuild clique em Add build step e na opção Build a Visual Studio project or solution using MSBuild. Em MSBuild Build File informe o caminho da solution no workspace do job, por exemplo: C:\CI\my-job\workspace\MyProject.sln
. Em Command Line Arguments você deverá informar os parâmetros do seu build, por exemplo: /t:Rebuild /p:Configuration="Release" /p:Platform=x86 /p:DevEnvDir="C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE"
. O parâmetro DevEnvDir
é importante, ele é utilizado para algumas coisas durante o processo de build, como por exemplo na execução do Code Analisys. Clicando em Advanced você também pode marcar a opção If warning set the build to Unstable.
Os testes do NUnit serão executados através do Console, para configurá-lo clique em Add build step e na opção Execute Windows batch command, no campo Command você deverá informar o nome do executável do NUnit (ou o caminho completo caso não o tenha adicionado nas variáveis de ambiente do Windows), o caminho do teste no workspace e o nome do arquivo XML onde será salvo o relatório com os resultados dos testes. Por exemplo: "nunit-console-x86.exe" "MyTest\bin\x86\Release\MyTest.dll" /xml=MyTest.dll-nunit-result.xml
.
Para que o Jenkins exiba os resultados dos testes clique em Add post-build action e na opção Publish NUnit test result report e em Test report XMLs e informe *nunit-result.xml
.
Para gerar os executáveis utilizando o Inno Setup clique novamente em Add build step e na opção Execute Windows batch command, no campo Command informe o nome do executável do Inno Setup (ou o caminho completo caso não o tenha adicionado nas variáveis de ambiente do Windows) e o caminho do seu script no workspace. Por exemplo: "Compil32.exe" /cc "setup.iss"
.
Como comentei no início do post, não pretendia me aprofundar nos conceitos relacionados a Integração Contínua e sim compartilhar algumas dicas que acredito serem úteis. A intenção também não era ser nenhuma receita de bolo, adapte o que for necessário para a realidade do seu projeto, o Jenkins é uma ferramenta muito poderosa, explore seus recursos.
Espero que tenha gostado do post, por favor deixe críticas, dúvidas ou sugestões nos comentários.
]]>Na década de 80 um estudante italiano chamado Francesco Cirillo estava tendo alguns problemas no tempo em que passava com os livros em sua universidade. Mas o que talvez nem mesmo ele imaginasse era que um utensilio doméstico poderia mudar isso. Olhando para um timer de cozinha em formato de tomate ele teve uma ideia e resolveu colocá-la a prova durante seus estudos.
Essa ideia acabou se tornando uma técnica de gerenciamento de tempo bastante conhecida hoje como Pomodoro Technique ou apenas Pomodoro, que consiste em alguns hábitos simples que prometem melhorar sua produtividade.
Um dos hábitos incentivados pelo Pomodoro é o de manter uma lista de tarefas organizada por prioridade. O “truque” da lista de tarefas é que ela libera “processamento” do seu cérebro para melhor realizar as tarefas, ao invés de ficar tentando memorizar tudo que você precisa fazer.
A técnica recomenda que você use as ferramentas mais simples possíveis para manter sua lista, ou seja, papel e caneta. Mas eu particularmente gosto de utilizar um software chamado Wunderlist. Fique a vontade para utilizar a ferramenta que funcione melhor para você.
Outro hábito que a técnica tenta melhorar está relacionado a não nos sobrecarregarmos. Nosso cérebro normalmente perde eficiência ao realizar uma mesma tarefa por longos períodos de tempo. Quem nunca teve um aha moment quando fez uma pausa para ir ao banheiro após um longo período tentando resolver um problema? Seu cérebro só precisava de uma pausa para achar a melhor solução. Para evitar essa sobrecarga o Pomodoro incentiva o gerenciamento do tempo de maneira que você alterne entre trabalhar duro em uma tarefa e relaxar o cérebro antes de exigi-lo novamente.
Você deve utilizar alguma ferramenta para marcar o tempo, novamente a técnica recomenda usar algo simples como um timer de cozinha em formato de tomate por exemplo (ok, não precisa ser em formato de tomate). Mas você pode utilizar algum software para isso, eu tenho utilizado o Moosti em minhas sessões.
Depois de escolhida a ferramenta, pegue uma tarefa da sua lista, marque 25 minutos no seu timer e trabalhe apenas nela (sem interrupções) durante esse tempo, quando chegar ao fim do Pomodoro marque a tarefa como concluída caso a tenha terminado, não se preocupe caso não tenha terminado em apenas um Pomodoro, você pode continuar a tarefa no próximo, mas tente dividir tarefas grandes em tarefas menores na próxima vez que for organizar sua lista.
Depois de trabalhar duro é hora de descansar seu cérebro, marque 5 minutos no timer e relaxe, você está livre para fazer qualquer coisa não relacionada a sua tarefa nesse tempo. Ao completar uma sessão de 4 Pomodoros de 25 minutos você pode tirar uma folga maior de 15 minutos antes de começar uma nova sessão.
Se você não ficou satisfeito apenas com a promessa de melhor produtividade e eficiência que o Pomodoro pode te beneficiar, saiba que como bônus você ainda pode ser tornar mais saudável.
Como? Basta que ao invés de ficar sentando acessando seu Facebook nos intervalos dos seus Pomodoros, você levante um pouco, vá até a cozinha pegar um café, faça algum alongamento, ficar sentando é uma dar piores posições para o ser humano. Acredite, não é da nossa natureza passar o dia sentado em frente a um computador.
Outro beneficio que o Pomodoro pode te trazer é o da satisfação e do bem estar consigo mesmo. A cada tarefa que você consegue finalizar o cérebro libera endorfina, produzindo uma sensação de calma e alegria em nosso corpo.
A técnica possui algumas outras “regras”, se você se interessou pode encontrar mais informações no site e no livro oficial do Pomodoro Technique. Os pontos que abordei são os mais importantes para mim e acredito que você não precise seguir tudo que está no livro oficial ao pé da letra, adapte a técnica para um jeito que vá funcionar para você, se você acha que consegue se manter focado por mais que 25 minutos, aumente o tempo dos seus Pomodoros, não fique preso a regras em um livro.
Experimente, talvez essa técnica não sirva para você, mas o ato de experimentar algo novo pode ser o suficiente para descobrir algo sobre você que te ajude melhorar.
Espero que este post possa ser útil de alguma maneira para vocês, fiquem a vontade para comentar sobre o post e sobre suas experiências com a técnica.
]]>Mas essa ideia voltou a tomar forma ao ler o post On Having Something to Say escrito por Chad Fowler em seu blog. Nele, Chad Fowler comenta sobre seus receios ao ser convidado para falar/escrever sobre suas experiências como programador (como na primeira vez em que foi convidado para palestrar em um evento ou quando foi convidado a escrever um livro sobre Ruby On Rails). Ocasiões em que ele se perguntava, “But, why? What do I have to say? I’m still learning, after all”.
Mesmo com suas dúvidas ele não deixou de aproveitar essas oportunidades para aprender e aperfeiçoar-se. E foi justamente este ponto que me motivou a colocar a ideia do blog em prática e aproveitar essa “oportunidade” para aprender e me aperfeiçoar.
Acredito que o compartilhamento de experiências (mesmo as experiências “erradas”) é uma ferramenta incrível para aprendermos coisas novas e/ou fixarmos o que já aprendemos. E como bônus podemos ajudar outras pessoas que estejam dispostas a aprender com nossas experiências.
Apesar de gostar de ler (principalmente sobre tecnologia e outros assuntos nerds) sempre tive dificuldades para escrever, e essa é uma das skills que espero melhorar através desse blog. Pois como tudo na vida, é a prática que nos leva à excelência.
Ainda não tenho muitas experiências profissionais como programador, mas espero poder compartilhar o que já sei e o que irei aprender através desse blog. Também não sei exatamente quais assuntos irei abordar ou com que frequência conseguirei atualiza-lo, mas gostaria que vocês aproveitassem o conteúdo que irei escrever e me dessem um feedback com suas opiniões.
Obrigado a vocês que leram até aqui, espero que voltem mais vezes! :)
]]>