Eustáquio Rangel

Desenvolvedor, pai, metalhead, ciclista

Fazendo o seu projeto brotar

Publicado em Developer


Alguns dias atrás, estava com a família esperando para atravessar uma rua com o nosso carro, pois haviam 2 carros subindo, um mais rápido e outro mais devagar. O mais rápido passou voando e o que estava mais devagar (mas que ainda não permitia que eu cruzasse com segurança) quando chegou à alguns metros da esquina, virou para a direita. Sem dar seta. Se tivesse dado seta, com certeza teríamos cruzado antes, mas ficamos lá esperando o lindo e maravilhoso motorista que não está nem aí para os outros não utilizar a seta da sua super-pickup-enorme-bambambam-importada. Não gera nenhuma situação mais extrema, mas é incrível que as pessoas não se prontificam de alguma forma a ter certas gentilezas perante o próximo. Ah, de repente o motorista esqueceu dessas vez, que chato! Concordo que pode ocorrer, mas estatísticamente a incidência disso é muito alta, o que me leva a ter um pouco menos de fé na humanidade levando em conta o quanto alguns se preocupam o mínimo com os outros a ponto de não mover o dedo mindinho na alavanca da seta para esboçar esse tipo de gentileza.

Essa foi uma analogia extrema (em termos de fé na humanidade!) mas que serve para eu introduzir aqui outro tema de gentileza: estruturar nossos projetos de forma com que pessoas novas que tenham que lidar com ele possam o fazer de uma forma rápida e eficiente. Está totalmente relacionado com os artigos anteriores onde eu mencionava o Driver Driven Development e o "code taste" (se você não leu, dê uma lida, na ordem inversa apresentada) mas resolvi fazer um novo artigo primeiro para fazer essa analogia com a gentileza e segundo para realmente fazer um guia de como implementar as coisas descritas nos artigos anteriores para não ter mais desculpas de "não entendi", pois já vai fazer quase 2 anos que eu publiquei o "Driver Driven Development" (é, eu sei, estou relaxado, fico mais no Twitter) e continuo pegando alguns projetos dessa maneira.

Vejam bem, longe de mim argumentar que quem não segue algumas dessas práticas não seja uma pessoa gentil ou coisa do tipo. Só peço para que sejam mais, para ajudar o seu time ou os futuros times do seu projeto. Tudo isso faz parte de comunicação com o seu time e outros desenvolvedores que porventura venham a tocar o seu projeto. A comunicação pelo código faz reduzir a comunicação que depende das pessoas (ainda mais se tiver alguma chance de isso ser centralizado) e aumenta a empatia pelo projeto. A seta é comunicação. E gentileza. Esses procedimentos também o são. Deixar o projeto rodando redondo no seu computador mas não facilitar para que rode em outros é um negócio meio complicado. Vamos lá então.

Clonei o projeto, faço o que?

Santo README

Ok, primeira coisa: um README. Em qualquer formato! Texto, Markdown, santo Deus, manda um PDF ou um .DOC (vixi) dentro do repositório, mas que contenha instruções para quem está chegando agora. Geralmente, quando armazenamos nossos códigos em repositórios Git, o que deixou de ser uma novidade já faz algum tempo (lembrando que tenho que escrever um artigo sobre como várias coisas que alguns anos atrás quando apresentadas eram vistas como a salvação da lavoura mas hoje já estão mais comuns - ainda bem!), criamos um arquivo MarkDown, por exemplo, README.md, que vai ficar bem legal quando visualizarmos no navegador, além de permitir a conversão para vários outros formatos. Descreva o seu projeto, o que ele faz em algumas poucas linhas, um cabeçalho legalzinho, não aquele tipo de coisa sem graça e sem sal como

Projeto da livraria

Já vi algumas pessoas que estavam em um dia ruim dando risada com alguma mensagem engraçada em um README. Não que precise ter isso, mas vocês entenderam a idéia.

Banco de dados

Se o seu projeto não usa banco de dados, pule para a próxima. Como a maioria geralmente tem alguma coisa relacionada, é bem interessante você especificar que banco que está sendo utilizado e como configurar e se conectar rapidamente com ele sem precisar fazer uma novena rezando várias Ave Marias, Pai Nossos e acendendo velas. Vamos pegar aqui de exemplo o Ruby on Rails, mas são instruções que podem ser adaptadas para vários outros frameworks.

