<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zygo Tecnologia</title>
    <description>The latest articles on DEV Community by Zygo Tecnologia (@zygo-tecnologia).</description>
    <link>https://dev.to/zygo-tecnologia</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2751%2F0cd03624-1fa8-4314-8bb8-ba118628d256.png</url>
      <title>DEV Community: Zygo Tecnologia</title>
      <link>https://dev.to/zygo-tecnologia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zygo-tecnologia"/>
    <language>en</language>
    <item>
      <title>O que não fazer ao usar background jobs(baseado em experiência com rails+sidekiq)</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Tue, 21 Dec 2021 22:15:59 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/o-que-nao-fazer-ao-usar-background-jobsbaseado-em-experiencia-com-railssidekiq-31d9</link>
      <guid>https://dev.to/zygo-tecnologia/o-que-nao-fazer-ao-usar-background-jobsbaseado-em-experiencia-com-railssidekiq-31d9</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4l8gEWGp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A2TQX4JJv779b-HHje7D6VA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4l8gEWGp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A2TQX4JJv779b-HHje7D6VA.jpeg" alt="" width="880" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To read the english version of this post, visit&lt;/em&gt; &lt;a href="https://medium.com/@jplethier/what-not-to-do-when-using-async-background-jobs-based-on-rails-sidekiq-experience-b66316fc3874"&gt;https://medium.com/@jplethier/what-not-to-do-when-using-async-background-jobs-based-on-rails-sidekiq-experience-b66316fc3874&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;É muito comum termos tarefas assíncronas em aplicações web. Elas podem ser bem úteis para removermos tarefas lentas e complexas do servidor web, assim como tarefas que dependem de serviços externos que não temos como garantir que estarão sempre online e tarefas que não são necessárias rodarem durante o request http. Dessa forma podemos deixar que os requests http da aplicação web façam somente o que é estritamente necessário e sejam mais rápidos, melhorando a interação do usuário com a aplicação.&lt;/p&gt;

&lt;p&gt;Ao mesmo tempo, é fácil nos depararmos com problemas não esperados ao lidarmos com tarefas em background e assíncronas se não tivermos experiência e não soubermos o que evitar para não gerar problemas. Por isso quero compartilhar nesse post práticas que eu evito ao trabalhar com tarefas em background, baseadas na minha experiência pessoal trabalhando com aplicações ruby on rails e usando &lt;a href="https://github.com/mperham/sidekiq"&gt;sidekiq&lt;/a&gt; para lidar com as tarefas em background.&lt;/p&gt;

&lt;h4&gt;
  
  
  Receber objetos complexos
&lt;/h4&gt;

&lt;p&gt;Vamos dizer que nós temos uma tarefa em background que precisa receber uma data e hora(timestamp) como um parâmetro. Considerando o uso do sidekiq nesse exemplo, se simplesmente passarmos um objeto DateTime para a task, o sidekiq terá que transformar o objeto DateTime em json, serializando ele, e depois transformar em DateTime de volta para ser usado na task. Esse processo de transformação normalmente pode ocorrer em diferentes servidores, diferentes máquinas, e não tem como garantir que não haverá perda de precisão. Talvez a perda de precisão seja somente nos milisegundos, e isso não faça diferença para a execução da tarefa, porém isso pode acontecer com outros objetos passados como parâmetro também. O ideal é passarmos objetos de tipos mais simples, fazendo nós mesmos a conversão desses objetos complexos para os tipos mais simples, e transformando de volta dentro da tarefa quando necessário.&lt;/p&gt;

&lt;h4&gt;
  
  
  Receber registros inteiros do banco de dados
&lt;/h4&gt;

&lt;p&gt;Esse é um caso muito comum em aplicações rails quando configuramos o mailer para ser executado em segundo plano com o sidekiq. Isso ocorre normalmente porque quando o mailer é executado de forma síncrona, passamos o registro inteiro para ele e funciona perfeitamente, e não lembramos de mudar a forma que os parâmetros são recebidos pelo mailer quando mudamos para serem executados em segundo plano, em uma background task. Pode acontecer também se nos acostumamos a trabalhar em um projeto que os envios de emails são síncronos e começamos a trabalhar em outro projeto que todos os envios de email são assíncronos. Seja nos mailers ou seja em outras background tasks, temos dois problemas quando passamos objetos inteiros de registro do banco de dados.&lt;/p&gt;

&lt;p&gt;O primeiro problema é similar ao do tópico anterior. Registros do banco de dados são objetos complexos, que possuem múltiplos atributos, de diferentes tipos. Se não nos preocuparmos com o processo de transformação deles, não temos como garantir que não haverá nenhuma perda de precisão dos valores de cada atributo.&lt;/p&gt;

&lt;p&gt;O segundo, e mais crítico em minha opinião, é que quando chamamos um job, ou seja, jogamos parte do código para ser executada de forma assíncrona e em segundo plano, não temos como garantir exatamente em que momento esse execução ocorrerá. É possível que quando a tarefa seja executada o registro do banco de dados já tenha sofrido alterações, pode inclusive ter sido removido. Por exemplo, vamos dizer que após o usuário alterar os dados do perfil dele, enfileiramos uma tarefa para ser executada de forma assíncrona para enviar um email informando ele das alterações e passamos o objeto inteiro do usuário como parâmetro, contendo o email como um dos atributos. Porém, antes da tarefa ser executada, o email desse usuário é alterado no banco de dados, fazendo com que quando a tarefa seja executada ela já esteja com um email desatualizado, e a aplicação não envie a comunicação para o email mais novo e correto.&lt;/p&gt;

&lt;p&gt;É importante sempre garantirmos que estamos com os dados atualizados ao executarmos um job. Considerando essa necessidade e o risco de perda de precisão que temos ao usar um objeto complexo como parâmetro, o ideal é sempre passarmos como parâmetro para a tarefa somente o necessário para que ela própria consiga buscar o restante das informações no banco de dados(ou em qualquer outro lugar).&lt;/p&gt;

&lt;h4&gt;
  
  
  Fazer uma chamada para um job dentro de uma transaction do banco de dados(ou qualquer outro processo que possa sofrer um rollback)
&lt;/h4&gt;

&lt;p&gt;Evitar essa prática parece algo óbvio quando pensamos sobre ela. Mas, especialmente com o uso de callbacks no activerecord, já vi esse problema acontecer muitas vezes. Basta chamar um job a partir de um callback que é executado antes da transaction do banco de dados ser finalizada. A melhor solução nesse cenário é usar sempre &lt;a href="https://guides.rubyonrails.org/active_record_callbacks.html#transaction-callbacks"&gt;callbacks que rodam após o commit da transaction do banco de dados&lt;/a&gt; quando queremos fazer chamadas para jobs, garantindo assim que o job nunca é chamado antes da transaction ser finalizada.&lt;/p&gt;

&lt;p&gt;Outro cenário que já vi acontecer é quando temos use cases ou services encadeados, ou seja, use cases(ou services) que chamam e dependem de outros use cases(ou services). Para ficar mais claro, vamos usar as classes abaixo como exemplo:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Se olharmos para a classe ProfileUpdate, tudo parece estar correto, com o mailer sendo chamado de forma assíncrona fora da transaction do banco de dados. Porém, se analisarmos o fluxo completo, incluindo a execução que acontece dentro da classe UserSafetyDataValidation, vemos que tem um job sendo chamado dentro do método call da classe UserSafetyDataValidation e consequentemente sendo chamado dentro da transaction do banco de dados. Isso pode causar um cenário onde a transaction do banco de dados é revertida se a linha user.update! falhar, ocorrendo um rollback, mas o job é executado mesmo assim, dado que ele é chamado antes da finalização da transaction.&lt;/p&gt;

&lt;p&gt;Nessa situação, uma abordagem que podemos usar é encadear as mensagens e eventos de volta nos retornos dos use cases e/ou services, centralizando a chamada a jobs na camada mais externa dos fluxos. Uma possibilidade é usarmos uma estrutura de listeners e broadcast para emitir eventos após a finalização da transaction.&lt;/p&gt;

&lt;p&gt;A gem &lt;a href="https://github.com/palkan/isolator"&gt;isolator&lt;/a&gt; pode ajudar a prevenir que esses cenários ocorram no projeto, e recomendo o &lt;a href="https://evilmartians.com/chronicles/rails-after_commit-everywhere"&gt;post Rails after commit&lt;/a&gt;, do &lt;a href="https://evilmartians.com/chronicles/"&gt;blog da evil martians&lt;/a&gt;, como uma boa leitura para aprofundar sobre como tratar e evitar esses cenários.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mudar a assinatura de um job que já está em produção
&lt;/h4&gt;

&lt;p&gt;Esse não é um cenário tão comum de ocorrer como os listados anterior, mas também não é tão raro, não é um edge case. O problema que ocorre quando mudamos a assinatura de um job é que podemos ter tasks desse job enfileiradas já para serem executadas de forma assíncrona que não consideraram essa nova assinatura. Por exemplo, se adicionarmos um novo parâmetro obrigatório a um job, todos as tasks desse job que já estiverem sido enfileiradas antes dessa mudança irão quebrar quando forem puxadas da fila para serem executadas, dado que elas não possuem esse novo parâmetro definido.&lt;/p&gt;

