<?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: João Paulo Lethier</title>
    <description>The latest articles on DEV Community by João Paulo Lethier (@jplethier).</description>
    <link>https://dev.to/jplethier</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%2Fuser%2Fprofile_image%2F441955%2Fc7725bc2-7fb7-4a94-843c-e5bddad850b5.jpeg</url>
      <title>DEV Community: João Paulo Lethier</title>
      <link>https://dev.to/jplethier</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jplethier"/>
    <language>en</language>
    <item>
      <title>(Not always) Cool methods to be careful when working with rails</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Tue, 30 Aug 2022 01:14:59 +0000</pubDate>
      <link>https://dev.to/jplethier/not-always-cool-methods-to-be-careful-when-working-with-rails-8cf</link>
      <guid>https://dev.to/jplethier/not-always-cool-methods-to-be-careful-when-working-with-rails-8cf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---PGhCyCz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/615/1%2AtbIOZX_tYatMWCEsj6KYDg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---PGhCyCz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/615/1%2AtbIOZX_tYatMWCEsj6KYDg.jpeg" alt="" width="615" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every ruby developer probably have heard at least a couple of times someone saying that rails is magic. This normally is said because of some cool methods that rails have that make our lives as developers easy, and sometimes we don't even know what happens inside those methods. And that is good because those methods help a lot to maintain good legibility of our code, but not knowing what happens when a method is called can leave to some unexpected behaviors sometimes when dealing with a bigger scale. Let's try to understand three of those methods and their issues.&lt;/p&gt;

&lt;h4&gt;
  
  
  pluck
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://apidock.com/rails/ActiveRecord/Calculations/pluck"&gt;pluck&lt;/a&gt; is a helpful method of active record, making easier to get an array of values from a specific column from a table. However it has a dangerous use that can impact on bad database query performances. Looking at rubocop cops, we can see a conflict. There is a cop that recommends the use &lt;a href="https://docs.rubocop.org/rubocop-rails/cops_rails.html#railspluck"&gt;pluck, instead of map&lt;/a&gt;, but also a cop that recommends the &lt;a href="https://docs.rubocop.org/rubocop-rails/cops_rails.html#railspluckinwhere"&gt;use of select instead of pluck&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The issue with pluck, as we can see in rubocop cop is when it is used inside a where condition. For example, if we have a code like &lt;code&gt;Book.where(author_id: Author.brazilian.pluck(:ids))&lt;/code&gt;, what will happen is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It will open a first connection with database to run the author query first, running something like &lt;code&gt;SELECT authors.id FROM authors WHERE ...&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;After running the authors query, active record will bring all the ids returned from the database and save it in a memory array&lt;/li&gt;
&lt;li&gt;Lastly, it will open a second connection with the database to make the books query and it will use the array returned by the first query as a parameter to the query, running something like this &lt;code&gt;SELECT * FROM books WHERE books.author_id IN (1, 2, 3, ..., 100, 101)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beyond the first unnecessary query, that could be avoided using a subquery, this approach could results in memory issues if the return of the first query brings a huge list that would need to be stored in memory as result.&lt;/p&gt;

&lt;p&gt;A better approach would be to replace the pluck by the &lt;a href="https://apidock.com/rails/v6.1.3.1/ActiveRecord/QueryMethods/select"&gt;select method&lt;/a&gt; in this scenario, that would result in only one query in the database, running something like this &lt;code&gt;SELECT * FROM books WHERE books.author_id IN (SELECT authors.id FROM authors WHERE ...)&lt;/code&gt;. This avoids both the unnecessary query and the memory usage that happens when using pluck inside where.&lt;/p&gt;

&lt;h4&gt;
  
  
  ids
&lt;/h4&gt;

