<?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: Clément Morisset</title>
    <description>The latest articles on DEV Community by Clément Morisset (@morissetcl).</description>
    <link>https://dev.to/morissetcl</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%2F595270%2F7b4a6eac-0fe3-4c34-a3dc-85957787b6a3.gif</url>
      <title>DEV Community: Clément Morisset</title>
      <link>https://dev.to/morissetcl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/morissetcl"/>
    <language>en</language>
    <item>
      <title>How to optimize factory creation.</title>
      <dc:creator>Clément Morisset</dc:creator>
      <pubDate>Wed, 21 Dec 2022 20:24:45 +0000</pubDate>
      <link>https://dev.to/potloc/how-to-optimize-factory-creation-3bnp</link>
      <guid>https://dev.to/potloc/how-to-optimize-factory-creation-3bnp</guid>
      <description>&lt;p&gt;At &lt;strong&gt;&lt;a href="https://www.potloc.com/" rel="noopener noreferrer"&gt;Potloc&lt;/a&gt;&lt;/strong&gt; we have a test stack which is pretty standard in the Rails ecosystem. We run tests with &lt;strong&gt;RSpec&lt;/strong&gt;, we use &lt;strong&gt;FactoryBot&lt;/strong&gt; for setting up our test data, &lt;strong&gt;Capybara&lt;/strong&gt; for user interactions, &lt;strong&gt;Github Actions&lt;/strong&gt; as a CI etc..&lt;/p&gt;

&lt;p&gt;These great tools allow us to code at a fast pace with good test coverage. But this pace comes at a cost. The more the team grows, the bigger the codebase gets and the more tests get written.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤕 The issue
&lt;/h3&gt;

&lt;p&gt;As developer we usually take care of optimizing our own code and queries, we are used to test new implementations with all the edge cases. But tests optimization is likely a topic that we put &lt;strong&gt;at the bottom of the list&lt;/strong&gt; and that’s if we ever even think about it.&lt;br&gt;
Up until the moment where quietly but surely you will end up with a CI that take ages to run and a whole test suite to speed up.&lt;/p&gt;

&lt;p&gt;Let’s see how we took on this challenge at &lt;strong&gt;Potloc&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the purpose of demonstration we will rely on this simple test file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Purging&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;QuestionnaireWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :worker&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:questionnaire&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:questionnaire&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"#perform"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"destroys a survey form"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;questionnaire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Questionnaire&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"given associations"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"destroys a questionnaire and its associations"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:postal_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;questionnaire: &lt;/span&gt;&lt;span class="n"&gt;questionnaire&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;questionnaire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SurveyQuestion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧑‍🚒 The solutions
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/thoughtbot/factory_bot" rel="noopener noreferrer"&gt;factory-bot&lt;/a&gt; gem is used in almost in all of our spec files and it make our set up much more easier than when we use fixtures. &lt;br&gt;
Here is the tradeoff, the easier the gem is to use, the  more likely you’ll end up with some pain to control its usage. And when the times come to tackle slow tests, &lt;strong&gt;the best bet you can take is to start digging into you factories&lt;/strong&gt; because it’s likely they are the primary reason why your test suite is slowing down&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Avoiding the factory cascades&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To quote &lt;a href="https://evilmartians.com/chronicles/testprof-2-factory-therapy-for-your-ruby-tests-rspec-minitest" rel="noopener noreferrer"&gt;Evil Martian&lt;/a&gt; a factory cascade is an&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;uncontrollable process of generating excess data through nested factory invocations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our test we use two factories a &lt;code&gt;questionnaire&lt;/code&gt; and &lt;code&gt;question&lt;/code&gt; who could be represented like this as a tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;questionnaire
|
|---- survey

question
|
|---- survey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this simple example each factory calls a nested factory. This means that every time we create a &lt;code&gt;questionnaire&lt;/code&gt; factory, we also create a &lt;code&gt;survey&lt;/code&gt; factory.&lt;/p&gt;