&lt;p&gt;Para evitar esse cenário, devemos sempre pensar em como garantir a [retrocompatibilidade](&lt;a href="https://en.wikipedia.org/wiki/Backward_compatibility#:%7E:text=Backward%20compatibility%20(sometimes%20known%20as,especially%20in%20telecommunications%20and%20computing.)"&gt;https://en.wikipedia.org/wiki/Backward_compatibility#:~:text=Backward%20compatibility%20(sometimes%20known%20as,especially%20in%20telecommunications%20and%20computing.)&lt;/a&gt; dos jobs. Por exemplo, em uma situação onde de fato faça sentido ou seja necessário mudar a assinatura de um job, podemos fazer essa mudança em etapas. Considerando o cenário onde um novo parâmetro obrigatório precisa ser adicionado, podemos no primeiro momento adicionar esse parâmetro como opcional, definindo um valor padrão para ele, e somente em um segundo momento, quando temos segurança e podemos garantir que todas as tasks enfileiradas desse job já possuem a nova assinatura e o novo parâmetro, podemos remover o valor padrão e torná-lo obrigatório. A mesma idéia pode ser usada caso a necessidade seja a mudança do nome, onde podemos criar um novo job com o novo nome primeiro, e só depois removemos o job antigo, quando todas as tarefas que tinham sido enfileiradas considerando o nome antigo já tenham sido executadas.&lt;/p&gt;

&lt;h4&gt;
  
  
  Não definir filas e suas prioridades
&lt;/h4&gt;

&lt;p&gt;Isso é muito comum no início dos projetos, onde normalmente temos um ambiente mais acelerado e com foco em terminar as funcionalidades essenciais para o lançamento da primeira versão do produto, e normalmente damos prioridade menor para "arrumação da casa" do projeto. Porém, assim que possível, é essencial definir as filas e prioridades que elas terão na execução assíncrona, sendo bem importante definir ao menos duas diferentes prioridades: uma para tarefas que podem ser mais demoradas, onde o tempo de demora e atraso não é muito relevante; e outra para tarefas que não podem demorar muito para rodar, onde o tempo de atraso é relevante e importante para a experiência do usuário. Por exemplo, vamos considerar que temos um job que envia um SMS de confirmação com um código para o usuário após ele mudar o telefone dele, porém quando a tarefa desse job é colocada na fila a mesma se encontra lotada de tarefas de envio de emails promocionais que fazem com que o SMS de confirmação demore muito para ser enviado, talvez até horas. Provavelmente nesse contexto o usuário não vai esperar esse tempo todo para receber o SMS e ter uma experiência frustrante com a aplicação.&lt;/p&gt;

&lt;p&gt;Essas são algumas práticas que aprendi a serem evitadas ao trabalhar com background jobs, mas acredito que boa parte dessas idéias podem ser aplicadas também a sistemas baseados em eventos e baseados em troca de mensagens assíncronas. Porém, como eu disse no começo do post, as idéias e práticas descritas aqui são baseadas somente na minha experiência, majoritariamente em aplicações ruby on rails usando o sidekiq como ferramenta de processamento assíncrono das tarefas. Imagino tenham outras práticas que são importantes serem evitadas baseado em outras experiências, linguagens, frameworks e contextos.&lt;/p&gt;




</description>
      <category>backgroundjobs</category>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Rails — Melhorando a performance das suas views com cache</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Mon, 01 Feb 2021 11:41:07 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/rails-melhorando-a-performance-das-suas-views-com-cache-47m</link>
      <guid>https://dev.to/zygo-tecnologia/rails-melhorando-a-performance-das-suas-views-com-cache-47m</guid>
      <description>&lt;h3&gt;
  
  
  Rails — Melhorando a performance das suas views com cache
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--arECg3cJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AvQJBCFupw4N-BtbuYSlxOw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--arECg3cJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AvQJBCFupw4N-BtbuYSlxOw.jpeg" alt=""&gt;&lt;/a&gt;Imagem dos 3 Rs da sustentabilidade em inglês: reuse, reduce e recycle&lt;/p&gt;

&lt;p&gt;Quando pensamos em melhorar a performance de uma aplicação, o uso de cache é uma das alternativas mais usadas na prática. Podemos pensar no uso de cache como a abordagem dos &lt;a href="https://www.infoescola.com/desenvolvimento-sustentavel/reduzir-reutilizar-e-reciclar/"&gt;3 Rs da sustentabilidade&lt;/a&gt;: &lt;strong&gt;Reduzimos&lt;/strong&gt; a carga de processamento e requests da nossa aplicação ao &lt;strong&gt;reutilizarmos&lt;/strong&gt; algo já calculado, já processado e salvo anteriormente, ao mesmo tempo que precisamos &lt;strong&gt;reciclar&lt;/strong&gt; essa informação de tempos em tempos ou sempre que houver uma novidade relevante que altere ela, para manter ela relevante para a aplicação e não mostrarmos informações erradas.&lt;/p&gt;

&lt;p&gt;Quando falamos de aplicações rails, cache não é algo novo. Pelo contrário, é algo que já existe no framework há muitos anos. Mesmo assim, é fácil cometermos erros se não entendermos bem como usar e, principalmente, como reciclar o cache.&lt;/p&gt;

&lt;p&gt;Uma das formas que eu acho mais interessante de usar cache no rails é usando ele diretamente nas views, cacheando o html gerado pelo erb, utilizando o método conhecido como &lt;a href="https://guides.rubyonrails.org/caching_with_rails.html#russian-doll-caching"&gt;&lt;em&gt;russian doll caching&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  O que é o russian doll caching?
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X9uUP9Gm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AE4hD7GkP1iBc3X2rF1pIjA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X9uUP9Gm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AE4hD7GkP1iBc3X2rF1pIjA.jpeg" alt=""&gt;&lt;/a&gt;Imagem de uma boneca russa desmembrada com cada boneca menor ao lado&lt;/p&gt;

&lt;p&gt;Nas &lt;a href="https://guides.rubyonrails.org/caching_with_rails.html"&gt;guides do rails sobre cache&lt;/a&gt; tem um tópico exclusivo para esse método de cache, explicando em mais detalhes esse tópico. De forma resumida, essa abordagem é a prática de usar o cache de forma encadeada nas views, ou seja, usarmos o cache dentro de partes já cacheadas. O nome russian doll vem das bonecas russas que tem bonecas menores dentro delas, conforme a imagem acima.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Para todos os exemplos citado abaixo nos próximos tópicos abaixo foi usado o projeto &lt;a href="https://github.com/jplethier/russian-doll-cache-examples"&gt;https://github.com/jplethier/russian-doll-cache-examples&lt;/a&gt;. O projeto foi criado somente com o intuito de ser usado como exemplo nesse post. Os cenários criados no projeto foram feitos especificamente para facilitarem o entendimento do uso do russian doll caching, não sendo necessariamente reais.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Utilizando o cache nas views
&lt;/h4&gt;

&lt;p&gt;Para usar o cache nas views do rails, podemos chamar direto o método helper cache . Esse método, conforme podemos ver na &lt;a href="https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache"&gt;documentação&lt;/a&gt;, recebe um objeto como primeiro parâmetro obrigatório, um hash de opções como segundo parâmetro opcional, e um bloco com o html que vai ser renderizado. É possível também usar esse método nas variações cache_if (&lt;a href="https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache_if"&gt;documentação&lt;/a&gt;)) e cache_unless (&lt;a href="https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache_unless"&gt;documentação&lt;/a&gt;). A diferença é que esses dois últimos métodos recebem um parâmetro a mais obrigatório que é a condição a ser avaliada. Por exemplo, no cache_if , o bloco passado só é cacheado se a condição for avaliada como verdadeira.&lt;/p&gt;

&lt;p&gt;Como exemplo, vamos pensar numa página que lista autores contendo o nome de cada autor, a quantidade de livros e a data da publicação do último livro. Para facilitar o exemplo, a página não possui nenhuma paginação.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;No código acima, ao iniciar a renderização dessa página, para transformar o erb em html, o rails vai verificar primeiro se todo o bloco passado para o cache(linhas 4 a 22) já foi cacheado. Se já estiver no cache, o rails não irá executar o bloco novamente, e usar o valor do cache. Caso contrário, ele executará o bloco e salvará o resultado dele no cache.&lt;/p&gt;

&lt;h4&gt;
  
  
  Invalidando o cache
&lt;/h4&gt;

&lt;p&gt;Uma das maiores dificuldades de trabalhar com cache é conseguir invalidar ele corretamente. É muito comum não invalidarmos alguma chave corretamente, e vermos informações antigas, informações que não são mais válidas. A forma mais simples de invalidar o cache é setando uma data de expiração.&lt;/p&gt;

&lt;p&gt;No exemplo acima, podemos passarm uma opção de expires_in ao utilizarmos o método cache, conforme o exemplo abaixo:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Usando dessa forma, garantimos que a informação nessa página estará sempre no máximo 1 hora atrasada. Mas e se eu quiser garantir que a informação está sempre atualizada? Se for importante que assim que eu fizer uma alteração no nome de um autor, a página já exiba essa informação mais atualizada corretamente?&lt;/p&gt;

&lt;p&gt;Para fazer isso, precisamos entender como é montada a chave do cache ao usarmos o método cache na view. Quando passamos um objeto para o método cache, o rails sempre monta a chave do cache usando algo que o ajude a identificar o que é o objeto e também a identificar se há uma versão mais recente desse objeto. No nossa página de lista de autores, ao passarmos uma lista de autores para o cache, o rails faz uma query para pegar o tamanho da lista e o maior valor para a coluna updated_at dos elementos dessa lista, e usa essas duas informações para compor a chave do cache e salva o resultado do bloco nessa chave. Na imagem, além da query feita para pegar o count e o updated_at, é possível perceber o uso desses valores no final da linha que tem o output do Read fragment, onde mostra os valores 4219–20210131190705856638, sendo o primeiro valor o resultado do count, e o último a data e hora da última atualização de um registro de autor na tabela.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--28UlZLOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AtXMxeg4-V_ZwjsS4Xs-g5A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--28UlZLOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AtXMxeg4-V_ZwjsS4Xs-g5A.png" alt=""&gt;&lt;/a&gt;Imagem do terminal mostrando a query de count e max updated at na tabela de autores antes do rails decidir usar ou não o cache&lt;/p&gt;

&lt;p&gt;Um outro caso onde pode ser necessário invalidarmos o cache é quando temos uma alteração no erb que foi passado como bloco para o cache. O rails também já resolve isso para gente de forma automática, pois ele gera um hash para identificar o código que está dentro do bloco, e ao ter qualquer alteração nesse bloco, o hash do novo bloco passa a ser diferente, gerando uma nova chave para o cache e não utilizando mais o valor cacheado anteriormente. Na imagem abaixo é possível ver o output de escrita no cache com um valor diferente na parte do meio da chave, onde aparece _index_with_cache:HASH, em comparação com a imagem acima, como consequência de uma pequena alteração feita no erb.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F3edLml4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AP0vmZgx4nooSwQnkPxOs7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F3edLml4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AP0vmZgx4nooSwQnkPxOs7w.png" alt=""&gt;&lt;/a&gt;Output do servidor rails exibindo a chamada de escrits no cache com a chave incluindo os dados da view de index, com o hash que identifica o fragmento cacheado, além dos dados da lista de autores&lt;/p&gt;

&lt;h4&gt;
  
  
  Utilizando o russian doll cache nas views
&lt;/h4&gt;

&lt;p&gt;Usando ainda o exemplo da lista de autores, pensando em um cenário extremo de milhares de autores(e de novo ignorando paginação para facilitar o entendimento), sempre que um único autor tiver uma simples alteração de nome, o cache da lista vai ser inteiro invalidado e toda a página contendo os milhares de autores terá que ser reconstruída. É nesse caso que o russian doll caching nos pode ser útil.&lt;/p&gt;

&lt;p&gt;Para resolver esse problema, podemos não só cachear a lista, como também cachear cada fragmento de autor na página, conforme abaixo:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Nesse caso, na primeira vez que o rails executar esse bloco, ele vai salvar cada fragmento menor de cada autor no cache primeiro, e depois vai salvar o fragmento maior, que contém todos os fragmentos de autores, também no cache. Ao recarregarmos a página após um ou alguns autores terem sido alterados, o rails não irá ler o cache da lista inteira(que foi invalidada pelo max updated_at da lista de autores), mas irá decidir caso a caso em cada fragmento de autor na página se irá ler do cache ou ter que recarregar as informações.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P-QNTqox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKxEUrl88b09kaR3C6Ks-6A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P-QNTqox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKxEUrl88b09kaR3C6Ks-6A.png" alt=""&gt;&lt;/a&gt;Output do servidor do rails do terminal mostrando várias chamadas de leitura ao cache para cada fragmento da view que corresponde a exibição de um autor.&lt;/p&gt;

&lt;p&gt;Na imagem acima é possível como fica o output do servidor rails no terminal, ao ler fragmentos de vários autores do cache. Também é possível observar novamente o padrão para a montagem da chave do cache, com um hash que identifica o pedaço de código do fragmento, o bloco passado para o método cache, assim como o updated_at de cada autor no final da chave. A diferença fica por conta do uso do id de cada registro de autor na chave, ao invés do hash da query e do count usados ao passarmos uma lista para o cache.&lt;/p&gt;

&lt;h4&gt;
  
  
  Caches com informações de múltiplos models
&lt;/h4&gt;

&lt;p&gt;Como podemos ver, o rails parece cuidar de tudo para nos facilitar a vida em relação a invalidar o cache. Tudo que precisamos é garantir que sempre que eu atualizar uma informação no banco, o updated_at daquele registro também seja atualizado, ou seja, basta evitar os métodos que fazem as chamadas direto no banco(como update_column, por exemplo).&lt;/p&gt;

&lt;p&gt;De fato o rails cuida de muita coisa, facilitando muito nosso trabalho ao usar o cache. Mas tem uma coisa que ele não consegue cuidar sozinho: informações de models diferentes no mesmo cache. No exemplo acima, temos duas informações sobre os livros dos autores em cada cache de fragmento de autor que fazemos: a quantidade de livros escritos e a data de publicação do último livro publicado. Da forma que escrevemos o código da listagem acima, ao cadastrar um novo livro para algum autor, esse cache não será atualizado, pois a ação de cadastrar um novo livro não impacta em nenhuma das informações compostas na chave do cache; não muda o bloco de código erb e consequentemente não gera um novo hash para ele; e também não muda o updated_at de nenhum autor.&lt;/p&gt;

&lt;p&gt;Bom, uma forma de resolvermos isso, é fazermos com que sempre que um livro for atualizado, criado ou removido, o autor daquele livre seja "atualizado". Ou melhor, que o autor daquele livro pareça ter sido atualizado, pois só precisamos atualizar a própria coluna updated_at daquele autor, e não atualizar nenhum outro atributo. Podemos fazer isso adicionando o parametro touch: true no relacionamento do model book, conforme abaixo:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Ao fazermos isso, sempre que um livro for alterado, o rails automaticamente irá atualizar o valor do atributo updated_at do autor correspondente àquele livro, e consequentemente invalidar qualquer cache que tenha sido feito considerando esse atributo.&lt;/p&gt;