&lt;p&gt;As we can see in &lt;a href="https://apidock.com/rails/v6.1.3.1/ActiveRecord/Calculations/ids"&gt;rails doc&lt;/a&gt;, this is just an alias to &lt;code&gt;pluck(:id)&lt;/code&gt;, so we have the same issues that we have when using it inside a where clause. However, it is recommended by rubocop to be used when outside a where clause, as we can see in this &lt;a href="https://docs.rubocop.org/rubocop-rails/cops_rails.html#railspluckid"&gt;PluckId cop&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  find_each
&lt;/h4&gt;

&lt;p&gt;Depending on the size of a database records list that we need to deal, querying everything at once can lead us to a memory issue. For example, if we need to iterate in a table of books that has millions of records, using Book.all.each will bring all millions of records to memory and iterate in the memory array after it. One way to avoid it is to use the &lt;a href="https://apidock.com/rails/v6.1.3.1/ActiveRecord/Batches/find_each"&gt;find_each method&lt;/a&gt;, which avoids bringing all records to memory at once and splits them into some batch queries.&lt;/p&gt;

&lt;p&gt;This is really good and helpful, but using the &lt;code&gt;find_each&lt;/code&gt; without worrying about the batch size can leave to another issue that is splitting the result into too many batches, which leads us to an issue that is too many queries being executed in database. Backing to the millions books example, let's say that we have 10 millions books and we have to iterate in everyone, using the &lt;code&gt;find_each&lt;/code&gt; with its default &lt;code&gt;batch_size&lt;/code&gt; that is 1000 will lead us to 10000 queries in our database. If we know that we have enough memory to use 10000 batches and use &lt;code&gt;find_each&lt;/code&gt; with this batch_size instead of the default, we could have less 9000 queries in the same task.&lt;/p&gt;

&lt;p&gt;This is a bigger issue when we have a slow query. The slower the query is, the more important is to balance between batch size and memory usage, hitting less the database with slow queries.&lt;/p&gt;




&lt;p&gt;In my opinion, the rails magic is a great and helpful thing, it makes us more productive and helps to maintain a good quality code, helping in maintainability and legibility. However, it is important to try to understand what is happing under the hood when using those methods, mainly when we start to work with high-scale projects.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>ruby</category>
      <category>activerecord</category>
      <category>rails</category>
    </item>
    <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>What not to do when using async background jobs(based on rails+sidekiq experience)</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Mon, 20 Dec 2021 15:25:13 +0000</pubDate>
      <link>https://dev.to/jplethier/what-not-to-do-when-using-async-background-jobsbased-on-railssidekiq-experience-doi</link>
      <guid>https://dev.to/jplethier/what-not-to-do-when-using-async-background-jobsbased-on-railssidekiq-experience-doi</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;p&gt;It is really common to have asynchronous tasks in web applications. It can be useful to move out of the web server tasks that are complex and slow, tasks that have external dependencies and those dependencies that you cannot rely that is always on, and any other kind of tasks that is not necessary to be run synchronous during the http request, letting the duration of the request be smaller.&lt;/p&gt;

&lt;p&gt;But it is easy to have some not expected issues when using background tasks if you are not used to them and know what to avoid and gets into some messy problems to worry about. That's what I want to share in this post, some practices that I tried to not do based on my own experience using &lt;a href="https://github.com/mperham/sidekiq"&gt;sidekiq&lt;/a&gt; to run background jobs in ruby on rails applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Receiving complex objects
&lt;/h4&gt;

&lt;p&gt;Let's say you have a background task that need to receive a timestamp as a param. If you just pass a DateTime object as a param to the sidekiq job, sidekiq will serialize it to json and then deserialize it again to timestamp. This usually can occur in different servers, different machines, and it is not certain that it will not lose precision. Maybe it lost the precision of milliseconds, and it will not make any difference to your application, but it can happen with any other complex param.&lt;/p&gt;

&lt;h4&gt;
  
  
  Receiving entire database records
&lt;/h4&gt;