No Rails, nós temos um arquivo que armazena as configurações dos nossos bancos de dados (sim, temos 3 ambientes por lá), onde especificamos também quais são os bancos de dados utilizados em cada um deles. Em uma aplicação Rails gerada do zero, temos esse tipo de conteúdo:

default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3

Vejam que ali está especificado o SQLite como banco de dados nos 3 ambientes. Em desenvolvimento e produção, muitas vezes dá para deixar dessa forma, pois procuramos ao máximo não depender muito de recursos de um banco específico (certo?), pois temos o nosso ORM que nos permite abstrair esse tipo de coisa, dessa forma até nos policiando para nos atermos à esse tipo de comportamento. Lógico que existem algumas particularidades entre os bancos que de repente os testes executados em um não podem refletir exatamente o comportamento no outro, como por exemplo algumas questões de inclusão de campos para ordenação que no SQLite funcionam de boa mas o PostgreSQL vai reclamar. Aí vai da escolha de cada um ali. Vários projetos eu mantenho mesmo o SQLite até para manter o "limpa tudo no final do dia" como mencionado no artigo sobre code taste.

Mas a parte importante aqui: sem esse arquivo, o seu projeto não irá rodar sem que ele seja criado de alguma forma. Então, mantenham ele por lá no repositório. "O que??? Manter a configuração dos meus bancos de dados no repositório? Vai que alguém pega?". Isso só faria diferença se você deixasse no arquivo as configurações de usuário e senha de acesso ao banco de dados, e isso você nunca fez, correto? Saber qual o tipo de banco de dados de produção dá para rodar uns scans de rede e descobrir, mas informações mais sensíveis não devem ser enviadas para esse arquivo, o que implica que o arquivo pode existir sem problemas. Por exemplo, esse arquivo:

development:
  adapter: postgresql
  encoding: utf8
  database: myapp_dev
  host: localhost
  username: myapp
  password: myapp

test:
  adapter: postgresql
  encoding: utf8
  database: myapp_test
  host: localhost
  username: myapp
  password: myapp

production:
  adapter: postgresql
  encoding: utf8
  database: myapp
  host: localhost
  username: <%= ENV['MYAPP_DATA_USER'] %>
  password: <%= ENV['MYAPP_DATA_PWD'] %>

Nele podemos ver que estamos utilizando o PostgreSQL em todos os ambientes, com os usuários e senhas fantásticos myapp para desenvolvimento e teste (ei, você não vai guardar coisas sigilosas do projeto aí no seu computador ou nos seus arquivos de seeds ou fixtures, correto?), mas, em ambiente de produção, estamos pegando as informações de variáveis de ambiente. Essa variáveis podem vir direto do ambiente servidor, podem ser configuradas em um arquivo application.yml utilizando a gem Figaro ou através do Rails credentials, mas o importante é que o arquivo existe, ninguém vai ficar precisando adivinhar o que colocar ali ou ter que entrar em contato com o desenvolvedor que sabe. Forneçam arquivos de configurações, sem informações sigilosas.

Assim, podemos colocar uma nota em nosso arquivo README.md:

# MyApp

Essa é a aplicação que vai gerenciar a nossa livraria. Temos recursos como
carrinho de compras, pagamento por cartão de crédito e acompanhamento de
entregas. Utilizamos a stack padrão do Ruby on Rails, bem boring mesmo (isso foi
sarcástico).

## Como rodar o projeto

1. Rodar as tarefas para criar o banco de dados: `bundle exec rails db:create db:migrate`

Ok, banco de dados configurado, vamos para a próxima.

Dados iniciais

O Rails é um framework que preza pela "convenção acima da configuração" e uma das convenções que o Rails nos brinda é a utilização do arquivo de seeds (seeds.rb), que é onde estabelecemos os dados iniciais para o nosso projeto "brotar" depois que criamos o banco de dados. O seeds.rb tem algumas características interessantes: ele deve conter esses dados que sempre vão ser necessários para o uso do projeto, diferente de tasks que podemos utilizar para configurar determinadas coisas, e deve ser idempotente. Que diabos é isso? Vamos pegar uma pequena definição do que é idempotência, lá da Wikipedia:

Em matemática e ciência da computação, a idempotência é a propriedade que
algumas operações têm de poderem ser aplicadas várias vezes sem que o valor do
resultado se altere após a aplicação inicial.