&lt;p&gt;Uma outra forma de tratar esses cenários de cache de fragmentos com informações de mais de um registro, é passar para o método cache uma lista contendo cada objeto que é usado naquele cache.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;No exemplo acima, a alteração da linha 13, onde o método cache passa a receber tanto o author, como a query que pega todos os livros daquele autor, faz com que o rails inclua na chave do cache também as informações da quantidade de livro do autor e a última atualização ocorrida em algum livro desse autor. Dessa forma, ao atualizar qualquer livro, o cache do fragmento específico dele irá ser invalidado, garantindo que a informação nova seja exibida.&lt;/p&gt;

&lt;p&gt;Um cuidado que precisamos ter é que cada objeto a mais que passamos para a chave do cache, significa também mais queries na hora de montar e verificar o cache, assim como chaves mais complexas para cada fragmento. Isso precisa ser levado em consideração na hora de escolher a abordagem correta para invalidar o cache e garantir que a informação exibida seja sempre a mais nova. No output da imagem abaixo, podemos ver a chave que o rails montou para o mesmo fragmento de um autor, ao incluirmos a lista de livros.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UsSfPZkn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aswf0FVkcIXOuKskT6elVmQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UsSfPZkn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aswf0FVkcIXOuKskT6elVmQ.png" alt=""&gt;&lt;/a&gt;Output do terminal mostrando a chamada de escrita no cache com todos os dados do autor e mais os dados da query, do count e do maior updated_at da lista de livros do autor.&lt;/p&gt;

&lt;h4&gt;
  
  
  Múltiplas páginas com caches do mesmo objeto
&lt;/h4&gt;

&lt;p&gt;Nos exemplos acima, usamos o objeto autor para cachear o fragmento referente as informações específicas desse autor na listagem. Mas e se quisermos também usar o objeto do autor para cachear informações dele na página de perfil do autor, conforme o código abaixo:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Olhando e pensando de forma rápida, parece que vai dar algum conflito nas chaves entre o cache usado nessa página de show e o cache do fragmento da página de lista de autores, certo? Sim, olhando rápido de fato nos dá esse receio, mas podemos novamente recorrer ao output do servidor no terminal para entender se ele usa a mesma chave ou se já é gerada uma chave separada para essa página de show.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Ro_QTj8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AzxTY4uQDslsSTTKpCTAUrA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Ro_QTj8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AzxTY4uQDslsSTTKpCTAUrA.png" alt=""&gt;&lt;/a&gt;Output do terminal mostrando a chamada de read ao cache com a chave gerada para o cache da página de show de autor&lt;/p&gt;

&lt;p&gt;Como podemos ver, a chave gerada nesse caso é diferente da chave gerada para o fragmento da página de lista de autores. O rails já escopa o cache de acordo com o arquivo erb onde ele está sendo chamado, permitindo que a gente possa simplesmente passar o mesmo objeto para o método cache em diferentes arquivos sem precisarmos nos preocupar em garantir que serão geradas chaves diferentes para cada um deles.&lt;/p&gt;

&lt;h4&gt;
  
  
  Outros exemplos
&lt;/h4&gt;

&lt;p&gt;No &lt;a href="https://github.com/jplethier/russian-doll-cache-examples"&gt;projeto&lt;/a&gt; criado no github para esse post, além da página de &lt;a href="https://github.com/jplethier/russian-doll-cache-examples/blob/main/app/views/authors/index.html.erb"&gt;index&lt;/a&gt; de autores, inclui mais exemplos de uso na página de &lt;a href="https://github.com/jplethier/russian-doll-cache-examples/blob/main/app/views/authors/show.html.erb"&gt;show&lt;/a&gt; de um autor e nas páginas de &lt;a href="https://github.com/jplethier/russian-doll-cache-examples/blob/main/app/views/books/index.html.erb"&gt;index&lt;/a&gt; e &lt;a href="https://github.com/jplethier/russian-doll-cache-examples/blob/main/app/views/books/show.html.erb"&gt;show&lt;/a&gt; de livros. Todos eles possuem exemplos de como ficaria a página sem usar o cache, e como ficaria usando o cache. Para rodar o projeto e carregar as páginas que usam o cache, basta setar a variável de ambiente CACHE_ON para true. Importante também lembrar que a partir do rails 5, para ligar ou desligar o cache no ambiente de development, é preciso rodar rails dev:cache.&lt;/p&gt;

&lt;h4&gt;
  
  
  Outros links sobre cache
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/caching_with_rails.html"&gt;Caching with Rails: An Overview - Ruby on Rails Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.appsignal.com/2018/04/03/russian-doll-caching-in-rails.html"&gt;Russian doll caching in Rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gorails.com/episodes/russian-doll-caching-with-rails-5"&gt;Russian Doll Caching with Rails 5 (Example) | GoRails - GoRails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>caching</category>
      <category>rails</category>
      <category>cache</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Autorização com cancancan no Rails — Verificando permissões</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Mon, 03 Aug 2020 01:32:52 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/autorizacao-com-cancancan-no-rails-verificando-permissoes-26p5</link>
      <guid>https://dev.to/zygo-tecnologia/autorizacao-com-cancancan-no-rails-verificando-permissoes-26p5</guid>
      <description>&lt;h3&gt;
  
  
  Autorização com cancancan no Rails — Verificando permissões
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;O código usado como exemplo para esse post está no github, no link &lt;a href="https://github.com/jplethier/cancancan-examples"&gt;https://github.com/jplethier/cancancan-examples&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i1JZH1rK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AHYQXfHVWoM00c-X8Tot-lg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i1JZH1rK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AHYQXfHVWoM00c-X8Tot-lg.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No &lt;a href="https://medium.com/sumone-technical-blog/autoriza%C3%A7%C3%A3o-com-cancancan-no-rails-definindo-permiss%C3%B5es-d508a71fc55"&gt;post anterior sobre o cancancan&lt;/a&gt;, abordei a questão das definições das permissões na classe Ability, os métodos can e cannot, como combinar permissões e a precedência nesses casos, como utilizar um hash como terceiro parâmetro para definir mais condições, e o uso de alias para agrupar ações.&lt;/p&gt;

&lt;p&gt;Nesse post, pretendo aprofundar um pouco em como verificar essas permissões nos controllers e nas views do projeto. Para entender melhor, vamos usar a mesma base de cenário do post anterior sobre definição de permissões, considerando a estrutura de Task e User. Abaixo temos a classe Ability definida no post anterior para facilitar o entendimento e lembrança nesse post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/0561be6a3a53c5e9d491a58706785181/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/0561be6a3a53c5e9d491a58706785181/href"&gt;https://medium.com/media/0561be6a3a53c5e9d491a58706785181/href&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  current_ability
&lt;/h4&gt;

&lt;p&gt;Todo o funcionamento das verificações do cancancan dependem de um método chamado current_ability, que é um método de controller, também adicionado como helper, e que o cancancan adiciona no projeto automaticamente. O código desse método é o seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def current\_ability
  @current\_ability ||= Ability.new(current\_user)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como podemos perceber, esse método pressupões duas coisas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Que a classe onde foram definidas as permissões se chama Ability;&lt;/li&gt;
&lt;li&gt;E que existe um helper method no controller que se chame current_user, que retorne o usuário logado atualmente no sistema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por ter essas expectativas, em alguns casos é necessário sobrescrever esse método current_ability para ter o funcionamento correto. Por exemplo, caso tenha mais de um namespace no projeto e cada um tenha sua própria classe de permissões, como Web::Ability e API::Ability, ou tenham sido criadas classes específicas para cada context, como User::Ability e Task::Ability, será necessário sobrescrever o método para usar a classe certa em cada contexto(usando o API::Ability no API::ApplicationController, por exemplo).&lt;/p&gt;

&lt;p&gt;No caso de não ter o método current_user, podemos tanto sobrescrever o método current_ability, como definir um alias para outro método. Por exemplo, seguindo o cenário de ter um API::ApplicationController, e imaginando que nesse contexto o método que retorna o usuário que está fazendo o request é chamado current_api_user, podemos escolher entre sobrescrever o método com Ability.new(current_api_user) ou colocar alias_method :current_user, :current_api_user no controller da API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verificando permissões nas views
&lt;/h4&gt;

&lt;p&gt;Um dos usos mais comuns para verificação de permissões acontece nas views dos projetos. Por exemplo, quando precisamos montar um menu e definir quais itens do menu o usuário tem permissão de visualizar e/ou clicar. Ou em uma listagem onde cada item listado possui botões de ações de edição, remoção e visualização de detalhes, e precisamos saber se o usuário tem as permissões necessárias para executar essas ações.&lt;/p&gt;

&lt;p&gt;Dado que o método current_ability existe e está definido corretamente, podemos utilizar outro helper do cancancan para nos ajudar nesses cenários: o método can?. Para exemplificar o uso desse método, vou usar o exemplo de uma listagem de tarefas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/f402dac0daf271e3c1c12849684513d0/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/f402dac0daf271e3c1c12849684513d0/href"&gt;https://medium.com/media/f402dac0daf271e3c1c12849684513d0/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como podemos ver no código acima, o uso do método can? é bem simples e intuitivo. Ele recebe a ação e o objeto a serem verificados a permissão, e esses dois parâmetros seguem a mesma ordem dos parâmetros do método can da classe Ability(visto no post anterior), com a ação sendo passada primeiro, e o objeto sendo o segundo parâmetro.&lt;/p&gt;

&lt;p&gt;Por trás dos panos, quando utilizamos can? :approve, task, o que acontece é que o cancancan vai "perguntar" para o current_ability se o usuário logado pode executar a ação de aprovar essa determinada task. Ou seja, no final das contas, o método can? acaba sendo um próprio método da instância definida em Ability.new, ou seja, seria o mesmo que utilizar diretamente na view current_ability.can? :approve, task, mas a possibilidade de esconder o current_ability na chamada deixa as views muito mais limpas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verificando permissão sem ter um objeto específico&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Um outro cenário que citei como muito comum ser necessário o uso das verificações de permissões é na construção de um menu de navegação. Vamos ver como ficaria um exemplo desse cenário:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/34fb6791f869420b0c52e119a9a7c8d7/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/34fb6791f869420b0c52e119a9a7c8d7/href"&gt;https://medium.com/media/34fb6791f869420b0c52e119a9a7c8d7/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Essa parte é um pouco mais confusa, pois ao invés de passarmos um objeto específico para o método can?, passamos uma classe para ele. O caminho para entendermos esse uso do can? é lermos ele como se fosse a seguinte pergunta: "O usuário logado atual tem permissão para ler &lt;strong&gt;alguma&lt;/strong&gt; tarefa?".&lt;/p&gt;

&lt;p&gt;O ponto chave é na palavra &lt;strong&gt;alguma&lt;/strong&gt; , pois quando passamos para o ability uma classe ao invés de um objeto específico, ele vai nos retornar que a permissão existe desde que ela &lt;strong&gt;possivelmente&lt;/strong&gt; exista para &lt;strong&gt;pelo menos um&lt;/strong&gt; objeto daquela instância. Ok, o alguma está entendido, mas &lt;strong&gt;possivelmente&lt;/strong&gt;? Como assim?&lt;/p&gt;

&lt;p&gt;Para entender melhor, vamos a um exemplo. Imaginem um cenário onde um usuário comum(não é admin e nem moderador) não criou nenhuma tarefa ainda, ou seja, Task.where(user_id: user.id) retorna uma lista vazia. Nesse cenário, não existe nenhum registro no banco de dados de uma tarefa que o usuário tenha permissão para ler, porém, ao executar a verificação da permissão, o ability não irá no banco de dados. O que vai ser feito será simplesmente uma verificação de que existe uma permissão onde o usuário pode ler tarefas com uma condição específica, mas não será verificado no banco de dados se existe alguma tarefa nessa condição. Dessa forma o ability retorna que sim, o usuário pode ler alguma tarefa.&lt;/p&gt;