&lt;p&gt;This mistake is really common if you configure your mailers to be used with sidekiq. Mostly because when mailers are sending synchronous, you pass entire record's objects to them with no issue and everything works fine. And at some point you configure your project to call mailers using sidekiq, or you start to work in a new project that already has that configuration. We have two problems with this approach.&lt;/p&gt;

&lt;p&gt;The first one is the same of the topic before. Database records are complex objects, you have multiple attributes of different types, and some of them can lose precision and you will ended up with different values in your job&lt;/p&gt;

&lt;p&gt;The second one, and most important in my opinion, is that when you call a job you cannot be sure when it will run. It is possible that when the job starts to run the database record passed to it got outdated. For example, we can call a job to send an email passing a database record that contains the email as an attribute, and after the job got enqueued the record got updated and we ended up sending the email to a wrong destination, using an outdated email information.&lt;/p&gt;

&lt;p&gt;So, if it is important to always get the updated data from database when you start to run your job, that is better to &lt;a href="https://github.com/mperham/sidekiq/wiki/Best-Practices#1-make-your-job-parameters-small-and-simple"&gt;pass only enough information&lt;/a&gt; so the job can search in the database to the rest of information by itself. Doing this we also avoid that the serialization and deserialization processes take more time, since simpler objects are quicker to process.&lt;/p&gt;

&lt;h4&gt;
  
  
  Call a job inside a database transaction(or any transaction that can be rolled back)
&lt;/h4&gt;

&lt;p&gt;This seems an obvious practice to avoid reading the line above. But specially with active record I see it happen a lot when we are using active record callbacks. You just have to call a job using a wrong callback, let's say, any callback that runs before the active record transaction finished and be committed. Considering a rails application as example, the best solution here is to always use &lt;a href="https://guides.rubyonrails.org/active_record_callbacks.html#transaction-callbacks"&gt;callbacks that run after transaction commit&lt;/a&gt; when you want to call jobs, since it runs only after the database transaction is finished.&lt;/p&gt;

&lt;p&gt;Another scenario that I saw it happen is when we have some use cases(or services) chained to other use cases(or services). For example, let's say we have the classes below:&lt;/p&gt;


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


&lt;p&gt;Looking to the ProfileUpdate class, everything appears to be correct, the mailer is being called outside the transaction. But if we look to UserSafetyDataValidation class, we can see that there is a job being called there, and everything on call method of this class run inside the database transaction initiated outside of this class. This can cause the job to get run even if the user.update! fail.&lt;/p&gt;

&lt;p&gt;In this case, one approach that we can use is to chain back the messages from inside classes to outside classes and call jobs and other tasks outside use cases and services, maybe using listener and broadcast architecture.&lt;/p&gt;

&lt;p&gt;You can use the &lt;a href="https://github.com/palkan/isolator"&gt;isolator gem&lt;/a&gt; to help you avoid those issues, and you can read about another good approach to this last scenario in this &lt;a href="https://evilmartians.com/chronicles/rails-after_commit-everywhere"&gt;rails after_commit everywhere post&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Change a production job signature
&lt;/h4&gt;

&lt;p&gt;This is not too common as the issues listed above, but it not an edge case either. This issue can be avoided if we always care and think of backwards compatibility in the code we are developing. The problem here is that when you change a job param or a job name, you can have jobs that were enqueued considering the old signature and it will fail when the job is selected from the queue to be executed. For example, if you add a new required param to a job, old enqueued jobs will fail since it does not have this new param enqueued with them.&lt;/p&gt;

&lt;p&gt;In a situation that you really need to change a job's signature, one approach is to change it in some steps. For example, if it is really necessary to add a new param to a job, you can add this param first as an optional param, setting a default value to it, and after a while, when you can be sure that all enqueued jobs now already have the new param being set to it, you can remove the new param's default value and make it required. If the requirement is to change the job name, you can create a new job with the new name, but keep the old job at first moment, removing it only when you do not have any task enqueued for that job and is safe to remove it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Not defining queues and their priorities
&lt;/h4&gt;