&lt;p&gt;To have a better vision of what objects are created in our spec file we can use &lt;a href="https://test-prof.evilmartians.io/" rel="noopener noreferrer"&gt;test-prof&lt;/a&gt;, a powerful gem that provides a collection of different tools to analyse your test suite performance. One of this tool is really useful to &lt;strong&gt;identify a factory cascade&lt;/strong&gt;, let’s introduce &lt;a href="https://test-prof.evilmartians.io/#/profilers/factory_prof" rel="noopener noreferrer"&gt;factory profiler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If we run &lt;code&gt;FPROF=1 bundle exec rspec&lt;/code&gt; the factory profiler, it will generate the following report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[TEST PROF INFO] Factories usage

 Total: 6
 Total top-level: 3
 Total time: 00:01.005 (out of 00:26.087)
 Total uniq factories: 3

   total   top-level     total time      time per call      top-level time               name

       3           0        0.6911s            0.2304s             0.0000s             survey
       2           2        0.6397s            0.3199s             0.6397s             questionnaire
       1           1        0.3658s            0.3658s             0.3658s             question
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most interesting insight is the &lt;strong&gt;difference&lt;/strong&gt; between the &lt;code&gt;total&lt;/code&gt; and the &lt;code&gt;top-level&lt;/code&gt;. The more this difference is important, the more you end up with factory cascade, meaning that &lt;strong&gt;you are creating useless factories&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s take the &lt;code&gt;survey&lt;/code&gt; , we don’t instantiate any surveys in our test suite but during the execution we create 4.&lt;/p&gt;

&lt;p&gt;The easiest workaround it is to instantiate a &lt;code&gt;survey&lt;/code&gt; at the top-level and to associate the factories to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Purging&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;QuestionnaireWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :worker&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:survey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:survey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:questionnaire&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:questionnaire&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;survey: &lt;/span&gt;&lt;span class="n"&gt;survey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt; &lt;span class="s2"&gt;"destroys a questionnaire and its associations"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:postal_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;questionnaire: &lt;/span&gt;&lt;span class="n"&gt;questionnaire&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;survey: &lt;/span&gt;&lt;span class="n"&gt;survey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the factory profiler we now have a different report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[TEST PROF INFO] Factories usage

 Total: 5
 Total top-level: 5
 Total time: 00:00.868 (out of 01:09.067)
 Total uniq factories: 3

   total   top-level     total time      time per call      top-level time               name

       2           2        0.6986s            0.3493s             0.6986s             survey
       2           2        0.0973s            0.0487s             0.0973s             questionnaire
       1           1        0.0729s            0.0729s             0.0729s             question
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! No more factory cascades. The &lt;code&gt;total&lt;/code&gt; and the &lt;code&gt;top-level&lt;/code&gt; columns are the same. &lt;strong&gt;We now create 5 factories instead of 8&lt;/strong&gt;. We have decreased the time spent creating factories &lt;strong&gt;by 30%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The caveat of this method it that it could be &lt;strong&gt;a heavy process to maintain&lt;/strong&gt;. Thankfully, &lt;code&gt;test-prof&lt;/code&gt; as a recipe called &lt;code&gt;FactoryDefault&lt;/code&gt;. Removing factory cascades manually could be good enough most of the time but if you want to go further you can follow the &lt;a href="https://test-prof.evilmartians.io/#/recipes/factory_default?id=instructions" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That being said, &lt;code&gt;test-prof&lt;/code&gt; has even more to offer, it’s time to introduce an awesome helper named &lt;code&gt;let_it_be&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reuse the factory you need&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's bring a little bit of magic and introduce a new way to set up a shared test data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;let_it_be&lt;/code&gt; is a &lt;a href="https://test-prof.evilmartians.io/#/recipes/let_it_be" rel="noopener noreferrer"&gt;helper&lt;/a&gt; that allows you to reuse the same factory for all your spec file. In our example we don’t need to create 2 &lt;code&gt;survey&lt;/code&gt; and 2 &lt;code&gt;questionnaire&lt;/code&gt; we could re-use the same ones for all our file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Purging&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;QuestionnaireWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :worker&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;let_it_be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:survey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:survey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;let_it_be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:questionnaire&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:questionnaire&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;survey: &lt;/span&gt;&lt;span class="n"&gt;survey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the factory profiler we now have a different report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[TEST PROF INFO] Factories usage

 Total: 3
 Total top-level: 3
 Total time: 00:00.272 (out of 00:24.264)
 Total uniq factories: 3

   total   top-level     total time      time per call      top-level time               name

       1           1        0.2024s            0.2024s             0.2024s             survey
       1           1        0.0323s            0.0323s             0.0323s             questionnaire
       1           1        0.0375s            0.0375s             0.0375s             question
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we only create the factories we need, by reusing the same ones throughout our file.&lt;/p&gt;