&lt;p&gt;Uma forma de resumir melhor essa regra é pensar que a verificação feita é o oposto, ou seja, o que é verificado é se o usuário &lt;strong&gt;não pode&lt;/strong&gt; ler &lt;strong&gt;nenhuma tarefa&lt;/strong&gt; , e só é retornado falso para o can? caso o ability consiga confirmar com 100% de certeza essa afirmação de &lt;strong&gt;não ter&lt;/strong&gt; permissão para &lt;strong&gt;nenhuma&lt;/strong&gt;  tarefa.&lt;/p&gt;

&lt;p&gt;Para finalizar sobre a verificação de permissões nas views, é importante citar que também é possível utilizar o método cannot?. Como o próprio nome já deixa claro, ele é a negação do método can?, e na prática normalmente não é quase utilizado, sendo recomendado utilizar o unless can? ... sempre que possível.&lt;/p&gt;

&lt;h4&gt;
  
  
  Verificando permissões nos controllers
&lt;/h4&gt;

&lt;p&gt;Quanto estamos escrevendo os controllers do projeto, temos duas preocupações em relação a autorização: a primeira é que precisamos verificar se o usuário pode chamar aquele endpoint, se pode executar aquela action do controller; a outra é que mesmo que ele possa executar a action, precisamos verificar quais objetos podem ser manipulados por esse usuário nessa action.&lt;/p&gt;

&lt;p&gt;Seguindo o cenário usado no tópico anterior de listagem das tarefas, na action de index do TasksController, precisamos verificar se o usuário pode ler alguma tarefa, e em seguida precisamos saber &lt;strong&gt;quais&lt;/strong&gt; tarefas ele pode ler. Isso pode ser feito com o código abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/662ad56be7137ae542375a1e5712d403/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/662ad56be7137ae542375a1e5712d403/href"&gt;https://medium.com/media/662ad56be7137ae542375a1e5712d403/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aqui já temos o uso do outro helper adicionado ao controller pelo cancancan, assim como de um scope novo, também adicionado pela gem aos models do projeto.&lt;/p&gt;

&lt;p&gt;O authorize! funciona de forma muito similar ao método can? usado nas views, onde ele recebe a ação e o objeto/classe a ser verificado junto com a ação. A diferença para o método can? é que o authorize! não retorna um booleano, mas sim joga a exceção Cancan::AccessDenied e aborta a execução do &lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/Exception-Handling"&gt;método com esse raise&lt;/a&gt;. Dessa forma, não precisamos colocar ifs em todos as actions, e podemos cuidar dessa exceção de forma centralizada, por exemplo, podemos colocar um rescue no ApplicationController e renderizar uma página genérica de acesso não autorizado sempre que um authorize! de alguma action jogar essa exceção.&lt;/p&gt;

&lt;p&gt;Já o scope utilizado para buscar as tarefas no banco de dados, ele recebe o current_ability, e por default busca todas as instâncias do model onde está sendo chamado que podem ser lidos(a ação :index é utilizada como regra por default). Também é possível passar uma action específica como segundo parâmetro, por exemplo, poderíamos chamar Task.accessible_by(current_ability, :approve), e nesse caso ele retornaria todas as tasks que podem ser aprovadas de acordo com as permissões do current_ability. A query no banco de dados é montada usando as condições passadas como terceiro parâmetro(hash conditions) para o método can que define a permissão para a action requisitada(segundo parâmetro do scope, index por default caso o parâmetro não seja passado).&lt;/p&gt;

&lt;h4&gt;
  
  
  Usando before_actions para autorizar actions dos controllers
&lt;/h4&gt;

&lt;p&gt;Usar o authorize! em todas as actions dos controllers pode se tornar muito repetitivo e verboso, além de ser muito propício a erros de desenvolvimento que gerariam bugs. Para facilitar essa verificação, o cancancan tem um outro helper, que pode ser adicionado no controller, onde o controller ficaria da seguinte forma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/0e09570414e505a36e02e8364e13bcbd/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/0e09570414e505a36e02e8364e13bcbd/href"&gt;https://medium.com/media/0e09570414e505a36e02e8364e13bcbd/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esses dois helpers utilizados(load_resource and authorize_resource) rodam como before_actions do controller, e fazem todas as verificações necessárias de permissões para carregar a lista(no caso do index) ou objeto(actions como show e edit, por exemplo) e também executam o authorize! para verificar a permissão de acesso na action do controller. Esses dois métodos podem ser utilizados de forma separada como mostrado acima, inclusive sendo utilizado somente um dos dois de forma isolada, como também podemos simplificar a chamada aos dois com o método load_and_authorize_resource no lugar de chamar os dois separadamente.&lt;/p&gt;

&lt;p&gt;Assim como um before_action comum, esses três métodos aceitam um hash de condições, seja para definir que eles rodem em somente algumas actions, usando only: %i[index update] por exemplo, ou para definir que eles não rodam em algumas actions, except: %i[index update] . Existem outras opções também que podem ser passadas para sobrescrever o método find, o método new, e outras coisas. Para aprofundar em todas as regras e cenários, vale ler a &lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/Authorizing-controller-actions#choosing-actions"&gt;wiki própria da gem&lt;/a&gt; sobre autorização nos controllers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fluxo de criação(new e create)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em todos os exemplos anteriores, sempre falamos de como o cancancan facilita a busca no banco de dados e a verificação se existe permissão para o usuário atual executar a ação no(s) objeto(s) retornados do banco de dados. Mas e no caso das actions de new e create?&lt;/p&gt;

&lt;p&gt;Nessas actions, o load_resource do cancancan instancia de forma automática o objeto correspondente ao model do controller, seguindo as seguintes regras:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Na action de new, o objeto é instanciado com as condições definidas no Ability&lt;/li&gt;
&lt;li&gt;Na action de create, o objeto é instanciado com os parametros enviados do formulário&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos novamente para o controller de tarefas para olhar exemplos e tentar trazer mais clareza, considerando o usuário logado um usuário normal(nem moderador, nem admin):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/f3eb933ce7242c40fb94d54256325415/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/f3eb933ce7242c40fb94d54256325415/href"&gt;https://medium.com/media/f3eb933ce7242c40fb94d54256325415/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nesse exemplo, considerando que definimos no ability que o user normal só pode criar tarefas para ele próprio, na action new a parte doload_resource funciona da mesma forma como se a gente executasse diretamente na action @task = Task.new(user_id: current_user.id), pois dado que a condição passada para o método can do ability foi user_id: user.id, ele criará a task com o id do user passado para o ability(no caso, o current_user). Dessa forma, ele garante que o objeto criado tem sempre as condições definidas na permissão e sem ter a necessidade de chamar o authorize! na instância recém criada.&lt;/p&gt;

&lt;p&gt;Já no create, o cancancan espera que exista um método com o nome task_params(seguindo a convenção MODELNAME_params, nesse caso task sendo o nome do model) para instanciar o objeto usando Task.new(task_params). Em seguida, ele vai chamar o método authorize! para verificar a permissão de criação para o objeto instanciado com os parâmetros enviados, e se por algum motivo o usuário estiver tentando criar uma task com um user_id diferente do id dele, vai ser jogada a exceção Cancan::AccessDenied.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fluxo de atualização ou remoção(show, edit, update e destroy)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As actions de show, edit, update e destroy funcionam de forma muito similar a action de create explicada acima. A diferença é somente na forma de popular a variável @task(no nosso caso do tasks controller). Ao invés de criar uma nova instância, é executado um find para buscar no banco. Esse find espera que as rotas sigam a convenção do rails com o id na url, e que o método find funcione conforme o esperado ao receber o id(ou seja, não tenha sido sobrescrito para funcionar de outra forma). Após o find, é executado o authorize! para verificar a permissão para a ação e o objeto encontrado.&lt;/p&gt;

&lt;p&gt;É possível passar para o load_and_authorize_resource um parâmetro find_by: :method_name definindo um método customizado para ser usado para a busca no banco de dados.&lt;/p&gt;

&lt;p&gt;Para mais clareza, segue abaixo o resultado final do tasks_controller com todas as actions de um CRUD criados e usando o load_and_authorize_resource:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/a9f3335821d649d5c8b390b267c1d711/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/a9f3335821d649d5c8b390b267c1d711/href"&gt;https://medium.com/media/a9f3335821d649d5c8b390b267c1d711/href&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resumo
&lt;/h3&gt;

&lt;p&gt;Tentei cobrir a maior parte de cenários e regras para verificação de permissões em um sistema, mas é uma parte muito diversa em cenários e contextos, então não tenho a presunção de ter conseguido cobrir todas as possibilidades e exceções. Para aprofundar mais, a documentação do próprio cancancan é um ótimo ponto de partida.&lt;/p&gt;

&lt;p&gt;Sobre a lib em si, particularmente, na parte de verificações de permissões nos controllers é onde eu acho que o cancancan, com o uso do load_and_authorize_resource em um controller centralizado, mais é vantajoso e ajuda a manter o foco de desenvolvedor nas funcionalidades, evitando a tarefa repetitiva de verificar as permissões em todas as actions de todos os controllers. Isso facilita também manutenção de problemas relacionados as funcionalidades do sistema, e melhora a legibilidade, pois os controllers ficam menores e somente com o código relativo a sua função.&lt;/p&gt;

&lt;p&gt;Isso ajuda a evitar erros de permissões também, pois ao centralizar e evitar a repetição de código, a parte de autorização do sistema é executada de forma uniforme em todos os controller, inclusive o controle da exceção jogada pelo cancancan, permitindo termos o sistema inteiro lidando com o acesso não autorizado de forma uniforme e centralizada também.&lt;/p&gt;

&lt;p&gt;Apesar de todos os helpers e convenções da biblioteca, ela é bem flexível e fácil de ser customizada caso necessário, permitindo o uso para praticamente qualquer situação e contexto sem muita dor de cabeça também&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/checking-abilities"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/Fetching-Records"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/changing-defaults"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/authorizing-controller-actions"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/Exception-Handling"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>cancancan</category>
      <category>ruby</category>
      <category>authorization</category>
      <category>rails</category>
    </item>
    <item>
      <title>Autorização com cancancan no Rails — Definindo permissões</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Mon, 27 Jul 2020 00:08:41 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/autorizacao-com-cancancan-no-rails-definindo-permissoes-3b28</link>
      <guid>https://dev.to/zygo-tecnologia/autorizacao-com-cancancan-no-rails-definindo-permissoes-3b28</guid>
      <description>&lt;h3&gt;
  
  
  Autorização com cancancan no Rails — Definindo permissões
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eYy7prjI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/559/1%2A0F7DVUxJQTlCu6-FFi71Mg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eYy7prjI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/559/1%2A0F7DVUxJQTlCu6-FFi71Mg.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quando o assunto é autorização em projetos RubyOnRails, duas bibliotecas despontam como as mais utilizadas, conforme pode ser verificado no site &lt;a href="https://www.ruby-toolbox.com/categories/rails_authorization"&gt;ruby toolbox&lt;/a&gt;. No ruby toolbox é possível observar que apesar da gem &lt;a href="https://github.com/varvet/pundit"&gt;pundit&lt;/a&gt; ter uma pontuação maior, a gem &lt;a href="https://github.com/CanCanCommunity/cancancan"&gt;cancancan&lt;/a&gt; é a que tem mais downloads. A gem cancancan é um fork criado e continuado pela própria comunidade a partir da gem &lt;a href="https://github.com/ryanb/cancan"&gt;cancan&lt;/a&gt; criada anteriormente pelo &lt;a href="https://github.com/ryanb"&gt;Ryan Bates&lt;/a&gt;, mas que foi descontinuada. Nesse primeiro post sobre o cancancan vou falar um pouco mais de como funciona a definição de permissões utilizando essa biblioteca, usando o cenário descrito abaixo como exemplo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O código usado como exemplo para esse post está no github, no link &lt;a href="https://github.com/jplethier/cancancan-examples"&gt;https://github.com/jplethier/cancancan-examples&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Preparação
&lt;/h4&gt;