Resumindo, vamos poder (essa é a meta!) executar esse arquivo quantas vezes eu quiser sem alterar alguma coisa na base de dados. Isso é bem importante quando precisamos preencher esse arquivo com mais dados durante o desenvolvimento do projeto, sem ficar com medo de alterar alguma coisa na base (onde, se seguindo o princípio utilizado para não criar "code taste", não daria problema algum). Para isso o Rails tem alguns métodos interessantes, como por exemplo o find_or_create_by. Vamos imaginar que precisamos de 2 usuários para fazer a autenticação no sistema, o admin@myapp.com e o user@myapp.com. Podemos especificar algo do tipo no seeds.rb:

admin = User.find_or_create_by(name: 'Eustáquio Rangel', email: 'taq@myapp.com', password: 'teste', admin: true)
user  = User.find_or_create_by(name: 'José Mané', email: 'zemane@myapp.com', password: 'teste', admin: false)

E agora inserir algumas informações no README:

# MyApp

Essa é a aplicação que vai gerenciar a nossa livraria. Temos recursos como
carrinho de compras, pagamento por cartão de crédito e acompanhamento de
entregas. Utilizamos a stack padrão do Ruby on Rails, bem boring mesmo (isso foi
sarcástico).

## Como rodar o projeto

1. Rodar as tarefas para criar o banco de dados: `bundle exec rails db:create db:migrate`
2. Executar o arquivo de seeds: `bundle exec rails db:seed`
3. Execute o servidor: `bundle exec rails s`

## Como utilizar o projeto

1. Abra o navegador e vá para `http://localhost:3000`.
2. Para fazer login, vá para `http://localhost:3000/login`.
3. Para administrador, utilize `taq@myapp.com' e a senha 'teste'.
4. Para usuário, utilize `zemane@myapp.com' e a senha 'teste'.

Lógico que dessa maneira, vamos ter uma página pelada quando a abrirmos no navegador. Vamos ser legais e colocar mais algumas coisas no nosso arquivo seeds.rb:

sp = State.find_or_create_by(name: 'São Paulo', abbr: 'SP')

City.find_or_create_by(name: 'São José do Rio Preto', state: sp)
City.find_or_create_by(name: 'São Paulo', state: sp)

tec = Category.find_or_create_by(title: 'Tecnologia')
dev = Category.find_or_create_by(title: 'Tecnologia')

ruby  = Book.find_or_create_by(title: 'Conhecendo Ruby', author: admin)
rails = Book.find_or_create_by(title: 'Conhecendo o Rails', author: admin)

if ruby.categories.count.zero?
   ruby.categories << tec
   ruby.categories << dev
end

if rails.categories.count.zero?
   rails.categories << tec
   rails.categories << dev
end

Dessa forma, quando entrarmos na página inicial, já vamos ter alguns dados ali para visualizar o projeto rodando. Vejam que não indiquei as imagens dos livros e mais uma pancada de coisas, mas deu para pegar a idéia. Podemos ver ali que temos alguns exemplos bem claros das tabelas envolvidas, mas existem projetos em que as tabelas e/ou as associações não podem ser tão claras assim, ainda mais em projetos onde as regras de negócio podem ser quase totalmente alienígenas para quem está pegando o projeto. Fornecer dados de exemplo de forma a já dar uma indicação visual de como tudo funciona pode dar várias pistas valiosas para quem está chegando agora assimilar o que encaixa com o que. Lógico que isso também é parte de comunicação de outras partes do código (que código é uma forma de comunicação vocês sabiam, correto?), mas fazer o projeto "pipocar no navegador" alguns segundos depois que ele é recuperado do repositório pode trazer muitas coisas positivas para o seu time.

Então, lembre-se que o seu arquivo seeds.rb é utilizado para alimentar o seu projeto, do zero, de forma idempotente, assim que o código é recuperado e o banco criado, com todas as tabelas necessárias.

Façam o seu projeto brotar com dados iniciais!

Configurações extras

Nesse ponto, o seu time e o seu desenvolvedor novo já devem estar felizes de ter o projeto rodando rapidamente. Mas se a sua stack tiver mais algumas outras coisas, lógico que vamos precisar de algumas outras configurações detalhadas ali no README. Temos algumas ferramentas para automatizar tudo isso, mas se de repente colocarmos no README algo como indicar que precisa disparar o SideKiq, não vai arrancar o braço de ninguém:

# MyApp

Essa é a aplicação que vai gerenciar a nossa livraria. Temos recursos como
carrinho de compras, pagamento por cartão de crédito e acompanhamento de
entregas. Utilizamos a stack padrão do Ruby on Rails, bem boring mesmo (isso foi
sarcástico).