&lt;p&gt;Be aware that &lt;code&gt;let_it_be&lt;/code&gt; come with a &lt;a href="https://test-prof.evilmartians.io/#/recipes/let_it_be?id=caveats" rel="noopener noreferrer"&gt;caveat&lt;/a&gt; section. I strongly encourage you to read the documentation and use this powerful helper in accordance with your needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀  Conclusion
&lt;/h3&gt;

&lt;p&gt;Let’s take a step back and relish our improvements:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Initial&lt;/th&gt;
&lt;th&gt;Without cascades&lt;/th&gt;
&lt;th&gt;With let_it_be&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Factories creation time&lt;/td&gt;
&lt;td&gt;00:01.00&lt;/td&gt;
&lt;td&gt;00:00.868&lt;/td&gt;
&lt;td&gt;00:00.272&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Numbers look nice for this simple example. But what is the impact in real life at &lt;strong&gt;Potloc&lt;/strong&gt;? &lt;/p&gt;

&lt;p&gt;So far we just applied this recipe for a specific folder of our codebase. Below the result by profiling locally that folder:&lt;/p&gt;

&lt;p&gt;Before we spent &lt;strong&gt;3.50 min&lt;/strong&gt; in factories creation, now &lt;strong&gt;2 min. (~ -50%)&lt;/strong&gt;&lt;br&gt;
Before we created &lt;strong&gt;6824&lt;/strong&gt; factories, now &lt;strong&gt;4378. (~ -35%)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;test-prof&lt;/code&gt; is the swiss army knife we needed to speed up our test suite. It’s still a long journey but by embracing this topic we have already taken an important step!&lt;/p&gt;

&lt;p&gt;Want to go further? Watch this &lt;a href="https://www.youtube.com/watch?v=eDMZS_fkRtk&amp;amp;ab_channel=parisrb" rel="noopener noreferrer"&gt;99 problems of slow test&lt;/a&gt; talk by &lt;a href="https://github.com/palkan" rel="noopener noreferrer"&gt;Vladimir Dementyev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Interested in what we do at &lt;a href="https://www.potloc.com/" rel="noopener noreferrer"&gt;Potloc&lt;/a&gt;? Come join us! &lt;a href="https://jobs.lever.co/Potloc" rel="noopener noreferrer"&gt;We are hiring&lt;/a&gt; 🚀&lt;/em&gt;&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>Streamline a service desk with JIRA automation</title>
      <dc:creator>Clément Morisset</dc:creator>
      <pubDate>Mon, 17 Jan 2022 12:22:38 +0000</pubDate>
      <link>https://dev.to/potloc/streamline-a-service-desk-with-jira-automation-2kfg</link>
      <guid>https://dev.to/potloc/streamline-a-service-desk-with-jira-automation-2kfg</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.potloc.com/"&gt;Potloc&lt;/a&gt; our service desk is dedicated for the operation teams where they can open a ticket for a bug or a service request. The more our web application grows, the more maintenance it requires. As a result we’ve seen a growing number of tickets pertaining to the same project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The need
&lt;/h2&gt;

&lt;p&gt;We wanted a way to link these tickets together to gather additional context.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Were there any issues on this project in the past?&lt;/li&gt;
&lt;li&gt;What was the solution?&lt;/li&gt;
&lt;li&gt;Which developer worked on it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions can be crucial in getting the visibility required when it comes to debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;One word: &lt;code&gt;Automation&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This aptly named feature  allows you to automate  tasks through a  series of actions triggered by specific events.&lt;/p&gt;

&lt;p&gt;Let’s link all created tickets for the same project using automation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zdNkMnGW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6npcw2ihkqzeyjtsirnr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zdNkMnGW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6npcw2ihkqzeyjtsirnr.png" alt="Image description" width="880" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The how
&lt;/h2&gt;