&lt;p&gt;This is really common in the first month of projects, when most of things need to be done quick to release the application's first version into production. It is important at least to define queues for two different priorities: tasks that need to be run in a short delay, that are more transactional; tasks that can take longer to run, that the delay is not really important. For example, imagine that you have a job that sends a confirmation SMS with a code after the user changes their phone number, but when that SMS is enqueued, you have a lot of promotional emails being sending and perhaps it causes an hour delay for the confirmation SMS. Probably your user will not wait all this time to receive the SMS.&lt;/p&gt;

&lt;p&gt;So, those are some of the practices that I learned to avoid when working with background jobs, but I think that most of those ideas can be applied to broadcast and events based application. But like I said in the beginning of the post, these are based only in my experience, mostly based in ruby on rails projects and sidekiq. Maybe there are some more important practices to avoid using background with other frameworks and languages.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>backgroundjobs</category>
      <category>sidekiq</category>
    </item>
    <item>
      <title>Rails — Enhancement application performance using cache on views</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Sun, 14 Mar 2021 22:00:13 +0000</pubDate>
      <link>https://dev.to/jplethier/rails-enhancement-application-performance-using-cache-on-views-4j9e</link>
      <guid>https://dev.to/jplethier/rails-enhancement-application-performance-using-cache-on-views-4j9e</guid>
      <description>&lt;h3&gt;
  
  
  Rails — Enhancement application performance using cache on views
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7I6-brUf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AYfjNXS5mANKOx1X6Czo3fA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7I6-brUf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AYfjNXS5mANKOx1X6Czo3fA.jpeg" alt="" width="880" height="494"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image with sustainability's 3Rs: reuse, reduce and recycle&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Versão em português do post no link &lt;a href="https://medium.com/sumone-technical-blog/rails-melhorando-a-performance-das-suas-views-com-cache-c4e318a7df45"&gt;https://medium.com/sumone-technical-blog/rails-melhorando-a-performance-das-suas-views-com-cache-c4e318a7df45&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we talk about application performance, cache is one of the most common approach used. Thinking about cache can be similar to &lt;a href="https://www.fiaformulae.com/en/news/2014/october/sustainability-the-3-r-s-rule.aspx"&gt;environment sustainability 3R's rule&lt;/a&gt;: we &lt;strong&gt;reduce&lt;/strong&gt; the use of resources and application load and requests, &lt;strong&gt;reusing&lt;/strong&gt; resources previously processed and saved, while we need to &lt;strong&gt;recycle&lt;/strong&gt; those resources from time to time to maintain all informations relevant to users and does not show dated and old informations.&lt;/p&gt;

&lt;p&gt;Talking about rails applications, cache is not a new topic. It is there since the beginning of the framework, but it is really common to made mistakes if we do not really understand how to use and how to recycle it.&lt;/p&gt;

&lt;p&gt;One thing that I really like in rails is the possibility to use cache directly in the rails views, caching erb generated html. This approach is also known as &lt;a href="https://guides.rubyonrails.org/caching_with_rails.html#russian-doll-caching"&gt;russian doll caching&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  What is russian doll caching?
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jlT7rl2U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AidRfTehSfY1VFLDijdVfSw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jlT7rl2U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AidRfTehSfY1VFLDijdVfSw.jpeg" alt="" width="880" height="494"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A Russian Doll with all its "children" side by side&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://guides.rubyonrails.org/caching_with_rails.html"&gt;rails guides&lt;/a&gt; are a great source of informations about cache, and has a exclusive topic about this approach, where you can understand in details and see examples of how to use. Being brief about this, it is a chained cache use in views, where you use cache in a smaller view fragment inside another cached view fragment. The name is inspired on old Russian dolls, like the above image.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using cache in views
&lt;/h4&gt;