&lt;p&gt;Para poder explicar melhor o uso e funcionamento da gem nesse post, vamos imaginar um projeto de gestão de tarefas onde existe os seguinte models task e user:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/9f3a9690682904cf7d9be85c460b7b7f/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/9f3a9690682904cf7d9be85c460b7b7f/href"&gt;https://medium.com/media/9f3a9690682904cf7d9be85c460b7b7f/href&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Instalação
&lt;/h4&gt;

&lt;p&gt;Instalar é simples como a instalação de qualquer outra gem em um projeto RubyOnRails, basta adicionar gem 'cancancan' no Gemfile e rodar bundle install .&lt;/p&gt;

&lt;h4&gt;
  
  
  Definindo as permissões
&lt;/h4&gt;

&lt;p&gt;A convenção do cancancan é que as permissões sejam definidas de forma centralizada em uma class Ability, que pode ser criada usando o comando rails g cancan:ability , onde ele cria um arquivo dentro da pasta app/models com o nome de ability.rb . Abaixo segue um exemplo da classe implementada nesse arquivo, considerando o contexto colocado anteriormente nesse post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/0561be6a3a53c5e9d491a58706785181/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/0561be6a3a53c5e9d491a58706785181/href"&gt;https://medium.com/media/0561be6a3a53c5e9d491a58706785181/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tem dois pontos importantes nessa classe. O primeiro é o include Cancan::Ability, que permite o uso dos métodos do cancancan utilizados dentro do initialize e também adiciona outros métodos que podem ser chamados diretamente nas instâncias dessa classe Ability, que falaremos mais abaixo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Como podemos ver, apesar do nome da biblioteca ser cancancan , o include na classe Ability ainda usa o nome de módulo CanCan , preservando o nome do módulo originalmente da gem cancan que originou a versão atual da lib mantida pela comunidade.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O segundo ponto importante é dentro do initialize. Todas as permissões são definidas nesse método. E como funcionam essas definições? Basicamente, definimos todas as permissões usando os métodos can e cannot, onde eles recebem no mínimo 2 parâmetros, sendo o primeiro parâmetro a ação para a qual estamos definindo a permissão, e o segundo parâmetro o objeto onde a ação poderá ou não(no caso do cannot ) ser executada. Ainda é possível passar um hash de condições como terceiro parâmetro, definindo mais regras para a permissão que está sendo configurada. Ok, muito bonito, mas um pouco confuso, como funciona na prática?&lt;/p&gt;

&lt;p&gt;Vamos usar a nossa classe acima como exemplo para entender melhor como funciona. Primeiro verificamos se o model user passado para o initialize não é nulo, caso seja nulo, que poderia ser no cenário de um visitante acessando um site na web, retornamos logo na primeira linha do método, sem definir nenhuma permissão, ou seja, no caso do visitante, não seria dada nenhuma permissão para ele.&lt;/p&gt;

&lt;p&gt;Logo em seguida, usando o método can, damos uma permissão de manage em cima do objeto all caso o user seja um admin. Mas calma ai, ação manage e objeto all, o que seria isso? No caso do manage, o cancancan permite que você agrupe ações criando um alias para cada grupo, e nesse caso o manage corresponde a todas as ações que possam ser feitas em um objeto. Isso inclui todas as ações CRUD padrões e qualquer outra ação que venha a ser criada para o objeto passado. O all é um outro facilitador que o cancancan tem para nos permitir definir uma permissão para qualquer objeto. Ou seja, no nosso caso, se o usuário tiver o papel de admin, definimos que ele pode executar qualquer ação em qualquer objeto.&lt;/p&gt;

&lt;p&gt;Passada a permissão dada para o admin, definimos uma permissão novamente de manage, dessa vez para o objeto Task com um hash de condições sendo passado dessa vez para definir regras mais específicas. No nosso caso, colocamos um hash com a chave user_id e o valor sendo o id do próprio usuário que estamos definindo as permissões no initialize(usando user.id). O hash de condições funciona de forma muito similar as condições passadas para montar queries do ActiveRecord, com exceção de que não podemos passar um objeto ActiveRecord como valor para esse hash, temos que passar o id(como foi feito exatamente nesse caso usando o atributo user_id e passando o user.id, ao invés de definir um hash como user: user). Com isso, essa permissão pode ser lida como definindo que o usuário pode gerenciar e executar qualquer ação em cima do objeto Task, desde que ele tenha o user_id igual ao id do próprio usuário, ou seja, de forma simples e menos técnica, definimos que o usuário pode gerenciar suas próprias tarefas.&lt;/p&gt;

&lt;p&gt;Em seguida, criamos um alias para as ações de approve e reprove, e chamamos esse alias de moderate. Dessa forma, conseguimos definir as permissões para essas duas ações de forma conjunta passando a ação moderate para o método can. Caso tivéssemos optado por não usar o alias, o método can poderia receber uma lista de ações, ou seja, o primeiro parâmetro pode ser um array com as ações. Por exemplo, poderíamos usar can [:approve, :reprove] como primeiro parâmetro para o método.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combinando permissões&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Usando o alias criado, primeiro definimos que o moderador pode realizar as ações de aprovar e reprovar em qualquer tarefa. Na linha seguinte, usamos pela primeira vez o cannot, para definir que a não ser que o usuário seja um moderador, ele não pode moderar(aprovar e rejeitar) as tarefas. Aqui acontecem os primeiros "conflitos" de permissões:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Temos uma permissão que define que o usuário pode realizar qualquer ação nas tarefas dele próprio, e depois usamos o método cannot para definir que o usuário não pode aprovar e nem reprovar as tarefas dele.
&lt;a href="https://medium.com/media/374fb3aec531ef89ef72c1a3808bf8f1/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/374fb3aec531ef89ef72c1a3808bf8f1/href"&gt;https://medium.com/media/374fb3aec531ef89ef72c1a3808bf8f1/href&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;O mesmo acontece com as permissões do moderador, onde primeiro definimos que o user que é moderador pode fazer as ações de aprovar e rejeitar em qualquer tarefa e, em seguida, definimos que ele não pode fazer essas ações nas tarefas dele.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O uso do método cannot é normalmente feito exatamente dessa forma, &lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/defining-abilities#combining-abilities"&gt;combinando permissões&lt;/a&gt;. Nesses casos, é muito importante a ordem em que as permissões são definidas, pois quando usamos can e cannot para definir regras para uma mesma ação e objeto, a última permissão definida vai sobrescrever a anterior, ou seja, no nosso caso o &lt;a href="https://github.com/CanCanCommunity/cancancan/blob/develop/docs/Ability-Precedence.md"&gt;cannot prevalece&lt;/a&gt;e garante tanto que o usuário normal não consegue realizar aprovações e reprovações em nenhuma tarefa, assim como o moderador só consegue aprovar e reprovar tarefas de outros usuários, não conseguindo realizar essas ações nas tarefas próprias dele.&lt;/p&gt;

&lt;p&gt;Por último, definimos uma regra simples usando o método can para garantir que o usuário tem a permissão necessária para gerenciar e editar suas informações pessoais, mas garantindo que não consegue editar dados de nenhum outro usuário, novamente utilizando o hash de condições como terceiro parâmetro para isso.&lt;/p&gt;

&lt;h4&gt;
  
  
  É preciso definir todas as ações existentes no sistema dentro dessa classe Ability?
&lt;/h4&gt;

&lt;p&gt;Não, não é preciso. Quando uma ação não é definida explicitamente no Ability, o cancancan assume que o usuário não tem permissão para executar essa ação. Por exemplo, no cenário usado como contexto nesse post, não é definido de forma clara para ninguém a permissão de criação de usuários. Isso significa que ninguém tem essa permissão? No nosso caso não, pois definimos que o usuário que é admin tem permissão para executar qualquer ação em qualquer objeto(can :manage, :all), dessa forma, mesmo não estando explícito na classe Ability, o usuário com papel admin pode sim cadastrar um novo usuário. Em contrapartida, nem o moderador e nem o usuário comum podem realizar essa ação, pois mesmo que não tenha nada explicitamente definindo que eles não podem fazer, não tem nada também que dê permissão para eles realizarem, e nesse caso o cancancan entende que se não foi definida a permissão(nem dada, e nem proibida), o usuário não pode executar a ação.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problemas para lidar com a classe Ability
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Classe crescer demais com muitas permissões
&lt;/h4&gt;

&lt;p&gt;É muito comum nossos projetos serem muito maiores e mais complexos do que o exemplo usado nesse post, e seguindo a convenção do cancancan de colocar todas as permissões dentro dessa mesma classe, ela tende a ficar muito grande e com regras muito complexas, dificultando o entendimento, manutenção e alteração no código. Para melhorar isso, recomendo fortemente quebrar em métodos, como por exemplo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/f85069f557f2c82130de6715908962d2/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/f85069f557f2c82130de6715908962d2/href"&gt;https://medium.com/media/f85069f557f2c82130de6715908962d2/href&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isso já ajuda em muitos cenários, mas nem sempre é suficiente, pode ser que a classe continue complexa e muito grande. Além disso, os testes unitários dessa classe tendem a ser muito extensos, pois testar todas as permissões para todos os possíveis papéis do usuário não é pouca coisa. O tamanho do arquivo de ability pode ter impacto inclusive na performance do seu projeto com a escala e aumento de models e ações do projeto. Nesses cenários, quebrar o arquivo de ability em vários arquivos é um caminho a ser considerado. Não irei aprofundar nesse tópico aqui, mas é possível ler mais sobre isso na própria &lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities%3A-Best-Practices#split-your-abilityrb-file"&gt;wiki do cancancan aqui&lt;/a&gt; e no post &lt;a href="https://medium.com/@coorasse/cancancan-that-scales-d4e526fced3d"&gt;CanCanCan that Scales&lt;/a&gt; do &lt;a href="https://medium.com/@coorasse"&gt;Alessandro Rodi&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Só consigo usar o model user dentro do ability, nenhum outro model pode ser passado para ele?
&lt;/h4&gt;

&lt;p&gt;Por default e convenção, a classe ability é criada com o initialize recebendo somente o model user, mas é possível alterar isso sem grandes dores de cabeça. A única preocupação que precisamos ter ao alterarmos o initialize, colocando mais parâmetros nele, é sobrescrever o método helper que o cancancan nos dá para ser utilizado nos controllers e views, definindo ele com a utilização correta do ability criado no nosso projeto. Por exemplo, no cenário desse post, poderíamos ter um objeto Project e ter as permissões todas sendo definidas escopadas em relação a ele, e ser necessário passar o projeto que está sendo acessado no site no momento para o initialize, definindo algo como:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/f70848c1907e2af785a2b4a378014159/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/f70848c1907e2af785a2b4a378014159/href"&gt;https://medium.com/media/f70848c1907e2af785a2b4a378014159/href&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resumo
&lt;/h3&gt;

&lt;p&gt;Bom, pessoalmente eu gosto muito de utilizar o cancancan para definir as permissões dos meus projetos, acho ele um pouco complexo de entender no início, mas me sinto muito produtivo e acho que vai ficando mais fácil de entender e usar ele conforme o tempo. Sempre tento seguir as boas práticas definidas na wiki do projeto, e sempre quebro pelo menos em múltiplos métodos dentro do próprio ability para ajudar na legibilidade e manutenibilidade.&lt;/p&gt;

&lt;p&gt;De fato as combinações de permissões podem ficar extensas e complexas, tornando necessário a quebra em múltiplas classes também, mas mesmo nesse caso o uso do cancancan não traz muitas dores de cabeça, pois mesmo fugindo da convenção padrão dele, é fácil sobrescrever os helpers dele e utilizar as classes customizadas criadas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Próximos passos
&lt;/h3&gt;

&lt;p&gt;Depois de definir todas as definições de permissões, o próximo passo é verificar essas permissões, principalmente nos controllers e nas views, mas pretendo escrever sobre isso em um próximo post somente.&lt;/p&gt;

