<?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: David Jiménez</title>
    <description>The latest articles on DEV Community by David Jiménez (@rydra).</description>
    <link>https://dev.to/rydra</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%2F451554%2F6b0861e7-95ce-417f-8520-0d6f11474b27.jpeg</url>
      <title>DEV Community: David Jiménez</title>
      <link>https://dev.to/rydra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rydra"/>
    <language>en</language>
    <item>
      <title>Reaching consensus and empowering discussions in your technical team</title>
      <dc:creator>David Jiménez</dc:creator>
      <pubDate>Mon, 07 Feb 2022 07:00:02 +0000</pubDate>
      <link>https://dev.to/rydra/reaching-consensus-and-empowering-discussions-in-your-technical-team-hcg</link>
      <guid>https://dev.to/rydra/reaching-consensus-and-empowering-discussions-in-your-technical-team-hcg</guid>
      <description>&lt;p&gt;Technical discussions can (and will) prove challenging and sometimes blocking. Surveying the team is enriching though, and helps with the spread of knowledge, awareness of relevant topics and speeds up decision making.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://planningpoker.com"&gt;Planning Poker&lt;/a&gt; can serve as a tool to poll the team for certain topics, and get to know their opinions quickly while empowering discussions.&lt;/p&gt;

&lt;p&gt;Here’s a screenshot of an example of what such a “survey poker” session could be:&lt;/p&gt;

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

&lt;p&gt;Several topics could be polled with this method. The cards used for such sessions are pretty self-explanatory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Yay&lt;/strong&gt;: “I agree with this topic”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nay&lt;/strong&gt;: “I disagree with this topic”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RFI&lt;/strong&gt;: “Request for Information. I need more information about this topic to make an informed decision“&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STC&lt;/strong&gt;: “Show me The Code“. A variant of “RFI”, you’d like to see code to make an informed decision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POC&lt;/strong&gt;: “Proof of Concept”. I like the topic, let’s put it to the test in our project or in a sample one to make a better informed decision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;?&lt;/strong&gt;: “I have no strong feelings one way or the other/ I don’t care“&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass&lt;/strong&gt;: Equal to ?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon revealing, team members can debate on why they chose a certain card and bring those points/concerns to the rest of the team. Afterwards team members can change their vote, should they will.&lt;/p&gt;

&lt;p&gt;This techniques works even on big teams. Since then you can pass the results to the management team to help them on their decision making.&lt;/p&gt;

&lt;p&gt;Happy voting!&lt;/p&gt;

&lt;h2&gt;
  
  
  Trivia
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This technique is homonymous to the real world referendums.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>teambuilding</category>
      <category>leadership</category>
    </item>
    <item>
      <title>How the Colemak layout distribution and a proper keyboard doubled my coding performance</title>
      <dc:creator>David Jiménez</dc:creator>
      <pubDate>Sat, 24 Jul 2021 07:27:28 +0000</pubDate>
      <link>https://dev.to/rydra/how-colemak-layout-distribution-and-a-proper-keyboard-doubled-my-coding-performance-29mb</link>
      <guid>https://dev.to/rydra/how-colemak-layout-distribution-and-a-proper-keyboard-doubled-my-coding-performance-29mb</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
The Colemak layout

&lt;ul&gt;
&lt;li&gt;Install instructions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Tools of the trade

&lt;ul&gt;
&lt;li&gt;Touchtyping&lt;/li&gt;
&lt;li&gt;Your keyboard&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Conclusions and final tips&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As a developer, I stay long hours in front of a monitor and a keyboard. It's only natural that I want to optimize my workspace and the tools I use. One of those tools is the keyboard. And after experiencing some shoulder and back pain over time, and after seeing how optimizing my usage of the keyboard reduced dramatically this pain trust me: you REALLY want to optimize your usage of the keyboard. And not only that: &lt;strong&gt;my typing speed and performance as a developer more than doubled&lt;/strong&gt; from how I worked before.&lt;/p&gt;

&lt;p&gt;The classical &lt;a href="https://en.wikipedia.org/wiki/QWERTY"&gt;QWERTY&lt;/a&gt; keyboard layout, as well as the staggered, row distribution of the keys date from the XIX century. It was designed to make people type faster with the old typewriters, which had physical limitations. These limitations do not apply anymore with modern computers, yet the layout is still the most mainstream layout that we can find. And not because it's good, but because it's just been with us for so long.&lt;/p&gt;