## Como rodar o projeto

1. Rodar as tarefas para criar o banco de dados: `bundle exec rails db:create db:migrate`
2. Executar o arquivo de seeds: `bundle exec rails db:seed`
3. Execute o servidor: `bundle exec rails s`
4. Execute o SideKiq: `bundle exec sidekiq`

## Como utilizar o projeto

1. Abra o navegador e vá para `http://localhost:3000`.
2. Para fazer login, vá para `http://localhost:3000/login`.
3. Para administrador, utilize `taq@myapp.com' e a senha 'teste'.
3. Para usuário, utilize `zemane@myapp.com' e a senha 'teste'.

Não esqueça dessas configurações, inclusive de possíveis tasks que possam ser necessárias.

Testes, fixtures e factories

Uma opinião meio forte aqui: eu removi as factories de todos os meus projetos e retornei às boas e velhas fixtures. Ei, o DHH também gosta de fixtures, então vamos nos concentrar nelas, ok? Só sei que, conforme os projetos cresciam, a factories saíram da sua argumentação inicial de simplicidade para gerar uma complexidade desnecessária, e afinal, não tem problema algum em "martelar" o banco de dados de teste. Se tem, alguma coisa está errada. Mas isso é assunto de outro artigo.

Existe uma dúvida em relação à popular o banco de dados inicial lá no seeds.rb através das fixtures. Não façam isso. Eu já tentei, e não deu muito certo após algum tempo. Vamos dizer que são, digamos, "semânticas" diferentes. O setup inicial é uma coisa, para colocar o projeto com o mínimo de dados para rodar, mas geralmente em nossos testes temos configurações para situações bem mais específicas para exercitar. Independente de como você configura os seus testes, forneça dados para que eles sejam executados e uma instrução no README de como os executar. Por exemplo:

# MyApp

Essa é a aplicação que vai gerenciar a nossa livraria. Temos recursos como
carrinho de compras, pagamento por cartão de crédito e acompanhamento de
entregas. Utilizamos a stack padrão do Ruby on Rails, bem boring mesmo (isso foi
sarcástico).

## Como rodar o projeto

1. Rodar as tarefas para criar o banco de dados: `bundle exec rails db:create db:migrate`
2. Executar o arquivo de seeds: `bundle exec rails db:seed`
3. Execute o servidor: `bundle exec rails s`
4. Execute o SideKiq: `bundle exec sidekiq`

## Como utilizar o projeto

1. Abra o navegador e vá para `http://localhost:3000`.
2. Para fazer login, vá para `http://localhost:3000/login`.
3. Para administrador, utilize `taq@myapp.com' e a senha 'teste'.
3. Para usuário, utilize `zemane@myapp.com' e a senha 'teste'.

## Como rodar os testes

1. Execute os testes com `bundle exec rails test` 

De repente o seu projeto tem o Guard configurado, etc ... o importante é que você deve deixar claro como rodar os testes do seu projeto. Outra coisa muito importante: mantenha seus testes organizados, sem muita "mágica" que deixa o código mais complicado (ei, eu tenho uma palestra sobre isso para que os seus testes sejam compreensíveis para outros usuários que devem se sentir confortáveis em lidar com eles (e faz mais testes!) e rodá-los de maneira rápida. Um dos grandes pecados de processos de testes de sistemas alguns anos atrás era que eram muito complicados e levavam uma eternidade para rodar. Com as ferramentas que temos hoje, se os seus testes ainda são complicados e levam uma eternidade para rodar, desconfio que o problema não seja exatamente as ferramentas.

Vou enviar o projeto de volta ao repositório, faço o que?

Nessa hora, seria interessante que você já estivesse preenchendo esses arquivos mostrados aqui conforme desenvolve. Ter tudo pronto nos seus lugares para ser executado novamente após limpar e clonar seu projeto novamente não tem preço. Fazer esse procedimento também é a garantia que você vai tomar cuidado para não perder alguma coisa que era importante para você e que com certeza vai ser mais importante para algum desenvolvedor que ainda não conheça o sistema.

Ok, espero ter dado dessa vez algumas contribuições mais práticas de como organizar esses tipos de coisa. Se você gostou mas ainda não conhece Ruby on Rails, convido à conhecer meu e-book "Conhecendo Ruby", que é gratuito, e meu e-book "Conhecendo o Rails", que vou atualizar para o Rails 6 daqui algumas semanas.




Comentários

Comentários fechados.

Sem nenhum comentário.

Artigos anteriores