&lt;p&gt;Deixo aqui alguns links que usei como fonte para o post e como referência para maiores detalhes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/defining-abilities"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities%3A-Best-Practices"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/blob/develop/docs/Ability-Precedence.md"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan/wiki/Action-Aliases"&gt;CanCanCommunity/cancancan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>ruby</category>
      <category>cancancan</category>
      <category>rails</category>
      <category>authorization</category>
    </item>
    <item>
      <title>Metas, aprendizados e foco</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Fri, 21 Jun 2019 18:39:25 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/metas-aprendizados-e-foco-1a7a</link>
      <guid>https://dev.to/zygo-tecnologia/metas-aprendizados-e-foco-1a7a</guid>
      <description>&lt;p&gt;No início desse segundo trimestre de 2019, estávamos com um desafio bem grande em produto e engenharia aqui na Zygo, tão grande que pouca gente do nosso próprio time achava possível que íamos conseguir alcançar as entregas que havíamos nos comprometido como área. Eu acreditava, sabia da capacidade do time, sabia que teríamos que ter uma dedicação, esforço e energia em níveis bem altos, mas acreditava que tínhamos capacidade de entregar todos os desafios. E ai talvez seja um dos maiores desafios que podemos ter como lideranças: "Como passar confiança para o time? Como fazer eles acreditarem que são capazes de vencer os desafios?" Era algo que eu precisava aprender ainda, e aprender executando ao mesmo tempo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rdiq-J5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/408/1%2ADod39C_3y-i3-RsRG38pHA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rdiq-J5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/408/1%2ADod39C_3y-i3-RsRG38pHA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Primeiro passo, algo que todos nós desenvolvedores aprendemos com metodologias ágeis e gestão de projetos: &lt;strong&gt;quebrar o desafio em desafios menores.&lt;/strong&gt; Fizemos isso, quebramos a entrega em 3 entregas menores, que chamamos de milestones, dessa forma conseguiríamos enxergar melhor cada passo que precisávamos dar como time, e mais importante, &lt;strong&gt;no que precisaríamos focar em cada momento&lt;/strong&gt;. Em cada milestone havia uma lista de tarefas/funcionalidades que precisavam ser entregues e um mínimo de cobertura de testes que precisava ser atingido no projeto.&lt;br&gt;&lt;br&gt;
Milestones definidas, quebramos as entregas de cada uma dessas milestones em semanas, e definimos o prazo de entrega de cada milestone. Como aqui na Zygo adoramos desafios e gostamos de jogar o sarrafo para cima, definimos também uma super meta para cada milestone, que seria conseguirmos entregar todas as tarefas da milestone e alcançar a cobertura de testes automatizados do projeto com uma semana de antecedência do prazo definido como meta.&lt;/p&gt;

&lt;p&gt;Feito tudo isso, parecia que estávamos prontos para trabalhar e alcançar nossas metas, ou melhor, nossas super metas, pois estávamos mirando alto. Começamos a primeira milestones, alguns atrasos nas primeiras semanas, que conseguimos recuperar nas semanas seguintes, e chegamos na semana da super meta bem próximos de conseguirmos terminar tudo e bater ela. Fizemos bastante força, colocando energia e dando o gás final para bater, mas não conseguimos terminar tudo no prazo da super meta.&lt;/p&gt;

&lt;p&gt;Mas nada perdido ainda, se quase batemos a super meta e ainda falta uma semana para a meta, vai ser fácil batermos a meta então, certo? Bom, aqui veio o primeiro grande aprendizado do trimestre que se resume em uma palavrinha: &lt;strong&gt;foco.&lt;/strong&gt; Lembra que escrevi acima que um dos objetivo de quebrar a entrega do trimestre em três milestones e depois quebrar as entregas das milestones em entregas semanais tinha como um dos objetivos ajudar a sabermos no que focar? E não colocar o foco no lugar certo foi nosso principal erro da última semana da primeira milestone. Com o sentimento de que estávamos perto, relaxamos, começamos a planejar e pensar na milestone seguinte, e só percebemos no último dia da milestone que faltava mais coisa do que achávamos, ou seja, não conseguimos entregar a milestone nem dentro do prazo da meta.&lt;/p&gt;

&lt;p&gt;Sinal amarelo e momento de refletirmos, como evitarmos repetir esses erros na segunda milestone? Como tomarmos as decisões certos no que focar na última semana, o que é preciso para isso? Refletindo bastante junto com o time para tentarmos encontrar as respostas dessas perguntas, algumas coisas começaram a emergir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faltou uma clareza maior na primeira milestone do que precisava ser feito, algo visual, uma lista de tudo que precisava ser terminado para que ela estivesse completa&lt;/li&gt;
&lt;li&gt;Precisamos nos alavancar no que cada um sabe fazer melhor nas semanas finais das metas, nos alavancar nas qualidades e capacidades de cada um, ou seja, cada um trabalhar onde é mais produtivo e rende e entrega mais.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bom, agora era colocar tudo isso em prática na segunda milestone, e começamos pela clareza das entregas. Temos um quadro branco na parede da nossa sala, onde escrevemos todas as coisas que o nosso usuário deveria conseguir fazer ao final da milestone dois, por exemplo: "Usuário tem que conseguir criar uma conta e recuperar a senha". Tinham mais de 10 user stories dessas, e escrevemos todas no quadro, e conforme íamos avançando, íamos marcando no quadro o que já era possível que o usuário fizesse, como uma checklist mesmo, algo que já é utilizado mundialmente de várias formas exatamente com esse objetivo: &lt;strong&gt;tornar mais claro e ajudar a focar&lt;/strong&gt;. Com isso, quando foi chegando nas últimas semanas da milestone 2, não precisamos mais ter o sentimento se estávamos perto ou não, sabíamos exatamente o que faltava ser feito, era só olhar para o quadro.&lt;/p&gt;

&lt;p&gt;Resolvendo o primeiro problema, fomos para o ponto dois. Esse ponto nos incomodou bastante, pois temos uma cultura muito forte de desenvolvimento e aprendizado, e se focarmos em cada um trabalhar no que já sabe, teríamos menos aprendizado acontecendo, mas entendemos que existe o momento certo para tudo, e chegamos a conclusão que o momento de abrirmos espaço para todos poderem explorar algo novo e acelerar o seu aprendizado, é no início, nas primeiras semanas, onde qualquer imprevisto ou dificuldade que apareça ainda pode ser recuperado pois temos tempo pela frente para isso ainda, e no final, como temos pouco tempo, precisamos diminuir os riscos de imprevistos, prevenindo-os. Para isso, fizemos uma reunião de planning na semana da super meta onde definimos claramente quem seria responsável por cada parte do sistema que estava faltando entregar, nos alavancando nas capacidades de cada um e aumentando o foco de cada um para a reta final. Resultado: &lt;strong&gt;SUPER META BATIDA.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TaEqVnvG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/833/1%2A6uMQFSacGt_KrKnifdjrHg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TaEqVnvG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/833/1%2A6uMQFSacGt_KrKnifdjrHg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Falei dos aprendizados e ações que tomamos em cima deles, mas para mim o maior aprendizado prático é que tudo gira em torno do &lt;strong&gt;foco&lt;/strong&gt; , aprendemos na prática a importância de não perdermos o foco até de fato termos conseguido chegar nos nossos objetivos. Sem foco não conseguimos tomar as melhores decisões do que fazer e do que não fazer, não conseguimos dizer não para o que não nos ajuda a chegar na meta, e não conseguimos ter clareza do que precisa ser feito.&lt;/p&gt;

&lt;p&gt;Termino com o vídeo da conquista do time, com direito a buzina e brinde. Parabéns a todos do time!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/x9NLCcN7AhY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




</description>
      <category>productdevelopment</category>
      <category>management</category>
      <category>productmanagement</category>
      <category>development</category>
    </item>
    <item>
      <title>Technical Debt</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Fri, 05 Apr 2019 11:01:02 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/technical-debt-4g46</link>
      <guid>https://dev.to/zygo-tecnologia/technical-debt-4g46</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MAoRlguJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AQ-8Re77suqpz5zc3-2S9jQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MAoRlguJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AQ-8Re77suqpz5zc3-2S9jQ.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my life, I worked in different companies, different projects, like e-learning, e-commerce, basic administrative systems, landing pages, … And in every project I had worked, as a developer, team leader or manager, I had to deal with technical debt, because even if anyone wants to talk about it and assume that your project has technical debts, all projects have it. The only way to not have any technical debt is to not have any code written in your codebase, that's the truth.&lt;/p&gt;

&lt;p&gt;Ok, all projects have it, but how to deal with it? And how to find it in your codebase? After we made the first step that is assume you have some technical debts in your code, answer those two questions are the next step to deal with it in a good way. But first, to help the understanding, let's define a little more on what is a technical debt.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bad code
&lt;/h4&gt;

&lt;p&gt;Technical debt can be a bad code that some developer, or even you, wrote in your codebase, and even if we think to never do that, I can think at least in two reasonable scenarios where it can happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The developer did not have the knowledge to write a better code&lt;/li&gt;
&lt;li&gt;The developer did not have enough time to write a better code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a team leader or even as a more experienced developer, you can help in those two scenarios. For example, in the first one you can define a code review process to avoid the code reach the master branch and use the code review to teach and help the developer to develop his skills, you can improve your onboard to improve developer skills before he start to work on your teams and you can create training sessions to keep your team evolving its skills always.&lt;/p&gt;

&lt;p&gt;The same way, you can think about processes and practices that you can add to your team or change in your company, for the second scenario.&lt;/p&gt;

&lt;h4&gt;
  
  
  Changes in the scenario
&lt;/h4&gt;

&lt;p&gt;But technical debt isn't always caused by bad code, sometimes even if you had written the best code any developer could do, the scenarios of the company, the product, or the customers can change and make your code not good to it anymore. For example, you can write a code that is able to send 10 thousand emails per minute, and that is a very good performance for email sending. But with the company scale and got more clients, sometime you may discover that you now need to send 30 thousand emails per minute, and it is not possible with the code you had written in the past to send 10 thousand per minute. That is a technical debt too.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to deal with it?
&lt;/h3&gt;

&lt;p&gt;Recently, I read &lt;a href="https://medium.com/s/story/technical-debt-is-like-tetris-168f64d8b700"&gt;this post&lt;/a&gt; from &lt;a href="https://medium.com/@erichiggins"&gt;Eric Higgins&lt;/a&gt; and I think that his analogy of technical debt with tetris is perfect. You have to be always dealing with technical debt, you need to always be solving some technical debt, because you and your team are always developing new technical debts, that can be discovered some months or some years in the future. Having technical debt in your code is not a problem, the issue is when you do not know what technical debt you have, how big it is and when you have to deal with it.&lt;/p&gt;

&lt;p&gt;So, what we are trying to do here at &lt;a href="https://site.zygotecnologia.com/home/"&gt;Zygo&lt;/a&gt; and I think that can be a good way to deal with it, is to accept that you and your team will be building new technical debts, but keep noted about all debt you already know you have, and keep an eye to discover new debts as soon as possible. The goal is not to solve all technical debts as soon as possible, but have a clear understand of when and how you will solve each one. Using another analogy, to deal with technical debt is like a mountain climb, you will not be able to climb the biggest mountain for the first time, you have to start climbing a smaller one, and even in this smaller one, you need to have a strategy and do it step by step, one part at a time.&lt;/p&gt;




</description>
      <category>projectmanagement</category>
      <category>webdev</category>
      <category>softwaredevelopment</category>
      <category>management</category>
    </item>
    <item>
      <title>Deploy vs release</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Tue, 02 Apr 2019 01:19:57 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/deploy-vs-release-17eo</link>
      <guid>https://dev.to/zygo-tecnologia/deploy-vs-release-17eo</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D2-vVXs5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/460/1%2AXinvqThF8j0ImXTytHV7_Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D2-vVXs5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/460/1%2AXinvqThF8j0ImXTytHV7_Q.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Começo o post com a imagem acima, que descreve bem como era o nosso processo de deploy aqui na Zygo, e abaixo quero contar como mudamos isso separando deploy de release e diminuindo muito os impactos causados aos nossos clientes.&lt;/p&gt;