&lt;p&gt;In this article I want to present you the Colemak layout and several recommendations about your keyboard and your typing style. Everything I comment in this article lead me to the coder typer I am today. It doesn't mean that the same will work for you, but there is a good chance it will as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Colemak layout&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;I'm not gonna go deep into the details of the layout (there are better pages suited for it, like &lt;a href="https://dreymar.colemak.org"&gt;DreymaR's Big Bag of Kbd tricks&lt;/a&gt;), but basically this layout offers several serious advantages over QWERTY:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backspace on the Bloq Mayus key for easy reach of such a common key&lt;/li&gt;
&lt;li&gt;The most frequent letters of English A, R, S, T, and N, E, I, O are placed in the home row, easy to reach. Other most commonly used keys are also placed in a location which is easy to reach by the fingers, while less used keys stay the same.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the letters you press the most are at a closer reach distance your fingers move less, and let you stay in a calm, comfortable position more often, which, even if you don't realize it over time, helps tremendously against most common illnesses derived from long ours in front of a keyboard: carpal tunnel syndrome, back and shoulder pain, wrist pain, fatigue, repetitive strain injury (RSI)...&lt;/p&gt;

&lt;p&gt;Some claim that &lt;a href="http://xahlee.info/kbd/dvorak_vs_colemak.html"&gt;Colemak is not the most efficient layout if you switch language&lt;/a&gt;. But as a developer the language you will encounter most of the time regardless of your mother languange is English, so that's not a real issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install instructions&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The default Colemak that is shipped with the system is good, but the mod DH variation is way, way better in terms of speed (and they only change a couple of keys). Follow the install instructions available on this link, and reach the "DOWNLOAD AND INSTALL" section: &lt;a href="https://forum.colemak.com/topic/1438-dreymars-big-bag-of-keyboard-tricks-linuxxkb-files-included/"&gt;https://forum.colemak.com/topic/1438-dreymars-big-bag-of-keyboard-tricks-linuxxkb-files-included/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools of the trade&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;While switching to Colemak will definitely improve your typing performance, this improvement is crippled and slowed down without the right tools for the job. So besides changing the layout, you need as well to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Master touchtyping
&lt;/li&gt;
&lt;li&gt;Have an appropriate keyboard
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Touchtyping&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;An ELI5 definition I'd give:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Touchtyping is a technique you learn in which you leverage all the fingers of your two hands to type while, at the same time, you do not look at the keyboard with your eyes to search for a certain key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(If you wish a more formal, and more comprehensive explanation, go check &lt;a href="https://en.wikipedia.org/wiki/Touch_typing"&gt;the Wikipedia page&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;And it's a relatively easy skill that you can acquire in one week of intense practice. I gotta say that it's a skill that is a lot easier to acquire and consistently keep if you use an efficient keyboard layout (ahem... colemak) and a good keyboard as I mentioned previously.&lt;/p&gt;

&lt;p&gt;On my learning journey I stumbled across a couple of sites that I can't recommend enough. I have tried many sites, and none of them compare to these two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.keybr.com/"&gt;Keybr.com&lt;/a&gt;: &lt;a href="https://www.keybr.com/"&gt;https://www.keybr.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://typing.io/"&gt;Typing.io&lt;/a&gt;: &lt;a href="https://typing.io/"&gt;https://typing.io/&lt;/a&gt; (suited for developers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with KeyBr and, when you feel good and have acquired some decent speed, go to typing.io.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your keyboard&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

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

&lt;p&gt;Get yourself a columnar mechanical keyboard. Mainstream staggered keyboard which are found everywhere date from the age when the typewriter was the only piece of technology to write with a keyboard. Due to the physical limitations presented by the typebars clashing among each other that those old machines had. Again, these limitations do not apply to modern computers.&lt;/p&gt;

&lt;p&gt;I use an Iris split keyboard like the one shown in the next image. It's extremely comfortable and improves my back posture due to being able to separate the shoulders (note that the key caps are blank, no need to have printed letters on the keys when you touchtype and the memory muscle triggers always).&lt;/p&gt;

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

&lt;p&gt;The next keyboard I have on my purchase list is the Preonic, which has the same key distribution, but it's more compact and ideal for travel and moving around the house.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wfDtHYAr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7yxoj8ctrbgsk0xrpj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wfDtHYAr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7yxoj8ctrbgsk0xrpj2.png" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In all seriousness, if you are a coder your keyboard is your main tool and you want to make sure you have the best tool available to offer the best code you can. Having a mechanical keyboard really improves not only your speed, but lowers the amount of mistakes you make when typing. In fact typing these blog posts is a pleasure with these keyboards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions and final tips&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Changing keyboard layouts as well as keyboard type is a massive change on your workflow; you will start slow, and making a lot of mistakes while your muscle memory builds up. But once you pick up speed, the results are impressive to say the least; It's such a quality of life improvement that I just can't describe with words precisely. I can tell that my programming speed has doubled and, if I spend less time thinking on how to type, I can spend much more time thinking to actually solve the problem at hand.&lt;/p&gt;

&lt;p&gt;And as a bonus, writing in the right way becomes a pleasant, almost relaxing experience.&lt;/p&gt;

&lt;p&gt;My final tips for someone wanting to get his feet wet would be to do, in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the Colemak keyboard distributions on your computer. If you are using a regular, staggered keyboard, use the Curl/Ergo mod variant, since it will put your fingers in a more natural position.&lt;/li&gt;
&lt;li&gt;Start practicing and be patient. It takes about almost week of intense training with &lt;a href="https://www.keybr.com/"&gt;Keybr.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Once you are convinced that Colemak is for you, get a columnar or orthogonal mechanical keyboard, but don't pay more than 200$ for it if possible. You can get a second-hand one from reddit at the &lt;a href="https://www.reddit.com/r/mechmarket"&gt;MechMarket&lt;/a&gt; subreddit. Make your own research though, but I recommend a Preonic, an Iris or a Lily58.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dreymar.colemak.org"&gt;DreymaR's Big Bag of Kbd tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://colemak.com/FAQ"&gt;Colemak official site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://xahlee.info/kbd/dvorak_vs_colemak.html"&gt;Dvorak vs Colemak&lt;/a&gt;(Always good to read people that challenge the Colemak layout and compares it with another popular alternative distribution, Dvorak)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>keyboard</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Getting started on profiling with python</title>
      <dc:creator>David Jiménez</dc:creator>
      <pubDate>Fri, 23 Jul 2021 18:32:32 +0000</pubDate>
      <link>https://dev.to/rydra/getting-started-on-profiling-with-python-3a4</link>
      <guid>https://dev.to/rydra/getting-started-on-profiling-with-python-3a4</guid>
      <description>&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;Some time ago I contributed to &lt;a href="https://github.com/OpenRA/OpenRA"&gt;OpenRA&lt;/a&gt;. OpenRA is an opensource Real-Time strategy game, and one of the most critical operations in this type of games is pathfinding; it needs to work, and it needs to run fast with lots of units moving at the same time each frame. Therefore, it needs to perform well to offer a nice gaming experience to the player.&lt;/p&gt;

&lt;p&gt;Precise performance measurement is an activity present in almost every serious project, not necessarily gaming. For a software engineer the optimization part of algorithms and code in general is where this job gets a good name. And it's a task which important to get comfortable with it. In development there is a saying when implementing a feature: make it work, and then make it better.&lt;/p&gt;

&lt;p&gt;My main daily driver at work is the python language. In this article I will show you some tools I use and some practical simple usages to get you started quickly on the topic of profiling. However, never miss the opportunity to access the official documentation of the tools exposed to fine tune the profiling to your own necessities!&lt;/p&gt;

&lt;h2&gt;
  
  
  The example I'm going to use for all the demonstrations
&lt;/h2&gt;

&lt;p&gt;I'm gonna use as base the same example over all the profiling executions I'm going to show in this article. In short, this &lt;br&gt;
is a unit test from my project &lt;a href="https://github.com/Rydra/jumper-py"&gt;jumper-py&lt;/a&gt;, and it computes, given a map, the shortest path between two nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_astar_intercardinal_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;1&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;2&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="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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="mi"&gt;2&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;walkable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

        &lt;span class="n"&gt;grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;finder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pathfinder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;astar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;walkable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heuristic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cardinal_intercardinal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;finder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotate_grid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;startx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;starty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;endx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
        &lt;span class="n"&gt;agent_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

        &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'x'&lt;/span&gt;
        &lt;span class="n"&gt;marked_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;X&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="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="n"&gt;X&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="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="mi"&gt;0&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="n"&gt;X&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="n"&gt;X&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="mi"&gt;0&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="mi"&gt;2&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="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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X&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="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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;finder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;starty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_assert_the_resulting_path_nodes_should_be_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;marked_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A quick and dirty time measure
&lt;/h2&gt;

&lt;p&gt;Sometimes less is better. A simple time measurement of a function might be more than enough for your needs.&lt;/p&gt;

&lt;p&gt;The context manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;process_time&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;timewith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cpu_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpu_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cpu_time&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;perf_counter&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;cpu_time&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;process_time&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time_func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; and took &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ms"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__enter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__exit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"finished"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="p"&gt;[...]&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;timewith&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  &lt;span class="c1"&gt;# The usage of the context manager
&lt;/span&gt;            &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;finder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;starty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you execute the test, it will output something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;operation finished and took 0.45445101568475366 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quite simple, but as a measurement it can get the job done.&lt;/p&gt;

&lt;h2&gt;
  
  
  cProfile and Yappi decorators
&lt;/h2&gt;

&lt;p&gt;Yappi and cProfile are what they call "deterministic profilers". The main difference between the two is that yappi supports multithreading execution and CPU measurement besides "Clock wall" measurement. These profilers are very detailed and provide a lot of information of the profiled code. Very useful.&lt;/p&gt;

&lt;p&gt;The context manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cProfile&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;contextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;process_time&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;yappi&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;contextmanager&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'results'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'yappi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'wall'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'pstat'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'yappi'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'profiling/results/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.prof'&lt;/span&gt;
        &lt;span class="n"&gt;yappi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_clock_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;yappi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builtins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;profile_threads&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;yappi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yappi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_func_stats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;output_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;disable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_stats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'profiling/results/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.prof'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adjust the variables to your own needs. Check the documentation for the respective projects.&lt;/p&gt;

&lt;p&gt;Sample usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="p"&gt;[...]&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  &lt;span class="c1"&gt;# The usage of the context manager
&lt;/span&gt;            &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;finder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;starty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, you can visualize the profiling results with a tool like kcachegrind or snakeviz.&lt;/p&gt;

&lt;p&gt;Display of the results with kcachegrind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pyprof2calltree -i profiling/results/results.prof -k
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Display of the results with snakeviz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;snakeviz profiling/results/results.prof
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Snakeviz is very visual, displaying each function in a different size in proportion to the time spent running it. Even then, I miss the display of more information in the icicle graph like the total number of calls each function performs to another function, like Kcachegrind does. You can refer for these values in the table that Snakeviz prints below the graphic though, but it would had been nice to include them. Maybe a function takes longer to execute not because it's slow per se, but because it's been called too many times unnecessarily, and that thing is not made explicit on the graph. A pity.&lt;/p&gt;

&lt;p&gt;This is why I usually use both tools when optimizing code. Snakeviz for having a quick, general overview of the code execution and, if I need more information and call details, I switch to Kcachegrind. I still haven't found the "profiling visualizer to rule them all".&lt;/p&gt;

&lt;p&gt;Worth mentioning too, you can use gprof2dot and xdot to have a quick glance at the call tree of your profile function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gprof2dot -f pstats profiling/results/results.prof -o profiling/results/results.dot
xdot profiling/results/results.dot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  PyInstrument
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/joerick/pyinstrument"&gt;PyInstrument&lt;/a&gt;, compared to cProfile or Yappi, it's a lot easier to use and requires much less configuration.&lt;/p&gt;

&lt;p&gt;With some small tweaking, we can take the &lt;a href="https://github.com/joerick/pyinstrument#profile-a-specific-chunk-of-code"&gt;code example&lt;/a&gt; from their documentation and write a decorator that we can easily place wherever we need to run this profiling.&lt;/p&gt;

&lt;p&gt;The context manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pyinstrument&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Profiler&lt;/span&gt;


&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;contextmanager&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'results'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;profiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Profiler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"profiling/results/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;profiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;profiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_html&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample usage (test from my project &lt;a href="https://github.com/Rydra/jumper-py"&gt;jumper-py&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="p"&gt;[...]&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  &lt;span class="c1"&gt;# The usage of the context manager
&lt;/span&gt;            &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;finder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;starty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="p"&gt;[...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Screenshot of the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xdg-open profiling/results/results.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;As you can appreciate, it quickly gives you information on how much time it has spent on each function so you can quickly focus your attention on what is important.&lt;/p&gt;

&lt;p&gt;Bear in mind though, pyInstrument does not replace other types of profiling, since it has a lower level of detail than cProfile or Yappi. But it's a really good starting point for your profiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Profiling is a crucial skill for any developer worth its salt. Maybe it's not something that you will use every day unless your business has high performance requirements, but it's something you can often rely on when "inexplicably" your application runs slower and you don't know why. And, eventually, you will cross paths which such problems, either in the form of algorithm optimization (like pathfinding algorithms) or in web development, helping you finding out why you're making so many database calls and give you hints on which ORM calls could be optimized.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uwpce-pythoncert.github.io/Py300/profiling.html#today-s-topics"&gt;https://uwpce-pythoncert.github.io/Py300/profiling.html#today-s-topics&lt;/a&gt;&lt;br&gt;
&lt;a href="https://pythonspeed.com/articles/beyond-cprofile/"&gt;https://pythonspeed.com/articles/beyond-cprofile/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://blog.blackfire.io/profiling-101-for-python-developers-existing-profilers-3-6.html"&gt;https://blog.blackfire.io/profiling-101-for-python-developers-existing-profilers-3-6.html&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Techniques to declare settings in a third party Django library</title>
      <dc:creator>David Jiménez</dc:creator>
      <pubDate>Fri, 23 Jul 2021 17:59:07 +0000</pubDate>
      <link>https://dev.to/rydra/techniques-to-declare-settings-in-a-third-party-django-library-52ai</link>
      <guid>https://dev.to/rydra/techniques-to-declare-settings-in-a-third-party-django-library-52ai</guid>
      <description>&lt;p&gt;Following up my article &lt;a href="https://dev.to/rydra/how-to-set-up-and-run-migrations-in-third-party-django-packages-44ln"&gt;How to set up and run migrations in third-party Django packages&lt;/a&gt;, another question I faced when developing third-party Django libraries was: how can/should I implement the settings for the application in a decoupled and reusable manner, while allowing users of my library configure the settings?&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem of using &lt;code&gt;django.conf.settings&lt;/code&gt; directly in a third-party library
&lt;/h2&gt;

&lt;p&gt;In Django, whenever you need to access a setting, unless you wrap them in a class, you have to import the &lt;code&gt;settings&lt;/code&gt; module from &lt;code&gt;django.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MULTIPLICATION_FACTOR&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can I you use this import inside your third party library? Sure, you could. However this poses three problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You need to document that the developer using your library &lt;strong&gt;MUST&lt;/strong&gt; specify the &lt;code&gt;MULTIPLICATION_FACTOR&lt;/code&gt;. Otherwise, codes like the previous example will crash.&lt;/li&gt;
&lt;li&gt;You cannot define a default value for the variables of your library.&lt;/li&gt;
&lt;li&gt;And again, unless you document your library very well, the settings your library uses are scattered everywhere, making it difficult to identify what I can customize.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A proposed better solution: the wrapper class
&lt;/h2&gt;

&lt;p&gt;This is a solution I found when looking at the source code of the well known package &lt;a href="https://github.com/adamchainz/django-cors-headers"&gt;Django Corsheaders&lt;/a&gt;. It is very, very simple. Just define a wrapper over the &lt;code&gt;django.conf.settings&lt;/code&gt;, and specify a property per each setting that your library allows to customize.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;MULTIPLICATION_FACTOR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MULTIPLICATION_FACTOR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we would modify the first code example like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;your_django_module.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MULTIPLICATION_FACTOR&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From that point forward, every time I would need a setting in my third-party library I would use that alternative import. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Settings&lt;/code&gt; class and the &lt;code&gt;MULTIPLICATION_FACTOR&lt;/code&gt; property are quite straightforward to explain: it checks if the user has defined an override for the settings called "MULTIPLICATION_FACTOR". If he/she did, take that value, otherwise, default to 2.&lt;/p&gt;

&lt;p&gt;This simple trick decouples you furthermore from the Django infrastructure, which promotes cleaner and more reusable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternative technique
&lt;/h2&gt;

&lt;p&gt;I'm not going to go in-depth with it, but I've seen a different technique used to achieve the same result in the &lt;a href="https://github.com/jazzband/djangorestframework-simplejwt"&gt;SimpleJWT project&lt;/a&gt; that can be found in &lt;a href="https://github.com/jazzband/djangorestframework-simplejwt/blob/master/rest_framework_simplejwt/settings.py"&gt;this file in the source code&lt;/a&gt;. Basically the technique relies on the &lt;code&gt;settings_changed&lt;/code&gt; signal to establish default values for settings and update those settings in the context of the package. I'm not sure if there is a massive improvement on this approach over the previous one (maybe caching?), but if there is, feel free to comment.&lt;/p&gt;

</description>
      <category>django</category>
      <category>settings</category>
      <category>thirdparty</category>
    </item>
    <item>
      <title>How to set up and run migrations in third-party Django packages</title>
      <dc:creator>David Jiménez</dc:creator>
      <pubDate>Thu, 22 Jul 2021 17:02:10 +0000</pubDate>
      <link>https://dev.to/rydra/how-to-set-up-and-run-migrations-in-third-party-django-packages-44ln</link>
      <guid>https://dev.to/rydra/how-to-set-up-and-run-migrations-in-third-party-django-packages-44ln</guid>
      <description>&lt;p&gt;Creating a simple &lt;strong&gt;python&lt;/strong&gt; package is a quite easy endeavor: create an empty project, create a module, put content inside, package it and done, you have a fully fledged package. However, creating a package that needs to integrate with &lt;strong&gt;Django&lt;/strong&gt; is not that easy; you need to make sure that the models, migrations, default settings, etc... are in the correct place in order to flawlessly integrate without much pain.&lt;/p&gt;

&lt;p&gt;This article will show you a way to run the Django migrations for your third party packages, decoupled from any external dependency. If any of you knows a simpler solution than the one exposed here, I will appreciate it so much!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DISCLAIMER: This guide assumes that some concepts and terms related to Django like migrations and settings are known by the reader.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;p&gt;Django is a dependency of any third party django package, so you'll have to declare it as such (with poetry, pipenv...)&lt;/p&gt;

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

&lt;p&gt;You placed your Django Models in a &lt;code&gt;models.py&lt;/code&gt;, but since you're implementing this third party library outside of the context of your monolith, there is no &lt;code&gt;manage.py&lt;/code&gt; to be found. But you want to run your migrations!&lt;/p&gt;

&lt;p&gt;Then you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a python package folder called &lt;code&gt;migrations&lt;/code&gt; inside your django package. Once you successfully run the migrations, they will be placed here.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;scripts&lt;/code&gt; folder on the root of the project.

&lt;ul&gt;
&lt;li&gt;Inside this folder, create a &lt;code&gt;makemigrations.py&lt;/code&gt; file. Put this content inside it. With that you'll be able to run the makemigrations script:
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"DJANGO_SETTINGS_MODULE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"scripts.mock_settings"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.management&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;execute_from_command_line&lt;/span&gt;

    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"makemigrations"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;execute_from_command_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add a &lt;code&gt;mock_settings.py&lt;/code&gt; too. This file is necessary for the &lt;code&gt;makemigrations&lt;/code&gt; command since any Django execution (and makemigrations is) requires a settings file. However, since we only need it to generate the migrations we can have a sample and dumb settings file just to do this operation. Put this content in the file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'Not a secret key at all, actually'&lt;/span&gt;
&lt;span class="n"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="n"&gt;DATABASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ENGINE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"django.db.backends.postgresql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"NAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"USER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"HOST"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"60000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# Django apps necessary to run the admin site
&lt;/span&gt;    &lt;span class="s"&gt;'django.contrib.auth'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.contenttypes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.sessions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.staticfiles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.messages'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;# And this app
&lt;/span&gt;    &lt;span class="s"&gt;'your_django_lib'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;STATIC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'/static/'&lt;/span&gt;
&lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.auth.middleware.AuthenticationMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.messages.middleware.MessageMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.sessions.middleware.SessionMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;TEMPLATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'BACKEND'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'django.template.backends.django.DjangoTemplates'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'OPTIONS'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;'context_processors'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s"&gt;'django.contrib.auth.context_processors.auth'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;'django.contrib.messages.context_processors.messages'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: Change this mock settings depending on your necessities. Instead of having to configure a real postgres database you could just change de &lt;code&gt;DATABASE&lt;/code&gt; settings and configure a sqlite database. However sqlite does not support the likes of the JSONField, and migrations with JSONFields won't work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need to spin up a real database just for the sake of migrations, you could use docker-compose. Use the following minimal setup:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.6'

services:
  postgres:
    image: postgres:9.5-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - 60000:5432
    expose:
      - "5432"
    restart: always
    environment:
      POSTGRES_PASSWORD: example

volumes:
  postgres_data:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end, the minimal structure for this to work would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-django-lib/
├─ your_django_lib/
│  ├─ migrations/
│  │  ├─ __init__.py
│  ├─ __init__.py
│  ├─ models.py
├─ scripts/
│  ├─ __init__.py
│  ├─ mock_settings.py
│  ├─ makemigrations.py
├─ .gitignore
├─ docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run the &lt;code&gt;makemigrations&lt;/code&gt; script. Depending on your virtual environment you'll have to execute the command in a different manner. With &lt;a href="https://python-poetry.org/"&gt;poetry&lt;/a&gt; for example you'd run it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;poetry run python scripts/makemigrations.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! It's been tough, but now you can run your migrations decoupled from any infrastructure or external Django dependencies! Now you should have your model migrations in the migrations folder!&lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
    </item>
    <item>
      <title>Ease your development hurdles with these [M]ake recipes</title>
      <dc:creator>David Jiménez</dc:creator>
      <pubDate>Wed, 21 Jul 2021 07:07:21 +0000</pubDate>
      <link>https://dev.to/rydra/ease-your-development-hurdles-with-these-m-ake-recipes-5d4m</link>
      <guid>https://dev.to/rydra/ease-your-development-hurdles-with-these-m-ake-recipes-5d4m</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Some Recipes/Tasks for your backend projects

&lt;ol&gt;
&lt;li&gt;Build the project&lt;/li&gt;
&lt;li&gt;Clean up the workspace&lt;/li&gt;
&lt;li&gt;Run the project&lt;/li&gt;
&lt;li&gt;Prepare the local environment for development&lt;/li&gt;
&lt;li&gt;Reset the database&lt;/li&gt;
&lt;li&gt;Freeze and unfreeze the state of the database&lt;/li&gt;
&lt;li&gt;Update non-local environments&lt;/li&gt;
&lt;li&gt;File generation scripts&lt;/li&gt;
&lt;li&gt;Run arbitrary command&lt;/li&gt;
&lt;li&gt;Migrate database&lt;/li&gt;
&lt;li&gt;Check code&lt;/li&gt;
&lt;li&gt;Bump version&lt;/li&gt;
&lt;li&gt;Run unit/acceptance tests&lt;/li&gt;
&lt;li&gt;Open the profiling tool&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;Conclusions&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Introduction&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Make&lt;/em&gt; and other similar expert/automation systems like &lt;em&gt;PyInvoke&lt;/em&gt; (I highly love this one over other build systems, even over Makefile), &lt;em&gt;Rake&lt;/em&gt;, &lt;em&gt;Cake&lt;/em&gt;, etc... avoid the necessity of having a bunch of scripts (bash, python...) to perform certain repetitive tasks. Even then, you often chain them in succession to achieve a certain goal (e.g. you know you have to run &lt;em&gt;script_1.sh&lt;/em&gt; before &lt;em&gt;script_2.sh&lt;/em&gt;, but that dependency is probably not made explicit in the project file structure). These dependencies are difficult to remember as they become more and more complex.&lt;/p&gt;

&lt;p&gt;Or you might install a whole bunch of other tools for your project: &lt;em&gt;poetry&lt;/em&gt;, &lt;em&gt;gulp&lt;/em&gt; or docker just to name a few. Eventually recalling the arguments required to perform a certain action becomes difficult. &lt;strong&gt;But it's never difficult to recall the action you need to perform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dumb metaphor: It's easier to remember that you need an spanish omelette than remember that you need eggs, potatoes, onions and salt to build such delicious dish. Wouldn't it be easier if you had the power to imagine an omelette and PUFF!, it materializes in front of you, than building the omelette by yourself each and every time? That's where &lt;code&gt;make&lt;/code&gt; and other derivatives can help you.&lt;/p&gt;

&lt;p&gt;Here I present you with several suggested recipes that I use on a daily basis on the backend projects I have been working. The example are expressed in &lt;em&gt;Makefile&lt;/em&gt; syntax, but the very idea can be applied to every build system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Recipes/Tasks for your backend projects&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build the project (&lt;code&gt;make build&lt;/code&gt;)  &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Pretty self explanatory. Every project of a reasonable size and maturity needs to be built, either by compilation, building the docker image, you name it. Therefore, the responsibility of this recipe should be to build/update the project and leave it in a runnable state. Optionally it could run migrations as well to keep the database schema up to date.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
        # Put here the commands to compile, install dependencies, build docker images...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clean up the workspace (&lt;code&gt;make clean&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This one is the inverse of build. Its responsibility is to clean up the docker images, volumes, generated files... created by the build command.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clean:
        # Clean docker images, dist files, etc...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run the project (&lt;code&gt;make run&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This one runs the project. Prior to spinning up the application (running &lt;code&gt;docker-compose up&lt;/code&gt;, executing the main script, etc.), it could prepare the environment to ensure that the project starts up successfully: invalidating caches, creating an AWS session, running database migrations, etc...&lt;/p&gt;

&lt;p&gt;This is the command we tell the frontend team to run after building the application with &lt;code&gt;make init&lt;/code&gt; to spin up the web server for theirs to run their HTTP requests. &lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run:
        # Run your project here

stop:
        # In the same sense that there is a run, it might be interesting for you to have a stop command that kills the process/es that starts up the `run` command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make run
make stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prepare the local environment for development (&lt;code&gt;make setup-dev&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This one is intended for those that are going to do development over the project. It installs dependencies locally, set up pre-commit hooks, etc... basically setup and install anything required for local development.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;setup-dev:
        # Run pre-commit scripts, install dependencies locally...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make setup-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reset the database (&lt;code&gt;make reset&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Eventually and through your testing or the testing of your colleague developers, the database is going to be heavily polluted, and this command should reset the database to &lt;code&gt;factory settings&lt;/code&gt; (destroying and creating the local database, re-running the initial data migrations, etc.)&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reset: down
        # Run db scripts to purge the DB/s, remove docker volumes...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make reset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Freeze and unfreeze the state of the database (&lt;code&gt;make freeze&lt;/code&gt; and &lt;code&gt;make unfreeze&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I came up with these after some people of my team suggested that it would be cool, from a testing perspective, if we could save a snapshot of the database and then restore it. It proved very useful.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Example with postgres, leveraging docker

freeze: down
        docker run -v my_postgres_vol:/volume -v /tmp:/backup --rm loomchild/volume-backup backup postgres

unfreeze: down
    docker run -v my_postgres_vol:/volume -v /tmp:/backup --rm loomchild/volume-backup restore postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make freeze
make unfreeze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update non-local environments like DEV/TEST/UAT/PROD (&lt;code&gt;make update-remote&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Often non-local environments have different characteristics than your machine (they might not run via docker, for example). Having a recipe to handle migrations and updates to those environments is very useful to avoid forgetting to run something.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;update-remote:
        # call here the commands that are intended to be called when you deploy to your remote environment: Database migrations, cache invalidations, scripts to fix something...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make update-remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  File generation scripts (&lt;code&gt;make generate-XXXX&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Sometimes you might need to generate files in your code (documentation, protobuf code, etc...). These kind of commands are intended for such tasks.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generate-docs:
        # call your doc generation tool (doxygen, sphynx...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make generate-docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run arbitrary command (&lt;code&gt;make run-command&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I work with Django (but this probably applies to other frameworks as well). And wrapping these commands into a recipe is often a good idea to keep the execution centralized.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run-command:
        # e.g. python src/manage.py ($cmd)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make run-command cmd="XXXXX"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Migrate database (&lt;code&gt;make migrate&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This recipe usually is a dependency of another of the previously mentioned recipes like &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;up&lt;/code&gt;. As the name implies, it applies the migrations to the database.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;migrate:
        # Run here the command to execute the migrations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check code (&lt;code&gt;make check&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Any serious project run some kind of code formatting tool to comply with your company coding guidelines and best practices. This command performs these checks on the code.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;check:
    # Example leveraging pre-commit
    pre-commit run --all-files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bump version (&lt;code&gt;make bump-version LEVEL=patch|minor|major&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This recipe updates the version of your project. Each time I commit code the pre-commit hook calls this command with the &lt;code&gt;patch&lt;/code&gt; argument, and each time I deploy to production I manually call it with the &lt;code&gt;minor|major&lt;/code&gt; argument depending on the type of code change I'm conducting.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bump-patch:
    # Example with bump2version
    poetry run bump2version $(LEVEL)
    git add _version.py
    git add .bumpversion.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make bump-patch LEVEL=patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run unit/acceptance tests (&lt;code&gt;make run-tests&lt;/code&gt; and &lt;code&gt;make run-acceptancetests&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This recipe runs the tests of my application with some default parameters I like to establish. Sometimes I create other similar recipes to run these very tests but with some quirks, like generating coverage and result reports when running.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run-tests:
        # Call here the command to execute your tests: py.test, NUnit... just make sure that the environment variables needed by your tests are loaded. This is why I like to run my tests with docker compose!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make run-tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Open the profiling tool (&lt;code&gt;make open-profiling&lt;/code&gt;)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;You might have been profiling some code to find bottlenecks in your application and generated a report you'd like to visualize with tools like &lt;code&gt;snakeviz&lt;/code&gt; or &lt;code&gt;pyprof2calltree&lt;/code&gt;. This command will open the latest report generated, ready for visualization.&lt;/p&gt;

&lt;p&gt;Make recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;open-profiling:
        # Example of command, adjust to your preferred visual tool
    pyprof2calltree -i profiling/results/$(FILE).prof -k
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make open-profiling FILE=results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I exposed several recipes that I found useful when developing. Even though I gave some clues of the tooling you could potentially use, it's impossible to provide a detailed implementation since it wildly depends on the programming language you use, the environment, etc... However, what's really important about each recipe is the main objective it tries to achieve, and that's a fact (building the project, running tests...).&lt;/p&gt;

&lt;p&gt;I'd like to hear from other fellow developers what other recipes you have come up with that could make our lives a little bit easier. If you have any to share please, feel free to contribute in the comments section!&lt;/p&gt;

</description>
      <category>make</category>
      <category>automation</category>
      <category>commands</category>
      <category>development</category>
    </item>
    <item>
      <title>Lessons learned after being a Tech Lead for three years</title>
      <dc:creator>David Jiménez</dc:creator>
      <pubDate>Mon, 19 Jul 2021 18:48:18 +0000</pubDate>
      <link>https://dev.to/rydra/lessons-learned-after-being-a-tech-lead-for-three-years-3na2</link>
      <guid>https://dev.to/rydra/lessons-learned-after-being-a-tech-lead-for-three-years-3na2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A great man doesn't seek to lead: he's called to it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;- Duke Leto, Dune -&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After being a technical lead for three years in my current company I decided it was a good time to retrospect on it and share my experience and conclusions. I hope you find my experiences helpful.&lt;/p&gt;

&lt;p&gt;Disclaimer: Any statement here are solely based on my experiences and knowledge. I am by no means a Leadership Coach so some conclusions I provide might be clash with some established ideas and conceptions. And that's ok, challenging what's established is always enriching!&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We are developers. By default we are not the most charismatic people in the world (this is why we are developers after all, otherwise we might have picked another less solitary profession). However, I felt like working up and improving certain soft skills at the same pace as technical skills could boost my career. I found the chance to be on that role and I took it. And I never could have been more certain.&lt;/p&gt;

&lt;p&gt;During these years I learnt a lot. I made mistakes. I messed up. And on occasions maybe, and just maybe, I was right.&lt;/p&gt;

&lt;p&gt;I'm going to highlight those aspects of the tech lead role I found the most challenging and important, and most of them, surprisingly, are not especially tech-related; most of them refer to communication with your team and behavioral aspects.&lt;/p&gt;

&lt;p&gt;The topics I'm going to pinpoint in this article are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Communication and self-retrospection&lt;/li&gt;
&lt;li&gt;You are a selfless enabler&lt;/li&gt;
&lt;li&gt;"The teacher"&lt;/li&gt;
&lt;li&gt;It's not about being bossy&lt;/li&gt;
&lt;li&gt;Manners and feedback&lt;/li&gt;
&lt;li&gt;It's good to be a point of technical reference, but it's not mandatory&lt;/li&gt;
&lt;li&gt;Share ownership&lt;/li&gt;
&lt;li&gt;Ultimately, it's about being an actual leader&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Communication and self-retrospection &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It looks pretty obvious, but communicating effectively is an essential part of being a tech lead. You will have to talk with a lot of people, from developers to clients, crossing product owners and testers in the path. You don't necessarily need to be always talkative, it is way more important to listen and understand foreign ideas while realizing that not everybody will understand your technical jargon. &lt;/p&gt;

&lt;p&gt;When communicating with your team, sometimes you might be a grumpy furious monkey without being aware of it. This is why, from time to time, you should sit down and look at yourself from the outside in order to find gaps in your communication skills or behavior. Even when writing these lines I'm just realizing some communication mistakes I did several days ago which I was completely oblivious.&lt;/p&gt;

&lt;h2&gt;
  
  
  You are a selfless enabler &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Tech Lead is a role meant to be selfless. Yes, we are all busy with our own share of work. But after all your teammates and you navigate in the same direction inside the context of the project. I like to think about it in an epic manner where you are a Spartan hoplite carrying a spear and a shield, but your shield is not meant to protect you, but your comrade at the left.&lt;/p&gt;

&lt;p&gt;Is a fellow developer blocked and you can help? Go help him ASAP and, if you cannot do it right now for whatever reason, kindly tell him/her to meet up at a &lt;strong&gt;specific&lt;/strong&gt;, later point in time to tackle the issue (note the word &lt;strong&gt;specific&lt;/strong&gt;) or delegate the responsibility to another team member.&lt;/p&gt;

&lt;p&gt;Neither you should hoard knowledge. I remember a debate with a former colleague from another company where he defended that, if you are good, it's better to keep your knowledge and best practices for yourself, with the statement that most people he shared his knowledge with were ungrateful and took advantage of that knowledge to step over him. Clearly a resentment from a past experience that sometimes gives you a lesson learned that, when you ponder about it with a good heart you think to yourself "that's a sad way to think about it".&lt;/p&gt;

&lt;p&gt;Even if you had any experience like that, I prefer to "flow in the good, selfless direction". Share your knowledge and eagerness to help by default, and if people are ungrateful and no one appreciates it, either you should reflect on how you are providing this help or maybe, just maybe, it could be time to move on to another job/place (yes, there are workplaces which are utter shitholes).&lt;/p&gt;

&lt;h2&gt;
  
  
  "The teacher" &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This follows up a little bit the last statements of the previous section. Another of the roles I have at my current company is that of a "Technical Coach", bringing the more junior employees or new recruits up to speed on the project regarding the architecture of the software solution, best practices, coding techniques, etc.&lt;/p&gt;

&lt;p&gt;When I was at university, I wanted to be a professor there, teaching Software Engineering to students, sharing my knowledge while researching something awesome. I just loved that idea, and I studied a master oriented towards a Ph.D. on the area of Software Engineering.&lt;/p&gt;

&lt;p&gt;So, when I receive a question from one of my fellow dev mates, this part of me triggers and I just fire the cannon of knowledge and experience on the topic relentlessly, even when unrequested. Not all people are equal though, and while some will appreciate your wisdom, others will frown upon that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You do not have to always be "the teacher"&lt;/strong&gt;. It's a bit like the classical advice you are given when flirting in your early 20's: do not go all out unless you are requested so and keep a juicy bait on the hook.&lt;/p&gt;

&lt;p&gt;Even then, I have a personal conflict over this. I like the TV show &lt;em&gt;Avatar: The last airbender&lt;/em&gt;, and one of my favorite characters is Uncle Iroh, the uncle of the main antagonist of the show. He spreads wisdom everywhere, even if his own nephew doesn't want it. Still, I love Iroh at heart. I would love any reader to share his opinionated view on this.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not about being bossy &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The role is not meant to exercise authority and supremacy over others. I like flat hierarchical structures, since it promotes the feeling of ownership of the solution and the flowing of ideas from all directions in a team. Even though formally in your contract you are the technical lead, you are still one more developer and you must consider your fellow teammates as an equal.&lt;/p&gt;

&lt;p&gt;Yes, as the tech lead you might be the only technical guy from your team invited to certain meetings for the sake of keeping noise under control. But that doesn't mean that you belong to the Bilderberg Club. Try to act as a voice and speaker for your team, not yourself.&lt;/p&gt;

&lt;p&gt;A subtle, yet significant change I noted and changed: When introducing myself to either new job candidates at technical interviews or clients, I started stating my role as the Technical Lead of the development team. Recently, for the sake of that flat hierarchy inside the team, I changed that and started introducing myself simply as a software engineer or backend developer. It made me realize it's just not necessary to boast yourself at the slightest opportunity (unless by protocol they need you to introduce yourself as such)&lt;/p&gt;

&lt;p&gt;This brought me to the following conclusion: Humility is a good quality for a leader. Your contributions to the conversations, your communication skills and your confidence will eventually give a clue to the rest of the team who is the leader.&lt;/p&gt;

&lt;p&gt;As I was writing this though, a thought clashed my mind. In this era of big CEOs on big companies or big personalities where everybody thinks of them as true leaders, I perceive most of them are everything but humble. Just one more example that sometimes statements and lessons learned depend on perception. &lt;/p&gt;

&lt;h2&gt;
  
  
  Manners and feedback &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Yes, you and your teammates are going to screw up. Someone will not follow gitflow to the line and have a polluted remote git repository. Some code will make you pour blood from your eyes when reviewing a pull request.&lt;/p&gt;

&lt;p&gt;There are people, however, willing to improve and listen to feedback. If you find those, kindly address them and provide them with constructive feedback. People will appreciate it. Just remember this quote from Lincoln when giving "constructive feedback": "A drop of honey catches more flies than a gallon of gall".&lt;/p&gt;

&lt;p&gt;AND REMEMBER, the same works the other way around. Feedback is a gift, a very difficult gift someone gives to you (it's always way easier to stay silent and let people fail). Take the time to appreciate it if you receive some.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's good to be a point of technical reference, but it's not mandatory &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;There are a number of reasons you might have obtained the role of tech lead of a team: it may have been your technical prowess, your communication skills, a mix of both...&lt;/p&gt;

&lt;p&gt;Remember though that you are not a walking encyclopedia: you do not know everything, nor you should force yourself to attain such state, nor you should look pedantic. It's ok to admit you don't know something. If a teammate is blocked though and you are required to solve a certain problem do what any other developer worth their salt would do: research, test and solve it.&lt;/p&gt;

&lt;p&gt;Neither you need to be the role-model or that steadfast statue one-hundred percent of the time. We are humans after all: we laugh, we explain bad jokes that make our mates either fall from the chair or frown upon us, we may have bad days. Showing weakness it's ok (and in fact, deeply, people appreciate it, it makes you more approachable than keeping the barrier up all the time), just make sure you always keep cool and don't let any storm you generate fall upon your teammates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share ownership &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As a tech lead, the definition of the architecture and implementation of parts of the software system are your responsibility. But that doesn't mean that you should define it on your own. It's a good idea to build something considering the input of your team. It's easier for developers to build something if they contributed to the solution actively.&lt;/p&gt;

&lt;p&gt;Do not be afraid to share ownership and collaborate with others to build a software solution. They are not paying you to be a hero. While making developers feel like the project is their child is cool, it's actually not yours unless you are the owner of the company. They are paying you to promote best development practices inside the team, coach junior members and new recruits, and offer a working, maintainable solution to the problem at hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ultimately, it's about being an actual leader&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Even though your contract might state the word "lead", it's not enough. &lt;strong&gt;A leader is someone that people follow by their own will&lt;/strong&gt;. Remember that a leader that people follow by force is not a leader, but a mere boss.&lt;/p&gt;

&lt;p&gt;And trust me, it's way, way, way more rewarding for everybody if people follow you because they want to. Not everybody is going to like you and that's ok, try though that statistically most people like you or retrospect about why it's not that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I'm a developer at the core, but having had the opportunity to develop a good share of soft skills in the shape of technical leadership has been one of the most rewarding and personality-bending steps I ever took on my career. It has changed me for good: it boosted my communication skills both inside and outside the technical team and gave me the chance to be able to share the joy of facing new technical challenges with my team. &lt;/p&gt;

&lt;p&gt;Nevertheless, being a technical lead (in fact, any leading position in general, not necessarily technical) is deeply bound to your own personality. If your personality clashes with some of the aspects required for the role (you're selfish and have a boundless evil ambition, or you prioritize yourself over your teammates), you'll eventually show up your true colors. So TL;DR: don't be an asshole.&lt;/p&gt;

</description>
      <category>leadership</category>
    </item>
  </channel>
</rss>