&lt;p&gt;We can use the helper method cache directly in the rails views. This method, as we can see in the &lt;a href="https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache"&gt;documentation&lt;/a&gt;, receive from 1 to 2 params and a code block. The first and required parameter is an object, the second and optional one is an options hash. The code block passed is what will be saved in the cache after it is processed and generate a html fragment. There are other two methods that you can use directly in views, where you can pass a conditional parameter: cache_if (&lt;a href="https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache_if"&gt;doc&lt;/a&gt;)) and cache_unless (&lt;a href="https://api.rubyonrails.org/classes/ActionView/Helpers/CacheHelper.html#method-i-cache_unless"&gt;doc&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For example, let's imagine that we need to build a page with authors list that contain the name, quantity of books and last book publication date for each author. Just to make easier this exercise, let's not worry about pagination for now.&lt;/p&gt;

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

&lt;p&gt;Let's use the code above to understand how rails will deal and process cache. When the page is rendering, it will first check if all block passed to cache is already processed and saved in cache. If it is found in cache store, it will not process the block again, using the previous processed and generated html that is saved in the cache store. Otherwise, it will process the block, generate a new html fragment and save it on cache for future uses.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache invalidations
&lt;/h4&gt;

&lt;p&gt;One of the most difficult things about cache is to correctly invalidate it. We can forget to invalidate a cache key. When this happens, we will see dated and invalid informations. The simplest way to invalidate some cache is defining a expiration date.&lt;/p&gt;

&lt;p&gt;Using the optional hash param, you can define the expiration date passing a timestamp to expires_in key when calling the cache method, as we can see in the example below:&lt;/p&gt;

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

&lt;p&gt;This way, we assure that this page will be at most 1 hour delay from fresh informations. This will be enough in some scenarios, but what if we want to assure that the informations are always up to date? What if it is important to have the page always showing fresh informations?&lt;/p&gt;

&lt;p&gt;To be able to do this, we must really understand how rails define the cache key when we call cache directly in the views. To define the cache key, rails will use at least three informations: first something that can be identified uniquely the object passed; another information that identifies how fresh the cache is, in other words, a timestamp that shows when the most recent information of the object passed was saved; lastly, one information that identifies uniquely the code block passed to cache method.&lt;/p&gt;

&lt;p&gt;In the authors list example used in the above paragraphs, this will work with rails generating a identifier for the authors query and getting the count result and the maximum value for the updated_at column for the authors queried. Besides that, rails will generate a hash to identify the code block passed. We can see all of this in the image below, in the line that shows the output of read fragment action. It shows a string containing a path to the partial used, followed by the code block hash identifier and the query identifier and authors' count, 4219, and maximum updated_at, 20210132190705856638, result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---wqpl4UO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AZ0ZxJWfR_cPMIVJCW5qi1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---wqpl4UO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AZ0ZxJWfR_cPMIVJCW5qi1g.png" alt="" width="880" height="251"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Console image showing rails console output with cache information key&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Using all those informations, rails makes a lot easier for us to deal with cache invalidation. Since it uses authors count in the cache key, if another author is created in the database, it will generate a new key and invalidate the old key. The same works for any update processed in any author in the database, that will update the updated_at column and automatically invalidate the old cache key. Lastly, using a query and code block hash identifiers make possible that if we change the query or change something in the erb fragment being cached, it will get the updated query/erb fragment and generate a new cache with up to date informations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache inside cache — using the russian doll approach
&lt;/h4&gt;

&lt;p&gt;In this authors list page example above, we saw how to have a cache that work for the entire page always. If a single author has its name changed, the entire page cache will be invalidated and all the list will be reloaded and generated again. If you have a really large list in the page, it can be an issue.&lt;/p&gt;

&lt;p&gt;Russian doll caching is a really good and useful option to solve this issue. Using this approach, we can cache not only the authors list, but cache each author fragment too.&lt;/p&gt;

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

&lt;p&gt;It works the same we saw before with the authors list cache. The difference is that this time rails will follow those steps for each author fragment, generating a cache key for each author fragment, using an identifier hash for each code block and the author's id and updated_at informations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mb8vlLc9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKralx33Vsp_gl32VMPUcqQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mb8vlLc9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AKralx33Vsp_gl32VMPUcqQ.png" alt="" width="880" height="176"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Console image showing rails console output with cache information key for each minor author fragment&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the image above, we can see that rails will perform the read fragment action for each fragment. We can see that the cache key pattern is the same as used on authors list cache — code block identifier, object identifier and a timestamp.&lt;/p&gt;

&lt;p&gt;With this change, the page will now be loaded and processed in three possible scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the cache for the entire page list if no author was change&lt;/li&gt;
&lt;li&gt;Using the fragments cache and processing one or multiples author fragment, for each author that has new informations, not saved previously in the cache. In this case, it will generate a new cache value for the entire list and save it to be used in the next requests&lt;/li&gt;
&lt;li&gt;Processing all the fragments and the list block and generating a cache for each of those, saving it to be used later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Caching with multiple model informations
&lt;/h4&gt;

&lt;p&gt;All the examples used in this post until now can led us to assume that rails take care of everything, and we do not need to worry with anything then just use the cache and be happy.&lt;/p&gt;

&lt;p&gt;That is partly true. Rails sure take care of lot of things that make a lot easier to use cache in rails applications views. But it is not able to deal with one situation alone: caching multiple models together.&lt;/p&gt;

&lt;p&gt;Let's go back to our authors list example. In this page, we show the author's name, books count and last published book date. The last two informations are not from authors table, they are related to the books table. In reality, if we create another book for some author, the page will not show the books count and last published date correctly, because it will not know about this new book we have just added. This happens because the action of add a new book to the database does not change any information used to generate the cache keys used on the page. It does not change the code block neither the authors query, neither change the authors query count or the updated_at column of any author.&lt;/p&gt;

&lt;p&gt;There are two approaches that we can use. The simplest one is to pass a list of objects as first parameter to cache method. This list should contain all objects that can impact the cache, that has informations used in the code block passed.&lt;/p&gt;

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

&lt;p&gt;Doing this, rails will add information about all objects in that list to the cache key, since the object being cached now is the list, not just an author, as we can see below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tlZji65Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AyfU9ip0Es-VTaXI0n9SFbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tlZji65Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AyfU9ip0Es-VTaXI0n9SFbg.png" alt="" width="880" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to take careful about this approach, because every object that we pass increase the rails needed queries to check and load the cache. It generates more complex cache keys too.&lt;/p&gt;

&lt;p&gt;Another approach, that personally I like and use more, is to "update" the author when one of his books is updated, created or deleted. We can solve this easy with rails too, we just need to make sure that we update the correspondent author updated_at column when some of his books has any change. We can do this adding the touch: true parameter to books belongs_to :author relationship in the book's model, like below:&lt;/p&gt;

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

&lt;p&gt;The difference of this is that we add more load to the books CRUD actions In the first approach, we are adding more load and complexity to the page loading, where we want to increase performance for better user experience.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;Using cache is a really useful way to increase performance in rails applications. Rails make it a lot easier to be used with its helpers and rules for cache keys. In most cases, you just need to make sure that you do not use methods that skip model callbacks and validations(like update_column) and all rails will take care of all. In some cases using multiple models in the same fragment cache, you can pass both objects to cache method as a list, or, what I prefer and think it is better, you can "update" the relationship model using belongs_to with touch: true option.&lt;/p&gt;

&lt;p&gt;The most important thing to know when using cache is to understand the cache keys and how to generate this correcly.&lt;/p&gt;

&lt;h4&gt;
  
  
  More examples
&lt;/h4&gt;

&lt;p&gt;I created a &lt;a href="https://github.com/jplethier/russian-doll-cache-examples"&gt;project&lt;/a&gt; that I used as based to write this post, there you can find more examples and scenarios. The scenarios always have a version with cache and another without, so you can compare the difference in the erb file. To run locally the project and run with cache version pages, just set the env var CACHE_ON to true.&lt;/p&gt;

&lt;h4&gt;
  
  
  Other resources and links
&lt;/h4&gt;

&lt;p&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;/p&gt;

&lt;p&gt;&lt;a href="https://blog.appsignal.com/2018/04/03/russian-doll-caching-in-rails.html"&gt;https://blog.appsignal.com/2018/04/03/russian-doll-caching-in-rails.html&lt;/a&gt;&lt;/p&gt;

&lt;p&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;/p&gt;




</description>
      <category>ruby</category>
      <category>rails</category>
      <category>cache</category>
      <category>caching</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>Ensinar para aprender</title>
      <dc:creator>João Paulo Lethier</dc:creator>
      <pubDate>Sun, 27 Jan 2019 20:43:05 +0000</pubDate>
      <link>https://dev.to/jplethier/ensinar-para-aprender-bi8</link>
      <guid>https://dev.to/jplethier/ensinar-para-aprender-bi8</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GCXnWhef--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A4iZJREVVa9niZeaPdY_5rQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GCXnWhef--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A4iZJREVVa9niZeaPdY_5rQ.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eu participo da comunidade &lt;a href="https://ruby.floripa.br/"&gt;RubyFloripa&lt;/a&gt; e sempre que posso tento ajudar de alguma forma nos eventos. Já ajudei na organização de um workshop para iniciantes, já consegui que a &lt;a href="https://lp.sumone.com.br/sitesumone/"&gt;SumOne&lt;/a&gt; recebesse a patrocinasse um meetup e já palestrei em alguns. Mas essa semana que passou eu aceitei um desafio que para mim foi maior e diferente.&lt;/p&gt;

&lt;p&gt;Tinha um tempo já que eu não fazia nenhuma palestra nos meetups, e conversando no nosso grupo no slack, o &lt;a href="https://medium.com/u/97fdc9b3fabc"&gt;Weverton Timoteo&lt;/a&gt; estava querendo alguém para se “voluntariar” a palestrar, pois ele já tinha palestrado no último e achava legal que nesse próximo os palestrantes fossem diferentes. Para dar um contexto maior, tínhamos a proposta já do meetup ser voltado para qualidade de código.&lt;/p&gt;

&lt;p&gt;No meio da conversa no grupo, eu pensei e refleti bastante do porquê eu não me propor a falar sobre o assunto. Enquanto refletia, uma das coisas que me segurava e pesava contra era que eu não tinha material nenhum pronto sobre o assunto, nunca tinha feito uma palestra sobre isso, nunca tinha parado para organizar de fato os pensamentos e idéias sobre esse tema.&lt;/p&gt;

&lt;p&gt;Por outro lado, eu enxergava também como uma boa oportunidade de me forçar a organizar os pensamentos e estudar mais a fundo um assunto bem importante e que eu também gosto muito. Sim, seria um desafio, e a grande dúvida era se os pontos positivos seriam maiores que os negativos.&lt;/p&gt;

&lt;p&gt;Bom, ainda não sei qual será o resultado final da palestra, se vou de fato conseguir contribuir para a comunidade da melhor forma e ajudar na passagem de conhecimento, pois o meetup é só na próxima sexta, mas com certeza para mim já está sendo um aprendizado enorme e uma forma muito eficaz de me forçar a estudar e pesquisar bem mais sobre algo. E isso me leva novamente a mesma conclusão que tive anos atrás quando trabalhei como monitor de matemática e física e que sempre tenho quando preciso ensinar algo para alguém do meu time:&lt;/p&gt;

&lt;p&gt;Ensinar e passar conhecimento adiante talvez seja uma das melhores formas de aprender e crescer.&lt;/p&gt;

</description>
      <category>development</category>
      <category>teach</category>
      <category>meetup</category>
      <category>learn</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>
  </channel>
</rss>