&lt;p&gt;No meio do ano passado(2018), eu estava lendo o livro &lt;a href="https://www.amazon.com/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339"&gt;Accelerate&lt;/a&gt;, que é sobre uma pesquisa conduzida por 4 anos por &lt;a href="https://www.linkedin.com/in/nicolefv/"&gt;Nicole Forsgren&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/jez-humble/"&gt;Jez Humble&lt;/a&gt; e &lt;a href="https://www.linkedin.com/in/realgenekim/"&gt;Gene Kim&lt;/a&gt; para entender as práticas e processos de devops utilizados nas empresas de tecnologia.&lt;/p&gt;

&lt;p&gt;O livro usa a pesquisa como pano de fundo para definir comportamentos que levam as empresas a serem high, medium ou low performers em relação a entrega de software, e uma das métricas que ele usa para isso é a frequência de deploy, e a conclusão deles a partir dos dados da pesquisa foi de que as empresas que melhor performam são as empresas que fazem cada vez mais deploy para cada desenvolvedor à medida que mais desenvolvedores entram no time. Ou seja, se o time tem 5 desenvolvedores e faz 5 deploys por semana, com 10 desenvolvedores fariam mais de 10, se antes era um deploy por cada desenvolvedor, passaria a ser mais de um essa relação.&lt;/p&gt;

&lt;p&gt;Apesar de muitas vezes parecer óbvia essa conclusão, pois se o objetivo é entregar software, quanto mais deploy fizermos mais software estaremos entregando, certo? Mas nem sempre isso é claro, e o deploy em muitas empresas também tem o lado ruim, de experiências com bugs sendo colocados em produção, etc.&lt;/p&gt;

&lt;p&gt;Bom, feita toda essa introdução, vamos para onde quero chegar. No momento em que eu lia esse livro, como líder da área de engenharia da &lt;a href="https://site.zygotecnologia.com/home/"&gt;Zygo&lt;/a&gt;, eu já estava incomodado com o nosso processo de deploy, mas faltavam dados e uma certeza maior de como resolver, e com os dados da pesquisa do livro eu tive a certeza que estávamos no caminho das empresas de baixa performance avaliadas por eles. O que tínhamos na época era o desenvolvimento sendo feito com a utilização de 3 branches separadas, development , staging e master , os deploys para produção tinham que ser agendados e organizados em conjunto com as outras áreas da empresa para não pegar ninguém de surpresa e tínhamos o caos acontecendo toda vez que tínhamos que passar o código em desenvolvimento para a master para colocar em produção com os problemas de conflitos, de código desatualizado, etc.&lt;/p&gt;

&lt;p&gt;A certeza de que a gente precisava mudar isso urgentemente eu já tinha, mas precisava decidir por onde começar. O primeiro passo foi refletir sobre o que tinha feito a gente chegar onde estávamos, e chegamos nesse processo que tínhamos por dois motivos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Experiência ruim de deploys passados com muitos bugs&lt;/strong&gt; : a quebra do desenvolvimento em 3 branches, com a idéia de colocarmos a branch de staging no ambiente de testes 1 semana antes para a empresa testar e encontrarmos os bugs antes de ir para produção foi para tentar melhorar esse ponto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Falta de alinhamento com o time de CS e de vendas:&lt;/strong&gt; CS e vendas eram pegos desprevenidos com um deploy que alterava alguma funcionalidade quando eles estavam no meio de uma call com o cliente/lead, e como não havia alinhamento da mudança, eles não sabiam como utilizar a funcionalidade alterada e acabavam gerando insegurança no cliente/lead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refletindo e chegando nos motivos, ficou mais claro ainda que o processo que implementamos por causa deles não estava funcionando, pois ele não tinha ajudado a resolver nenhum dos dois problemas que citei acima.&lt;/p&gt;

&lt;p&gt;Refletindo novamente sobre os dois problemas, cheguei a conclusão que o que de fato causava isso era que o nosso release estava atrelado ao deploy, ou seja, colocar um código novo em produção significava também disponibilizar as alterações ou nova features para os nossos clientes no mesmo momento. Opa, finalmente chegamos no problema raiz, encontramos o que de fato precisávamos mudar no processo.&lt;/p&gt;

&lt;p&gt;Com o problema definido, fomos em busca da solução e o maior desafio era mudar o processo de desenvolvimento dos desenvolvedores do time. Quem é ou já foi desenvolvedor, sabe que o fluxo normal quando é pedido uma alteração em uma funcionalidade é o desenvolvedor começar apagando ou alterando código, mas se o objetivo que queremos é podermos fazer deploy das alterações e novidades sem que elas apareçam para o cliente no momento do deploy, precisamos manter o código antigo e o novo convivendo simultaneamente em produção, ou seja, o desenvolvedor não pode simplesmente alterar o código do projeto, ele precisa desde o início do desenvolvimento pensar em como manter o código atual e adicionar o novo sem causar nenhum bug no funcionamento atual. Para isso ecolhemos uma ferramenta de &lt;a href="https://martinfowler.com/articles/feature-toggles.html"&gt;feature toggle&lt;/a&gt;, no nosso caso escolhemos a gem &lt;a href="https://github.com/jnunemaker/flipper"&gt;flipper&lt;/a&gt; e começamos a criar a cultura e o mindset dentro do time de desenvolvimento de trabalharmos de forma a tornar isso possível.&lt;/p&gt;

&lt;p&gt;Depois de alguns meses trabalhando dessa forma, conseguimos de fato colher os resultados, onde as maiores vantagens foram:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Se tornou possível validar hipóteses e idéias em produção com poucos clientes, escolhendo quem queremos que teste as novidades para nos permitir coletar mais informações antes de lançarmos para a base toda;&lt;/li&gt;
&lt;li&gt;Os bugs e problemas de usabilidade foram descobertos enquanto a novidade estava sendo utilizada por poucos clientes, causando um impacto muito menor do que se tivesse sido descoberto depois de ter sido lançado para todos os clientes;&lt;/li&gt;
&lt;li&gt;Conseguimos diminuir o atrito do release junto às outras áreas da empresa, tendo tempo para gerar materiais de educação tanto para nossos clientes como para a própria empresa sobre as novidades;&lt;/li&gt;
&lt;li&gt;Apesar de todas essas vantagens, conseguimos fazer todos os releases disponibilizando para 100% dos nossos clientes em menos de 2 semanas sempre, as vezes em menos até do que 1 semana;&lt;/li&gt;
&lt;li&gt;O código da master estava sempre atualizado e pronto para ser colocado em produção, sem termos que nos preocupar com o que o deploy poderia causar de impacto nos nossos clientes;&lt;/li&gt;
&lt;li&gt;A quantidade de deploys feitos pelo time de desenvolvimento aumentou, mas isso hoje passa despercebido do restante da empresa, pois a única coisa que impacta eles e nossos clientes é de fato o release.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bom, colocando dessa forma parece que é a bala de prata para qualquer time de desenvolvimento conseguir entregar com mais qualidade, facilidade e agilidade, mas tudo tem seu preço e lado negativo. Com o uso do feature toggle, o código começou a ter ifs e condições sendo checadas para ver para quem cada alteração e/ou novidade pode ou não aparecer. A longo prazo isso é um débito técnico que tornaria o código mais difícil de ser mantido, corrigido e melhorado, por isso é muito importante que trabalhando dessa forma se crie uma rotina de que após o release estar completo e disponível para 100% da base de clientes, o time volte no código para limpar os ifs e condições que foram colocados para permitir o feature toggle, e também para limpar o código que se tornou antigo e não é mais utilizado, mantendo assim a qualidade do código do projeto em relação a manutenção futura principalmente.&lt;/p&gt;

&lt;p&gt;Para quem tiver interesse no livro que citei, &lt;a href="https://medium.com/slashdeploy/book-review-accelerate-92ebc00f4354"&gt;nesse post&lt;/a&gt; tem um bom review falando mais sobre o livro.&lt;/p&gt;

&lt;p&gt;Seguem alguns links sobre a prática de feature toggle para quem quiser ler mais sobre isso também:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/articles/feature-toggles.html"&gt;https://martinfowler.com/articles/feature-toggles.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.infoq.com/br/presentations/feature-toggles-os-2-lados-do-poder"&gt;https://www.infoq.com/br/presentations/feature-toggles-os-2-lados-do-poder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://featureflags.io/"&gt;http://featureflags.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/7707383/what-is-a-feature-flag"&gt;https://stackoverflow.com/questions/7707383/what-is-a-feature-flag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/jettech/feature-toggles-give-you-superpowers-78fdeb7ab5e8"&gt;https://medium.com/jettech/feature-toggles-give-you-superpowers-78fdeb7ab5e8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>deploy</category>
      <category>featureflipping</category>
      <category>releasemanagement</category>
      <category>releases</category>
    </item>
    <item>
      <title>Parallel tests on circleci</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Sun, 27 Jan 2019 18:00:48 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/parallel-tests-on-circleci-4k4i</link>
      <guid>https://dev.to/zygo-tecnologia/parallel-tests-on-circleci-4k4i</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xE3gEROK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ASatWV791L3emuqDLKi-B4A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xE3gEROK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ASatWV791L3emuqDLKi-B4A.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you have a large codebase, the tests suite that in the beginning used to take less than 5 minutes to run, starts to take more than 30 minutes to run. That is the case of our main project, we have a automated build that runs brakeman, rubocop, bundler audit and tests configured on circleci to run for every PR opened on github and every commit pushed to master too, and the builds were tooking more than 40 minutes to run.&lt;/p&gt;

&lt;p&gt;This always bothered me, but in the last weeks I began to be even more bothered with that. The main reason for that change is that we change some practices and culture here in SumOne to make the developers team care more with the final delivery and to create a more agile team, a faster build and delivery process, so we are able to deliver more value to ours clients. So, ours developers now care about and are involved in every step of our process, since the discovery until delivery, and have to wait more than 40 minutes to be able to have yours pull requests approved to be merged were not a agile and faster process in the middle of all this.&lt;/p&gt;

&lt;p&gt;So, I started to think about how to solve this. Some months ago I tried to use a gem to run tests in parallel locally, but it did not work fine. So, I decided to have just one goal, how to make the build faster and the tests run in parallel just on circleci, and give up for faster local tests for now.&lt;/p&gt;

&lt;p&gt;First of all, you need to decide how many parallel jobs you want, and unfortunately circleci does not allow parallelism on free account. So, for example, if you decide to run 2 jobs in parallel, you will need to add a paid container that costs $50 dollars. After that, just configure .circleci/config.yml with parallelism: 2 .&lt;/p&gt;

&lt;p&gt;The next step is to run different files for rspec for each container. Looking for the documentation and support conversations of circleci, I ended up with those lines in my .circleci/config.yml project file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- run:
  name: Tests
  command: |
    TESTFILES=$(circleci tests glob "spec/\*\*/\*.rb" | circleci tests split --split-by=timings)
    bundle exec rspec -- ${TESTFILES}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line first get all the files inside spec folder using circleci tests glob command and then split it using the circleci tests split command. There is some options to decide how to split the tests, you can find it &lt;a href="https://circleci.com/docs/2.0/parallelism-faster-jobs/#splitting-test-files"&gt;here in documentation&lt;/a&gt;. I decided to use the --split-by=timings to let circleci take care and find the best way of how to run the tests faster. The output of the first line is a list of files, and it is a different list for each circleci container that is running your build.&lt;/p&gt;

&lt;p&gt;The second line is easier to understand for rails developers that are used to rspec test library. We just call rspec command passing to it the list of files, so that way we have each container running different tests.&lt;/p&gt;

&lt;p&gt;Easier right? Almost! After I've done that, I found the first problem. We use factory_bot in our project, and circleci tests glob "spec/**/*.rb are getting the files inside the spec/factories folder, and when it is passed to bundle exec rspec as argument, rspec tried to run the factories file and we got a Factory FACTORY_NAME already registered error and nothing works.&lt;/p&gt;