&lt;p&gt;In our particular case the event is &lt;code&gt;When a new issue is created&lt;/code&gt; . The entry point for grouping tickets by a specific project is the url field. Each request has a link to easily locate the issue. In 99% of case our url will have the ID of a project. So the first action that you need to process after the creation of an issue is to scan the URL  in order to extract the project ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Extract the survey ID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can have a hidden field that is not displayed to the user but will allow you to store the extracted ID.&lt;br&gt;
Let's add a &lt;code&gt;New component&lt;/code&gt; &amp;gt; &lt;code&gt;New action&lt;/code&gt; &amp;gt; &lt;code&gt;Edit an issue&lt;/code&gt; and the field you want to use. For the purpose of this article we will use &lt;code&gt;Root cause&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ybHYBSxn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rb23lvyssg0j203c0mjw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ybHYBSxn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rb23lvyssg0j203c0mjw.png" alt="Image description" width="880" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above we are using &lt;code&gt;Smart values&lt;/code&gt;, according to Jira &lt;a href="https://support.atlassian.com/cloud-automation/docs/jira-smart-values-issues/"&gt;documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Smart values allow you to access issue data within Jira. For example, you can use the following smart values to send a Slack message that includes the issue key and issue summary: &lt;code&gt;{{issue.key}} {{issue.summary}}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;customfield_10364&lt;/code&gt;: is the ID for our URL field that contains the project ID. You can go to settings or inspect your input to find it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;substringBetween&lt;/code&gt;: is a &lt;a href="https://support.atlassian.com/cloud-automation/docs/jira-smart-values-text-fields/"&gt;built-in function&lt;/a&gt; that returns the text between the given parameters.&lt;/p&gt;

&lt;p&gt;From this moment your field will contain the ID. But to have an up to date flow (with the extracted ID) we have to refetch the data issue by adding a new action &lt;code&gt;Re-fetch data issue.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, we would like to link issues with the same project ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Find linked issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Jira has an action for this called Lookup issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rF-9vOpj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvcgxdvxqy7lfw62r6jj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rF-9vOpj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvcgxdvxqy7lfw62r6jj.png" alt="Image description" width="880" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cf[10359]&lt;/code&gt; : is the identifier of our &lt;code&gt;Root cause&lt;/code&gt; field (replace the number with your ID)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;issue.customfield_10359&lt;/code&gt; : is the identifier of the &lt;code&gt;Root cause&lt;/code&gt; field for the other issues&lt;/p&gt;

&lt;p&gt;&lt;code&gt;issueKey != "issue.key"&lt;/code&gt; : we exclude our current issue of the lookup&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Careful! Your fields will be named differently according to your need.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What we do, in pseudo code, is basically searching via &lt;code&gt;JQL&lt;/code&gt; if the extracted ID of the issue matches any previously-created issues. We also ensure we exclude the current issue of the search.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: In our context, Root cause is a text field. It's likely that you have to find another operator if the type of your field is different.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Link similar issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finally, we need to link all results from our previous query with our current issue using the &lt;code&gt;Link issue to action&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's it. From now on you should see previously-created tickets for this project on all your tickets.&lt;/p&gt;

&lt;p&gt;Here’s the final result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iFlDjM8V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzc2svacas4682tdvjz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iFlDjM8V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzc2svacas4682tdvjz1.png" alt="Image description" width="476" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e0gWoMod--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3gwfdrl1ne0b4nwibmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e0gWoMod--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r3gwfdrl1ne0b4nwibmp.png" alt="Image description" width="472" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pro tips: You could add a condition as a guard clause to stop the automation if the Lookup issues actions does not return anything.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Happy automation. 🎉&lt;/p&gt;

&lt;p&gt;Interested in what we do at &lt;a href="https://www.potloc.com/"&gt;Potloc&lt;/a&gt;? Come join us! We are &lt;a href="https://jobs.lever.co/Potloc"&gt;hiring&lt;/a&gt; 🚀&lt;/p&gt;

</description>
      <category>jira</category>
      <category>automation</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to use Sentry for profiling a test suite</title>
      <dc:creator>Clément Morisset</dc:creator>
      <pubDate>Mon, 20 Sep 2021 20:11:37 +0000</pubDate>
      <link>https://dev.to/potloc/how-to-use-sentry-for-profiling-a-test-suite-30l7</link>
      <guid>https://dev.to/potloc/how-to-use-sentry-for-profiling-a-test-suite-30l7</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.potloc.com/"&gt;Potloc&lt;/a&gt; one of our core values is learning, that's why each quarter the development team has a dedicated time to explore new things. This aptly named &lt;strong&gt;Dev Happiness Week&lt;/strong&gt; allows us to tackle either a new pattern, open source project, write a blog post and so on.&lt;/p&gt;

&lt;p&gt;One of my initial projects was to monitor our test suite. The more our test suite was growing quickly the more seconds were added to our &lt;code&gt;CI&lt;/code&gt;. But without any monitoring on how long each test takes to run it was complicated to identify and speed up the slowest ones. Let's fix this by asking the following question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;What is the minimal valuable move to have a test profiling dashboard ?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We use &lt;strong&gt;Sentry&lt;/strong&gt; for error tracking and they have a great feature for &lt;a href="https://sentry.io/for/performance/"&gt;performance&lt;/a&gt; monitoring that allow you to track queries and get transactions duration.&lt;/p&gt;

&lt;p&gt;Hence the following steps will show you how to twist the performance overview page of Sentry into a test suite monitoring. 🤓&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimers: The following example assume that you use &lt;code&gt;RSpec&lt;/code&gt;  gem and that you already have a dedicated Sentry environment for it (eg: &lt;code&gt;app-test-suite&lt;/code&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We would like to satisfy three specifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cover all of our test suite&lt;/li&gt;
&lt;li&gt;Don't flood our monitoring with unnecessary data (eg: unit tests that run in under 1 second)&lt;/li&gt;
&lt;li&gt;Make it easy to locate our slower tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cover all of our test suite
&lt;/h3&gt;

&lt;p&gt;The easy one. Let's start by creating a &lt;code&gt;profiling.rb&lt;/code&gt; file that will be run in our test suite, add an &lt;a href="https://relishapp.com/rspec/rspec-core/v/2-0/docs/hooks/around-hooks"&gt;around hook&lt;/a&gt; and compute the elapsed time between the beginning of a run and the end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#spec/support/config/profiling.rb&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;example_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;

    &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

    &lt;span class="n"&gt;example_end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="n"&gt;elapsed_time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example_end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;example_start&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice, we now cover the first bullet point of our specification. &lt;/p&gt;

&lt;h3&gt;
  
  
  Don't flood our monitoring with unnecessary data
&lt;/h3&gt;

&lt;p&gt;The purpose here is to avoiding flooding Sentry when everything work as expected. We just want to send data when a test is slow or takes longer than expected.&lt;/p&gt;

&lt;p&gt;We don't have the same expectations between a system test and a unit test. Hence we have to set a different thresholds accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#spec/support/config/profiling.rb&lt;/span&gt;

&lt;span class="no"&gt;SLOW_EXECUTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="no"&gt;MEDIUM_EXECUTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"integration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"export"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="no"&gt;SLOW_EXECUTION_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="no"&gt;MEDIUM_EXECUTION_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="no"&gt;DEFAULT_EXECUTION_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spec_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;example_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;

    &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

    &lt;span class="n"&gt;example_end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="n"&gt;elapsed_time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example_end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;example_start&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execution_time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Slow alert!"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# input: "./spec/integration/restaurants/owner_spec.rb:16"&lt;/span&gt;
&lt;span class="c1"&gt;# output: "integration"&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;spec_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./spec/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{^[^/]*}&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;threshold_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;SLOW_EXECUTION&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="no"&gt;SLOW_EXECUTION_THRESHOLD&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;MEDIUM_EXECUTION&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="no"&gt;MEDIUM_EXECUTION_THRESHOLD&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="no"&gt;DEFAULT_EXECUTION_THRESHOLD&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Firstly we set our &lt;strong&gt;threshold constants&lt;/strong&gt;, corresponding maximum execution times. &lt;br&gt;
Then we add a &lt;code&gt;spec_category&lt;/code&gt; method that allows us to identify the type of test, and we check if the runtime is lower or higher than expected.&lt;br&gt;
If it does we print a beautiful message.&lt;/p&gt;

&lt;p&gt;We are almost there! We just have to generate a custom Sentry &lt;strong&gt;transaction&lt;/strong&gt; in order to populate the performance table with our profiling data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#spec/support/config/profiling.rb&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spec_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;example_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Sentry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;op: &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;hub: &lt;/span&gt;&lt;span class="no"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_current_hub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execution_time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       
      &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;Sentry::Transaction&lt;/code&gt; takes 2 arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;op&lt;/strong&gt; : used for the name of the &lt;a href="https://github.com/getsentry/sentry-ruby/blob/3ddb146ed2feaf2d2c078c979629ab567a2701d8/sentry-ruby/spec/sentry/transaction_spec.rb#L10"&gt;operation&lt;/a&gt; (eg: &lt;code&gt;sql.query&lt;/code&gt; ). In our case we will populate it with the category of our spec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hub&lt;/strong&gt;: Sentry requires an hub, we use the current. According to &lt;a href="https://docs.sentry.io/platforms/ruby/enriching-events/scopes/"&gt;documentation&lt;/a&gt; &lt;em&gt;"You can think of the hub as the central point that our SDKs use to route an event to Sentry"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By finishing our transaction we send our event to Sentry. From now on you should see your first tests appear on the performance dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G-XPsw-c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmmqxkt0k5u84e1ywz24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G-XPsw-c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmmqxkt0k5u84e1ywz24.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to read Sentry &lt;a href="https://docs.sentry.io/product/performance/metrics/"&gt;documentation&lt;/a&gt; for a better understanding the performance table.&lt;/p&gt;

&lt;p&gt;⚠️ By tweaking the performance dashboard of Sentry for our test suite we have to deal with the expected behaviour of Sentry. Such as send event when there is fail in your environnement&lt;/p&gt;

&lt;p&gt;That's why it's necessary to add the following configuration in our Sentry test initializer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#spec/support/config/sentry.rb&lt;/span&gt;

&lt;span class="no"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://4**************************************3"&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;excluded_exceptions&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Exception"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our context of profiling we won't send an event when a test fails on our &lt;code&gt;CI&lt;/code&gt;. For avoiding noise and flooding you have to exclude all &lt;strong&gt;exceptions&lt;/strong&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Make it easy to locate our slower tests
&lt;/h3&gt;

&lt;p&gt;By default the transaction name corresponds to the name of the controller where the transaction has been run. In the screenshot above it shows that our test uses the method &lt;code&gt;template&lt;/code&gt; in &lt;code&gt;ChartsController&lt;/code&gt;. It's not really convenient to identify what test this concerns.&lt;br&gt;
We need to tweak our transaction a little bit more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#spec/support/config/profiling.rb&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execution_time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
      &lt;span class="n"&gt;assign_custom_transaction_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assign_custom_transaction_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure_scope&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_transaction_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the configuration &lt;a href="https://docs.sentry.io/platforms/ruby/enriching-events/scopes/"&gt;scope&lt;/a&gt; we are able to update our transaction name. According to documentation, &lt;em&gt;"The scope will hold useful information that should be sent along with the event"&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Here we assign our test path as the transaction name. That's it.&lt;br&gt;
Run your test again and you should see&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g_D3xfI1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/esl8bq3d6w1elkkrjonj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g_D3xfI1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/esl8bq3d6w1elkkrjonj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the purpose of development we didn't add a guard clause so far, but you can check if you are on your &lt;code&gt;CI&lt;/code&gt; environment before profiling your tests. &lt;/p&gt;

&lt;p&gt;The entire &lt;code&gt;profiling.rb&lt;/code&gt; class should look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#spec/support/config/profiling.rb&lt;/span&gt;

&lt;span class="no"&gt;SLOW_EXECUTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="no"&gt;MEDIUM_EXECUTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"integration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"export"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;span class="no"&gt;SLOW_EXECUTION_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="no"&gt;MEDIUM_EXECUTION_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="no"&gt;DEFAULT_EXECUTION_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"CI"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spec_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;example_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;

      &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;

      &lt;span class="n"&gt;example_end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
      &lt;span class="n"&gt;elapsed_time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example_end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;example_start&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;execution_time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;threshold_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         
        &lt;span class="n"&gt;assign_custom_transaction_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# input: "./spec/integration/restaurants/owner_spec.rb:16"&lt;/span&gt;
&lt;span class="c1"&gt;# output: "integration"&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;spec_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./spec/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{^[^/]*}&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;threshold_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;SLOW_EXECUTION&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="no"&gt;SLOW_EXECUTION_THRESHOLD&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;MEDIUM_EXECUTION&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="no"&gt;MEDIUM_EXECUTION_THRESHOLD&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="no"&gt;DEFAULT_EXECUTION_THRESHOLD&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assign_custom_transaction_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure_scope&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_transaction_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all, happy profiling ! 🎉&lt;/p&gt;

&lt;p&gt;Interested in what we do at &lt;a href="https://www.potloc.com/"&gt;Potloc&lt;/a&gt;? Come join us! We are &lt;a href="https://jobs.lever.co/Potloc"&gt;hiring&lt;/a&gt; 🚀&lt;/p&gt;

</description>
      <category>sentry</category>
      <category>rspec</category>
      <category>profiling</category>
      <category>ruby</category>
    </item>
  </channel>
</rss>