&lt;p&gt;So, another look at circleci documentation and I found &lt;a href="https://circleci.com/docs/2.0/parallelism-faster-jobs/#globbing-test-files"&gt;this section&lt;/a&gt; where they show the options they allow you to pass to circleci tests glob as params to filter the files. So, I changed again the .circleci/config.yml file and the result was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- run:
  name: Tests
  command: |
    TESTFILES=$(circleci tests glob "spec/{controllers,features,helpers,listeners,mailers,models,services,workers}/\*\*/\*.rb" | circleci tests split --split-by=timings)
    bundle exec rspec -- ${TESTFILES}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, now the circleci only filters by real test files and pass it as params to rspec command. After commit and push it to github, circleci ran it again and the tests suites that were taken more than 40 minutes starts to run in less than 10 minutes.&lt;/p&gt;

&lt;p&gt;One last thing, I decided to do that for rubocop task too, so I added those lines to the config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- run:
  name: Rubocop
  command: |
    PROJECTFILES=$(circleci tests glob "app/\*\*/\*.rb" | circleci tests split --split-by=timings)
    bundle exec rubocop -- ${PROJECTFILES}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rubocop task time was not a big issue, but since I did it for rspec, there was no reason to not do this with rubocop, and the result was that a task that was taken almost 2 minutes is now takin less than 20 seconds to run.&lt;/p&gt;

&lt;p&gt;So, that was the easy way to improve our process and let the build faster, the next steps are to really improve our tests, try to make every single test faster to run.&lt;/p&gt;




</description>
      <category>rails</category>
      <category>circleci</category>
      <category>rspec</category>
      <category>tdd</category>
    </item>
    <item>
      <title>Retail Hacking</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Mon, 03 Sep 2018 01:37:36 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/retail-hacking-3c6p</link>
      <guid>https://dev.to/zygo-tecnologia/retail-hacking-3c6p</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ga_iF_tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/1%2AqdTX7QhcZr9KoVyPbPDaoA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ga_iF_tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/1%2AqdTX7QhcZr9KoVyPbPDaoA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How excited is for a developer to be able to build something from scratch quickly? And for a designer to think in a featureAnd how often is it possible in the day-by-day jobs?&lt;/p&gt;

&lt;p&gt;We can build great environments, great teams, but the truth is that from monday to friday, developers have to follow processes, rules, good practices and normally has few moments to be really a hacker, to be creative, to enjoy what they are building and build it really quick to see this working soon. And as a leader we have to search a way to let them exercise the hacker side of them, the creative mind, but we need them to deliver constantly, with good frequency, quality and following processes and good practices most of the time.&lt;/p&gt;

&lt;p&gt;A couple of months ago, &lt;a href="https://medium.com/u/34358052ebfb"&gt;Paulo Hashimoto&lt;/a&gt; bring me a discussion about how we could exercise those skills in our product and engineering team at &lt;a href="http://www.sumone.com.br"&gt;SumOne&lt;/a&gt; and bring an idea to the table: stop following processes for one day and build something from scratch from 8am to 6pm. I liked the idea, talked with &lt;a href="https://medium.com/u/545d4364c934"&gt;Thiago Victorino&lt;/a&gt;, leader of SumOne product team, and together we start to thinking how to organize and do this with our teams. We named this activity as "Retail Hacking" and defined some goals to accomplish with this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Team building: We want to use the activity to create stronger relationships between product team(product managers and ux designers) and engineering team(software developers)&lt;/li&gt;
&lt;li&gt;Prototype: We could work on ideas and requests from our clients that we did not prioritize yet, or ideas that we already thought about but did not have time to work on&lt;/li&gt;
&lt;li&gt;Exercize: The most important, a day to be free of day-by-day processes and rules to build something quick from scratch and exercize creative and the ability to make quick decisions and, why not, review decisions and spin off quickly too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all of this defined, and choose one day, and this was last friday, August 31th, when we realized our first Retail Hacking. We started to work at 8am with the main goal to build something related to &lt;a href="https://www.facebook.com/business/marketing/messenger"&gt;Messenger Marketing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We started with a product scope in mind, but in the first hours of the day we discovered that some of the tools we wanted to build would not be possible until we had our app and page reviewed and approved by facebook, what would take around 2 or 3 days. This was the first challenge and a great exercize to review and spin off the product and choose new features and a new path to get back to work and focus. At 6pm, we had a simple platform, with user registration, login, configuration, and features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List of facebook fan page subscribers;&lt;/li&gt;
&lt;li&gt;Integration to send messages to fan page subscribers;&lt;/li&gt;
&lt;li&gt;List of messages of the fan page and a way to answer all the messages;&lt;/li&gt;
&lt;li&gt;Possibility to configure automated responses for messages using regex;&lt;/li&gt;
&lt;li&gt;Pre formatted messages with call to actions;&lt;/li&gt;
&lt;li&gt;And all of this with a good interface and UX for the "MVP".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beside all of this, we finish the day really tired, but really excited and proud of what the team were able to do. Everyone in the team were able to contribute, everyone felt that learn a lot during the day. Before the retail hacking starts, I had one great doubt that was how we could integrate in this activity the two new interns that started to work with us less than one week before and were still in the onboard learning a lot. In the end of the day, I realized that the energy is so big in a exercize like this, the energy is so contagious in a hacking day, that there is always a way to everyone to contribute, and the interns helped a lot the team building some layouts in the interface and even some features for the user flow(login, edit, configure, ..)&lt;/p&gt;

&lt;p&gt;This was the first Retail Hacking of many that we want to do, the next steps are discover how we can keep doing this over time, with more people in the team, with more learns and delivering more great "products" in the end of each hacking.&lt;/p&gt;




</description>
      <category>softwaredevelopment</category>
      <category>productdevelopment</category>
      <category>productmanagement</category>
      <category>hackingteam</category>
    </item>
    <item>
      <title>Advanced queries with RubyOnRails — Talks</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Sat, 17 Mar 2018 14:02:34 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/advanced-queries-with-rubyonrails-talks-3fpf</link>
      <guid>https://dev.to/zygo-tecnologia/advanced-queries-with-rubyonrails-talks-3fpf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HPjjl_rz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/840/1%2Aa_H8ZUnYxPZvw47KRwZzgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HPjjl_rz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/840/1%2Aa_H8ZUnYxPZvw47KRwZzgg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some months ago I did a talk in a meetup for RubyFloripa about advanced queries in RubyOnRails. The objective of the talk was to explain that it does not need to be extermely hard and stressfull to write complex queries in rails projects. And it does not need to have a lot of strict sql written directly in your code, in your classes either. You can write complex queries in a way that is easy to read and understand using a combination of activerecord and arel and following some good practices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/003bfed9727e9502b434435313867fee/href"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/003bfed9727e9502b434435313867fee/href"&gt;https://medium.com/media/003bfed9727e9502b434435313867fee/href&lt;/a&gt;&lt;/p&gt;




</description>
      <category>sql</category>
      <category>ruby</category>
      <category>arel</category>
      <category>rails</category>
    </item>
    <item>
      <title>Unifying records</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Sun, 11 Mar 2018 01:00:44 +0000</pubDate>
      <link>https://dev.to/zygo-tecnologia/unifying-records-61e</link>
      <guid>https://dev.to/zygo-tecnologia/unifying-records-61e</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9OGHfWbm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AfqhsCA2OlUbqxayxP0IouQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9OGHfWbm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AfqhsCA2OlUbqxayxP0IouQ.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have already worked in different projects since 2009, when I started to work as software developer, and there is a problem that appears to be a common issue for a lot of projects: unifying two records into only one.&lt;/p&gt;

&lt;p&gt;For example, sometimes a user can create an account logging in using his google account, forget about it, and in the next time creating another account logging in using facebook account. Normally, this user appears in support asking where are his history, his activities, and other stuff for the first account, because he forgot about the google login, but did not forgot about the past use and informations in the platform.&lt;/p&gt;

&lt;p&gt;A real example happened with me, some years ago I created an account in Meliuz platform using my email and choosing a custom password and register my CPF(CPF is a unique number that identifies each person in Brazil). I did not use this site for a while, and when I logged again I used my facebook account to log in. I started to use with this account, and in some point I was asked for my CPF in this new account to be able to perform a action in the system, but when I tried to input my CPF I got an error telling me that my CPF was already registered. I had to send a message to theirs support and probably they had to unify my two accounts so I could keep using the facebook account.&lt;/p&gt;

&lt;p&gt;The problem unifying records is that in a big project you have a lot of dependencies. For example, let's imagine that we have a project/task manager platform and the below structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MFNtziNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/581/1%2A8d93yTnDy-xH4eAxyfpH2Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MFNtziNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/581/1%2A8d93yTnDy-xH4eAxyfpH2Q.png" alt=""&gt;&lt;/a&gt;UML Class diagram for task manager project&lt;/p&gt;

&lt;p&gt;So, in this case, when we have to unify an user, we have to change projects and tasks too. In this little example, we can think it is not a big deal have to add methods to three models and finish the feature. But when you have a lot bigger project, you will have to deal with lots of models that has to change when you are unifying a record.&lt;/p&gt;

&lt;p&gt;Another scenario is that after you build the unify user feature, someone works on a task that adds a new model with user_id and forgets to adds this model in unify user feature. The next time you run the unify users it will break because you have a new model that is not prepared to be changed and keep working after you unify two users.&lt;/p&gt;

&lt;p&gt;So, when I get a task in my current job to build an unify users feature, I put two requisites for this to be done:&lt;/p&gt;

&lt;p&gt;1 — It has to be easy to run this, only one command, one service to deal with this and the code has to be short and easy to understand;&lt;/p&gt;

&lt;p&gt;2 — Every new model created with user_id has to be easy to be added in this unify users flow.&lt;/p&gt;

&lt;p&gt;To accomplish the first item, we reach a code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ApplicationRecord.descendants.each do |model\_class|
  next unless model\_class.column\_names.include?('user\_id')

  model\_class.unify\_users!(user\_to\_empty, user\_to\_keep)
end

user\_to\_empty.delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using ApplicationRecord.descendants we had a list of all models, since all of them inherits from ApplicationRecord . So, we iterate on this list and first check for each model if it has a column named user_id. If does'nt, just go next, if has we call unify_users! method. After iterate on all models,, we just delete the duplicated user. So, with this we were satisfying about accomplish the first goal, the first requisite, we had a nice, short and easy to understand code.&lt;/p&gt;

&lt;p&gt;But how about the second? We have a problem that for each new model we create with user_idwe would have to remember to create a method named unify_users! , if we forget the code above would break. And even for the existent models with user we would have to create a lot of unify_users method for each model. So, we were not complete satisfied yet, we have to acomplish the second goal.&lt;/p&gt;

&lt;p&gt;So, we create a concern and put a rule in the team, we can never adds a &lt;code&gt;belongs_to :user&lt;/code&gt; directly in a model, we should always use our new module named UserBelongs :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module UserBelongs
  extend ActiveSupport::Concern

  included do
    belongs\_to :user, optional: true
  end

  # Add the unify\_users! method to the model class.
  module ClassMethods
    def unify\_users!(from, to)
      # rubocop:disable Rails/SkipsModelValidations
      where(user: from).update\_all(user\_id: to.id)
      # rubocop:enable Rails/SkipsModelValidations
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, now we have only one implementation for unify_users! that is enough for almost every model. For some models that needs specific rules for this unify we still need to implement if on each model file, but it is ok, since it is specific rules about that model anyway. Another observation is that we use update_all on this method, and it was on purpose. Update all is the fast way we find to update a lot of records in one single transaction and skipping validations, what was needed for this specific case, because speed was the main concern here since a user could have a lot of records and if we run validations and run a transaction for each one it could take a long time to finish this task.&lt;/p&gt;

&lt;p&gt;The most advantage of this approach was seen last month, when we had to create another unify feature, for another model, and with that already built, was really easy and quick to apply the same idea and concept for another model unifying.&lt;/p&gt;

&lt;p&gt;So, if you have any suggestions about this, please leave a comment, we would love to discuss and find an even better way to implement this.&lt;/p&gt;




</description>
      <category>activerecord</category>
      <category>rails</category>
    </item>
  </channel>
</rss>
