<?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: Rémy 🤖</title>
    <description>The latest articles on DEV Community by Rémy 🤖 (@xowap).</description>
    <link>https://dev.to/xowap</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%2F3256%2Fde2f8a41-dad0-43cd-932e-9255cf615a81.jpg</url>
      <title>DEV Community: Rémy 🤖</title>
      <link>https://dev.to/xowap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xowap"/>
    <language>en</language>
    <item>
      <title>The Ultimate Python main()</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Wed, 12 May 2021 21:55:36 +0000</pubDate>
      <link>https://dev.to/xowap/the-ultimate-python-main-18kn</link>
      <guid>https://dev.to/xowap/the-ultimate-python-main-18kn</guid>
      <description>&lt;p&gt;So you want to write a CLI utility in Python. The goal is to be able to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./say_something.py -w "hello, world"
hello, world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then to get your script running. This isn't so hard and you've probably done it already, but have you thought of all the use cases? Let's go step by step on all the things you can do to make your &lt;code&gt;main()&lt;/code&gt; bullet-proof. The first sections are going to be very basic and we'll build up something less obvious as we progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello, world
&lt;/h2&gt;

&lt;p&gt;Let's start with a basic script that just says "hello, wold".&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this is fairly basic but still won't work if you call it directly like &lt;code&gt;./say_something.py&lt;/code&gt;. In order for this to work, you need to add the famous shebang at the beginning.&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="c1"&gt;#!/usr/bin/python3
&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — Let's also not forget to do a &lt;code&gt;chmod a+x say_something.py&lt;/code&gt; to give execution rights on the file&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, what if you're using a virtualenv? By calling &lt;code&gt;/usr/bin/python3&lt;/code&gt; directly you're forcing the path to the Python interpreter. This can be desirable in some cases (by example package managers will tend to force this for safety) but if you're writing a distributable script it's simpler to get it from the environment.&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — We're calling &lt;code&gt;python3&lt;/code&gt; instead of &lt;code&gt;python&lt;/code&gt; because in some systems you'll find that &lt;code&gt;python&lt;/code&gt; points to Python 2 and who wants to be using Python 2?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Importability
&lt;/h2&gt;

&lt;p&gt;Something that you'll learn early on from manuals is that you want to make sure that if for some reason someone imports your module then there won't be any side-effect like a surprise &lt;code&gt;print()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because of that, you need to "protect" your code with this special idiom:&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="c1"&gt;#!/usr/bin/env python3
&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="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll note the use of the special variable &lt;code&gt;__name__&lt;/code&gt; whose value is going to be &lt;code&gt;__main__&lt;/code&gt; if you're in the "root" file that Python is running and the module name otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing arguments
&lt;/h2&gt;

&lt;p&gt;You'll notice that so far the program only says "hello, world" but doesn't parse the &lt;code&gt;-w&lt;/code&gt; argument that we've described above. Parsing arguments in Python is not so different from C — for those who had the chance of learning it at school — except that well it's Python so you have a few more goodies.&lt;/p&gt;

&lt;p&gt;The main difference is that instead of receiving the arguments as a an argument of &lt;code&gt;main()&lt;/code&gt; you'll have to import them.&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;stderr&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="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;msg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;exit&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="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;what&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hello, world"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&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;argv&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="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"-w"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;fail&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;'Unrecognized argument "&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;argv&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="si"&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;what&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argv&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="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Too many/few arguments"&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="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, while this fits the bill, this is oh so much more laborious to parse arguments manually like this. For a long time I was afraid to use &lt;code&gt;argparse&lt;/code&gt; because the documentation is quite the beast, but in the end its basic use is very simple.&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&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;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&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;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way you let all the hard work to &lt;code&gt;ArgumentParser()&lt;/code&gt; and as a bonus you get an auto-generated help text when calling with &lt;code&gt;-h&lt;/code&gt; as an argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outside call
&lt;/h2&gt;

&lt;p&gt;All this is cool but what if you want to call the CLI tool from another Python program? Experience showed me that it's often a much more pragmatic thing to do rather than to design a specific API for Python and a different API for CLI (which is arguments parsing).&lt;/p&gt;

&lt;p&gt;The problem with what we've got here is that we can't reference our call from the outside world. To run it &lt;em&gt;has&lt;/em&gt; to be in &lt;code&gt;__main__&lt;/code&gt;, which is not necessarily what we want.&lt;/p&gt;

&lt;p&gt;Fortunately there is an easy solution to this: create a C-style &lt;code&gt;main()&lt;/code&gt; function.&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&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;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this works, this doesn't fit the bill of letting you call custom arguments from an outside package. However, all you need to do is add the arguments as an argument to your main function:&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&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;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantage of this is that by default it will keep on working like before except that this time it will let you pass custom arguments to call it from the outside, like that by 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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;say_something&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;main&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="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A bit of assistance
&lt;/h2&gt;

&lt;p&gt;For good measure and clarity, let's take out the arguments parsing into a separate function.&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Namespace&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&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;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now you'll notice that the typing annotation isn't very helpful. I usually like to be a little bit more verbose to get more assistance from my IDE later on.&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NamedTuple&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__dict__&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&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;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling signals
&lt;/h2&gt;

&lt;p&gt;For the purpose of this demonstration, let's add some "sleep" in this code to simulate that it's doing something instead of printing the text right away.&lt;/p&gt;

&lt;p&gt;Now what if the program receives a signal? There is two main things you want to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SIGINT&lt;/code&gt; — When the user does a &lt;code&gt;CTRL+C&lt;/code&gt; in their terminal, which raises a &lt;code&gt;KeyboardInterrupt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SIGTERM&lt;/code&gt; — When the user kindly asks the program to die with a &lt;code&gt;TERM&lt;/code&gt; signal, which can be handled (as opposed to &lt;code&gt;SIGKILL&lt;/code&gt;) but we'll see that later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start by handling &lt;code&gt;CTRL+C&lt;/code&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&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;sleep&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NamedTuple&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__dict__&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&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;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__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="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stderr&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="s"&gt;"ok, bye&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it's as simple as catching the &lt;code&gt;KeyboardInterrupt&lt;/code&gt; exception. Please note that we do this outside of &lt;code&gt;main()&lt;/code&gt; because the famous hypothetical "caller module" will already have its signal handling in place so that's something that we only need to setup if we're running on ourself.&lt;/p&gt;

&lt;p&gt;Next comes the handling of &lt;code&gt;SIGTERM&lt;/code&gt;. By default the program will just stop but we don't really want this. By example if we're using the following pattern inside the code:&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# do something
&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# cleanup
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We won't have a chance to cleanup if an exception isn't raised. Fortunately for us, we can raise the exception for ourselves.&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&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;sleep&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NamedTuple&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;signal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SIGTERM&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__dict__&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;sigterm_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;SystemExit&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&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;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__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;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sigterm_handler&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stderr&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="s"&gt;"ok, bye&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens is that we're using &lt;code&gt;signal()&lt;/code&gt; to register &lt;code&gt;sigterm_handler()&lt;/code&gt; as our handler for &lt;code&gt;SIGTERM&lt;/code&gt;. What happens "under the hood" is that the signal handler will be executed before the "next instruction" that the interpreter would otherwise have considered. This gives you a chance to raise an exception which is going to bubble up to our &lt;code&gt;__main__&lt;/code&gt; and exit the program with a &lt;code&gt;1&lt;/code&gt; return code while triggering all the &lt;code&gt;finally&lt;/code&gt; and context managers along the way.&lt;/p&gt;

&lt;p&gt;As stated before, there is a &lt;code&gt;sleep()&lt;/code&gt; in the middle of the function. This means that you can run the code from this section and then hit &lt;code&gt;CTLR+C&lt;/code&gt; or send a &lt;code&gt;SIGTERM&lt;/code&gt; to see what happens when you interrupt the program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Reporting
&lt;/h2&gt;

&lt;p&gt;Sometimes — more often that you'd like — your program will fail either by its own fault or because the inputs are incorrect. By example, if the user tries to open a file that doesn't exist, you might want to report it. And by "report it" I mean nicely, not with a harsh stack trace. Keep the stack trace for the cases that you didn't expect this way you know that something is really wrong.&lt;/p&gt;

&lt;p&gt;Let's imagine that we want to forbid the user to say "ni". In order to report the error, we're going to create a specific error type for our program&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;class&lt;/span&gt; &lt;span class="nc"&gt;SaySomethingError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we're going to handle it in our &lt;code&gt;__main__&lt;/code&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="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;SaySomethingError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stderr&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="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally we're going to raise the error from &lt;code&gt;main()&lt;/code&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"ni"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;SaySomethingError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Saying "ni" is forbidden'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an overall code that is:&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NamedTuple&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;signal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SIGTERM&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SaySomethingError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__dict__&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;sigterm_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;SystemExit&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&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;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"ni"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;SaySomethingError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Saying "ni" is forbidden'&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__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;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sigterm_handler&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stderr&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="s"&gt;"ok, bye&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit&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="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;SaySomethingError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stderr&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="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this very simple system, you can raise an error from your code no matter how deep you are. It will trigger all the cleanup functions from context managers and &lt;code&gt;finally&lt;/code&gt; blocks. And finally it will be caught by the &lt;code&gt;__main__&lt;/code&gt; to display a proper error message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packaging
&lt;/h2&gt;

&lt;p&gt;A good idea when providing a &lt;em&gt;bin&lt;/em&gt; script inside a package is to have it be called with Python's &lt;code&gt;-m&lt;/code&gt; option. By example instead of writing &lt;code&gt;pip&lt;/code&gt;, I usually write &lt;code&gt;python3 -m pip&lt;/code&gt; to be sure that the Pip I'm running is indeed the one from my virtual env. As a bonus, you don't need the environment's &lt;code&gt;bin&lt;/code&gt; directory in your &lt;code&gt;$PATH&lt;/code&gt;. You'll find that most famous and not-so-famous packages provide both ways of calling their binaries.&lt;/p&gt;

&lt;p&gt;In order to do that, you need to put your script into a &lt;code&gt;__main__.py&lt;/code&gt; file. Let's do this (this is UNIX commands but Windows should have no trouble translating):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;say_something
&lt;span class="nb"&gt;touch &lt;/span&gt;say_something/__init__.py
&lt;span class="nb"&gt;mv &lt;/span&gt;say_something.py say_something/__main__.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can call your script with &lt;code&gt;python3 -m say_something&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — We're creating an empty &lt;code&gt;__init__.py&lt;/code&gt; file to signify to Python that this is a proper module&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here it's clearly becoming a question of preferences but my personal favorite tool for packaging has become &lt;a href="https://python-poetry.org/"&gt;Poetry&lt;/a&gt; because it is &lt;em&gt;so&lt;/em&gt; simple. Let's create a basic &lt;code&gt;pyproject.toml&lt;/code&gt; for our project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.poetry]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"say_something"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="py"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"WTFPL"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^3.8"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.poetry.dev-dependencies]&lt;/span&gt;

&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["poetry&amp;gt;=0.12"]&lt;/span&gt;
&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"poetry.masonry.api"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try this out to see if it worked&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry run python &lt;span class="nt"&gt;-m&lt;/span&gt; say_something
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is good but did the command go? Have no fear, Poetry lets you declare your "bin" commands pretty easily. The only thing is that it needs a function to call directly, including all the signal-handling shenanigans. Let's move it all into a separate function then:&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="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NamedTuple&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;signal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SIGTERM&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NamedTuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SaySomethingError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&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="s"&gt;"--what"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__dict__&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;sigterm_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;SystemExit&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&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;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"ni"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;SaySomethingError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Saying "ni" is forbidden'&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&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;__main__&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sigterm_handler&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stderr&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="s"&gt;"ok, bye&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit&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="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;SaySomethingError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stderr&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="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;exit&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="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;__main__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can add the following to the &lt;code&gt;pyproject.toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.poetry.scripts]&lt;/span&gt;
&lt;span class="py"&gt;say_something&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"say_something.__main__:__main__"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, let's try this out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry run say_something &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"hello, poetry"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way when someone installs your package they will have the &lt;code&gt;say_something&lt;/code&gt; command available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The pattern presented in this article is one that I use &lt;em&gt;all the time&lt;/em&gt;. Weirdly, I never did a proper template for it but now that I wrote this article I know where to come and copy it. I hope that you found it useful!&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>On the usefulness of `git rebase`</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Sat, 01 May 2021 11:52:39 +0000</pubDate>
      <link>https://dev.to/xowap/on-the-usefulness-of-git-rebase-f4f</link>
      <guid>https://dev.to/xowap/on-the-usefulness-of-git-rebase-f4f</guid>
      <description>&lt;p&gt;Over 10+ years of using Git, I've only ever used &lt;code&gt;git rebase&lt;/code&gt; for very specific cases and most of those uses were at the beginning of my career when I read that it was a good practice to use it.&lt;/p&gt;

&lt;p&gt;The most common argument is that it helps reading the Git history. As I've been avoiding &lt;code&gt;rebase&lt;/code&gt; for years now I can tell you that in no situation I found myself being like "oh no I wish this commit was rebased for clarity", it's even quite the opposite. I find it much clearer to see what actually happened rather than a redacted history.&lt;/p&gt;

&lt;p&gt;On the other hand, rebases can cause weird and unexpected behaviors. After all you're messing with the git history, which is not the most intuitive thing.&lt;/p&gt;

&lt;p&gt;So in the end, who uses rebase and for what reason?&lt;/p&gt;

</description>
      <category>git</category>
      <category>discuss</category>
    </item>
    <item>
      <title>20 years later — How I find and fix bugs</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Sun, 21 Mar 2021 09:22:50 +0000</pubDate>
      <link>https://dev.to/xowap/20-years-later-mdash-how-i-find-and-fix-bugs-5567</link>
      <guid>https://dev.to/xowap/20-years-later-mdash-how-i-find-and-fix-bugs-5567</guid>
      <description>&lt;p&gt;It has been about 20 years since I first tried to fix a bug and I'm pretty sure that at that time I've used the ancestral technique of "&lt;code&gt;print&lt;/code&gt; things". Time has passed, I became a professional developer, got more senior, started leading a team to the point where now my job is part anticipating to avoid bugs and part helping juniors fix those bugs. I'm a bug buster and this is my story.&lt;/p&gt;

&lt;p&gt;As life might have taught you, there is no silver bullet. Some techniques are good for some situations while some are for others. Not every problem is a nail so you'll need more tools than a hammer. Here we go over &lt;em&gt;my&lt;/em&gt; favorite tools when it comes to bug hunting, from crystal clear situations to the most &lt;em&gt;quantum&lt;/em&gt; bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The obvious
&lt;/h2&gt;

&lt;p&gt;When talking about debugging, a few classic tricks come to mind. They are the most obvious and while I use them a lot it's important that they don't occupy too much of your mind space. You know what they do, I'm going to explain when to use them — or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;print&lt;/code&gt; things
&lt;/h3&gt;

&lt;p&gt;Every developer will — sooner than later — use &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;console.log&lt;/code&gt; or their friends to debug their code. You're probably going to expect me to say it's wrong but in truth it's really a worthy tool as long as you use it appropriately.&lt;/p&gt;

&lt;p&gt;By example Python comes from the &lt;a href="https://docs.python.org/3/library/logging.html"&gt;&lt;code&gt;logging&lt;/code&gt;&lt;/a&gt; standard module. I know it's scary as shit and makes you feel like you're doing some Java, once you slap on some goodies like &lt;a href="https://coloredlogs.readthedocs.io/en/latest/index.html"&gt;&lt;code&gt;coloredlogs&lt;/code&gt;&lt;/a&gt; you're going to feel that it's not that bad. Here is a few reasons to choose a logging module over just writing to std{out,err}.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They are made to print data structures and might be helpful in the conversion from memory to string&lt;/li&gt;
&lt;li&gt;They are often widespread so other tools can integrate with them. By example &lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt; will report logs previous to an incident&lt;/li&gt;
&lt;li&gt;They can automatically come with dates, PID numbers, thread IDs or any kind of contextual information that will help you understand what you're reading&lt;/li&gt;
&lt;li&gt;You can adjust their level depending on if you're just running the app or if you're explicitly trying to debug it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use it&lt;/strong&gt; — When you've got a complex system and most notably when it's doing some important API calls, data modification, etc. Always good to keep a trace of what is happening. Don't hesitate to preemptively install logs in your app when you still remember what is important to log, which might help you later on to understand a problem. It's also useful to check the order of execution of functions when the sequencing matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to misuse it&lt;/strong&gt; — Printing stuff is commonly used to check the value of variables inside a function or to understand what is going on during the execution of some code. This is often counter-productive as much better tools exist for these tasks. Introducing...&lt;/p&gt;

&lt;h3&gt;
  
  
  Debuggers
&lt;/h3&gt;

&lt;p&gt;Debuggers are the most obvious tools for debugging, as the name suggests. I've seen countless juniors and less juniors being afraid of using debuggers. For sure, it's probably intimidating beasts at first glance but their usefulness will make you forget the pain.&lt;/p&gt;

&lt;p&gt;The first blocking point to debuggers is often the setup. I know that the Python, Java or even the PHP process is nice with things out of the &lt;a href="https://www.jetbrains.com/products/#type=ide"&gt;IDEA family&lt;/a&gt;. In the JS world you'll have various luck depending on your build chain and how much of code maps worked (so probably not much).&lt;/p&gt;

&lt;p&gt;Once the tool is setup and that you have established some trust in the breakpoints, it's time to start using it. Mainly you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explore data structures at a given point in time, with the program completely on pause. Especially useful when using a dynamically-typed language and you're not sure what exactly the data is going to be.&lt;/li&gt;
&lt;li&gt;Try-and-test expressions quickly and &lt;em&gt;in situ&lt;/em&gt;. Very useful for fine-tuning &lt;code&gt;if&lt;/code&gt; conditions by example.&lt;/li&gt;
&lt;li&gt;See what happens step-by-step to finally understand what goes wrong in your code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use it&lt;/strong&gt; — You've identified more or less where the code goes wrong — by example from the stack trace — but you still need to understand why. Or maybe you're writing some code but you need to know what are your options: what is the precise type of parameters, what properties do they have, etc. Once I've used a debugger simply to understand the behavior of Apache on some precise matters! Debuggers are for precision work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to misuse it&lt;/strong&gt; — Something somewhere is broken but you have no clue what. Something is broken &lt;em&gt;sometimes&lt;/em&gt; but you don't know under which conditions. &lt;/p&gt;

&lt;h3&gt;
  
  
  App-level monitoring
&lt;/h3&gt;

&lt;p&gt;When "on your machine it works" but that production disagrees with you, you'll need to know what exactly happened. And you'll need to know that it happened at all. As mentioned earlier, I personally am very satisfied with &lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt;, however other options might be suitable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get notified as soon as something unusual happens in your code, whether it's an exception or some custom message sent by your code&lt;/li&gt;
&lt;li&gt;See the decoded stack trace with relevant parts of the source code&lt;/li&gt;
&lt;li&gt;At each level of the stack, see the value of variables&lt;/li&gt;
&lt;li&gt;Inspect the logs surrounding the event&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use it&lt;/strong&gt; — To detect and understand any bug happening in production. At least, to gather the data that will help you reproduce the said bug and then move to another technique.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to misuse it&lt;/strong&gt; — I've not seen any serious misuse of it. Just make sure to disable Sentry when using the app on your dev machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The blind
&lt;/h2&gt;

&lt;p&gt;While some bugs are obviously easy to trigger and to locate, others can be very tricky to understand. Maybe they are related to a specific browser version, maybe a specific screen density will cause problems or maybe an undefined sequence of actions will get you into trouble. In those cases, you have potentially no idea of what is going on yet something that worked before is broken now or maybe just a share of your users have an issue.&lt;/p&gt;

&lt;p&gt;In those cases you'll often feel like it's not really a bug. That the user is doing something wrong. That they should empty their cache or use a different computer and it will solve the problem. But one thing is for sure: the user is never wrong. At least, your application must not crash due to the user's behavior. And it's not because the bug is elusive that it is less real.&lt;/p&gt;

&lt;p&gt;Those special cases are in fact the hardest to debug. We will try here to cover the basic ideas which can let you efficiently unlock the situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bisecting
&lt;/h3&gt;

&lt;p&gt;So you've developed some feature and you know for a fact that it used to work quite well. Maybe you convince yourself by checking the database and seeing that it has indeed been widely used. Yet, now it's broken and you don't know why. Obviously the code that pertains directly to this feature didn't change but something between there and here broke it, the only question being to find out which one of those 200 commits is responsible.&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://en.wikipedia.org/wiki/Bisection_(software_engineering)"&gt;bisection&lt;/a&gt; comes into play and more specifically &lt;a href="https://git-scm.com/docs/git-bisect"&gt;&lt;code&gt;git bisect&lt;/code&gt;&lt;/a&gt;. In a nutshell, you tell it the most recent commit you known where it worked, the earliest commit you know where it was broken and then it will help you find the exact commit at which the problem happened.&lt;/p&gt;

&lt;p&gt;Typically helpful to figure when one of your colleagues introduced this CSS rule in another file which is completely affecting your component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it&lt;/strong&gt; — Something worked before and doesn't work anymore but you don't know why. Usually finding the faulty commit will quickly lead to a "oh fuck" moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to misuse it&lt;/strong&gt; — If you're not sure that it ever worked properly or that the trigger conditions are weird then don't lose your time with this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Slashing
&lt;/h3&gt;

&lt;p&gt;There is a few contexts where not loading the code will not prevent the app from displaying to some extent. By example if your JS code doesn't load then it's just that some buttons won't work. On the other hand, a syntax error anywhere could cause the whole thing to fail.&lt;/p&gt;

&lt;p&gt;When something somewhere in my JS code is preventing the rest from loading, my usual plan would be to comment out all the code except the one that I want to work. Then I check if it works. If it does, I un-comment half of the commented code and vice-versa otherwise. I do this until I find the fault piece of code.&lt;/p&gt;

&lt;p&gt;This also applies to server-side logic. I can end up isolating pieces of code by commenting most of the code around it, forcing some conditions, mocking the API responses, etc. This can also be done with unit tests. What matters here is to isolate the precise location of the bug.&lt;/p&gt;

&lt;p&gt;Those are just examples, writing a generic guide on this practice would take more than a few paragraphs. The core thinking behind this is to disable everything that could cause harm and that is not necessary to test your feature, then to progressively enable the rest. When you find the precise thing that once enabled breaks your code then you know what to fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it&lt;/strong&gt; — You don't know what but something prevents your code from executing properly. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to misuse it&lt;/strong&gt; — That's definitely not a go-to option in many cases. Try to think the problem through and read your code before commenting half your project out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Random testing
&lt;/h3&gt;

&lt;p&gt;One of the hardest bug I've ever had to find had a report alike to this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sometimes when I'm on page X and I click on that button then the app crashes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After inspecting the code of the button through and through it felt like I had no idea what could even go wrong, I figured that the bug was probably the side effect of some middleware, hook or other transversal piece of code. Even armed with this idea though, I could not find what.&lt;/p&gt;

&lt;p&gt;So I wrote a custom "unit test". The idea was the following: I scripted a Selenium driver to randomly click on the website and do random actions until those actions lead the "user" to the buggy page. Then it would automatically perform the buggy action and see if the bug was present or not.&lt;/p&gt;

&lt;p&gt;After running a few hundreds of those tests and looking at the logs it became obvious that it was the prior navigation to a specific page that triggered the bug. Some code was missing to reset global states. This code had to be present on all the pages but was forgotten from this particular one. I guess the morale of this story is that no breakpoint can ever stop into the code that is missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it&lt;/strong&gt; — This is a last-resort option for when your bug report is "sometimes when I do I'm not too sure what then it crashes". I've had to use it &lt;em&gt;once&lt;/em&gt; ever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to misuse it&lt;/strong&gt; — It's so hard to setup that you will hardly think about it if you don't really need it. Although now that I've put this idea into your mind be very careful to actually look for other options before trying this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The meta
&lt;/h2&gt;

&lt;p&gt;Of course the techniques I've listed above do not cover all the cases that you will ever meet. While I lack the ability to predict your future, I can tell you the general state of mind that will be helpful then.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't panic
&lt;/h3&gt;

&lt;p&gt;Don't panic. It's a simple advice but you need to always keep it in mind. I know how hard it is when your project is late and complex and bugs are starting to pop up from all directions.&lt;/p&gt;

&lt;p&gt;Don't fucking panic. You've created the software hence you've created the bugs. They are not beyond your ability to solve, I've never ever encountered a bug that I created which was beyond my brain power. Of course sometimes you cannot fix it for whatever reason but at least you will be able to understand it and go around it.&lt;/p&gt;

&lt;p&gt;You need to stay very factual. Don't hesitate to log everything you do into a notebook to make sure that your memory doesn't fail you. Be aware that a bug could have many consequences out of domino effect. Remember that you are using some data that was created by a buggy code then maybe some effects of that bug will persist even though you fixed it. Don't hesitate to clean up everything and start again. Methodically and calmly.&lt;/p&gt;

&lt;p&gt;And of course, don't multi-task. If you're in an intense debugging session where everything seems to fail, identify the most problematic bug and fix it first. Then identify the second one and continue. Stay calm, keep notes and move forward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go to sleep
&lt;/h3&gt;

&lt;p&gt;I've factually solved the hardest problems of my career simply by going to bed. Sleep is paramount:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you're tired your IQ simply goes down because your brain needs some rest.&lt;/li&gt;
&lt;li&gt;All your thoughts are re-indexed during sleep. When you wake up, the complexity of looking relevant ideas up is decreased by a lot.&lt;/li&gt;
&lt;li&gt;Beyond that, all brain functions perform better when rested. By example — and that's particularly true if you've got ADHD — the ability to organize.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Often when the day is ending and I'm getting stuck on some stupid things I just close my computer, go home, enjoy my evening, sleep and go back to work the day next. Then I'll open my computer, see the problem, realize that the solution is in fact very simple and get on with my day.&lt;/p&gt;

&lt;p&gt;Getting sleep when you need it — even in the middle of the day —  is going to get you much more productive than losing hours stuck dry out of ideas and pushing your body into long hours at night.&lt;/p&gt;

&lt;p&gt;Let's stay pragmatic though, don't take a nap every time you receive an exception, but knowing when your brain needs rest will definitely resolve many bugs by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While I've written before about &lt;a href="https://dev.to/xowap/10-rules-to-code-like-nasa-applied-to-interpreted-languages-40dd"&gt;how to avoid making bugs&lt;/a&gt; now was the time to consider how to fix the bugs when they present themselves. Sometimes the problem is obvious, sometimes it seems evasive. Yet, it's always a tiny condition or typo somewhere in your code that breaks everything. Changing single line will often resolve the problem. The only question is: which line.&lt;/p&gt;

&lt;p&gt;The answer is that if yous stay calm and methodical, there is plenty of tools for you to apply. Of course not all the tools have the same purpose, but overall you'll always be able to find a way to deal with the problem at hands.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why robot citizens will save the tech industry</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Sat, 12 Sep 2020 16:53:45 +0000</pubDate>
      <link>https://dev.to/xowap/why-robot-citizens-will-save-the-tech-industry-2h7l</link>
      <guid>https://dev.to/xowap/why-robot-citizens-will-save-the-tech-industry-2h7l</guid>
      <description>&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Turing_test"&gt;Turing Test&lt;/a&gt; was a very hypothetical test imagined by — guess who — &lt;a href="https://en.wikipedia.org/wiki/Alan_Turing"&gt;Alan Turing&lt;/a&gt; during the 50s. In a nutshell, a human would chat up a robot through text messaging and if they cannot tell apart the robot from another human then various conclusions can ensue according to your philosophical inclinations. Some might declare that robots became sentient, some might say that nothing replaces a soul, I might say &lt;a href="https://en.wikipedia.org/wiki/GPT-3"&gt;GPT-3&lt;/a&gt; is pretty damn close and in &lt;em&gt;no way&lt;/em&gt; can it be considered as sentient.&lt;/p&gt;

&lt;p&gt;More importantly, this test has focused our conception of robots and Artificial Intelligence as being a pure duplication of human capabilities through artificial means. However the past decade has shown to us that it's far from the case. Artificial Intelligence is for some tasks much better now than humans: &lt;a href="https://web.archive.org/web/20200407103529/https://www.mmcventures.com/wp-content/uploads/2019/02/The-State-of-AI-2019-Divergence.pdf"&gt;according to some&lt;/a&gt; it has been some years that AI has a higher performance than humans on speech or image recognition. Car makers like Tesla claim &lt;a href="https://twitter.com/elonmusk/status/1247695291739852800"&gt;superhuman powers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And while &lt;em&gt;physical&lt;/em&gt; robots are expensive and potentially dangerous in the real world, the web has been swarming with robots since its earliest days. Some are looking for security issues to exploit, some are participating massively in contests to win an iPhone, some others are trying to play weaknesses in social media algorithms. But maybe the most successful bot of all is actually omniscient and omnipotent on the Internet: Google. It visits &lt;em&gt;every single&lt;/em&gt; page on the open web at least once a month.&lt;/p&gt;

&lt;p&gt;This makes it a very interesting case:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We solve every day an increasing number of CAPTCHAs because website owners want to avoid bots. Yet, those same owners would lose their mind if Google was to avoid them.&lt;/li&gt;
&lt;li&gt;While today Google's algorithms are most likely choke-full of AI, Google acquired its power at a time where it used mostly a simple algorithm, namely the &lt;a href="https://en.wikipedia.org/wiki/PageRank"&gt;PageRank&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This indicates to us that while a lot of bots are annoying, Google is actually a &lt;em&gt;desirable&lt;/em&gt; bot which on top of that &lt;em&gt;does not need&lt;/em&gt; AI to be desired. It is not sentient, it is not passing the Turing Test, it is not even &lt;em&gt;speaking&lt;/em&gt; but yet you welcome it into your home.&lt;/p&gt;

&lt;p&gt;Let us now consider another family of bots. Twitter bots. Most of us have seen them liking posts with a particular hashtag, propagating fake news, increasing engagement numbers and many many things that I don't dare to imagine. However, is this all really the product of bots? Or is it the product of &lt;a href="https://en.wikipedia.org/wiki/Click_farm"&gt;click farms&lt;/a&gt; from India? Worse, angry groups of people would harass other individuals for free and at great scale for little to no reason at all except their own boredom.&lt;/p&gt;

&lt;p&gt;And here it is. That line. So blurred that it is not a line anymore, it is the air we breathe. It has become impossible to make the difference between a bot and a human. If Twitter is considered so &lt;a href="https://www.amnesty.org/en/latest/research/2018/03/online-violence-against-women-chapter-1/"&gt;toxic for Women&lt;/a&gt; it is not because of bots, it is because of 100% legit users. And if Google is so useful it's not because a librarian classified all 130 trillion pages of the Internet, it's because a robot did it.&lt;/p&gt;

&lt;p&gt;Humans can be "bad", robots can be "good". This is because robots are ultimately controlled by sentient beings — for now just us humans. So it all boils down to means and motives. Robots are a mean. Humans have the motive.&lt;/p&gt;

&lt;p&gt;Starting from this point you can consider the following: if you think that robots can do a lot more bad than a "bare" person, don't you think that robots can also do a lot more good? That if you give robots an opportunity to use your website/platform/app then someone might be able to build something nice upon it?&lt;/p&gt;

&lt;p&gt;A good example would be the RSS feeds. In that distant past of blogging you could aggregate the content of several websites to stay on top of everything, relay your friend's content and vice versa and overall have a truly inter-operable web. This has faded away for better or for worse but one thing stays true: this way of working only happened because we gave robots an opportunity to cooperate.&lt;/p&gt;

&lt;p&gt;The question which remains — and whose answer is far from being simple — is how can we imagine a web that is open for humans &lt;em&gt;and&lt;/em&gt; robots?&lt;/p&gt;

&lt;p&gt;A first hint would be to consider robots as actual users of your website. Users that have a different purpose than hanging out reading your content. A few ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everyone tries their best to remove bots from analytics. Embrace the fact that bots will visit your website and have different metrics for different "classes" of users. How &lt;em&gt;many&lt;/em&gt; people came to your website versus how &lt;em&gt;much&lt;/em&gt; of your content has been crawled.&lt;/li&gt;
&lt;li&gt;You could "watermark" your content depending on the bot to keep track.  With &lt;code&gt;utm&lt;/code&gt; variables in URLs, hidden meta-data in images, etc.&lt;/li&gt;
&lt;li&gt;Present several interfaces. HTML/CSS for people, micro-formats for crawlers, APIs for services building on top of yours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a regulatory level, it seems increasingly obvious that &lt;a href="https://www.raconteur.net/risk-management/legal-innovation-2019/robot-rights-ethics"&gt;laws need to adapt&lt;/a&gt; so we can keep track of responsibilities. At the level of a website, it means making it natural to associate the actions of the robot to the will of a person:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have a simple-to-use, well-documented API which can be accessed with an identifying token.&lt;/li&gt;
&lt;li&gt;When you put up a wall like a CAPTCHA or a SMS verification, you create a mechanism where you'll lose control. When users will figure how to bypass your security it will mean that you have no more means of detecting anything bad that they are doing.&lt;/li&gt;
&lt;li&gt;A few years back, I worked in a company making Facebook apps. In spite of all the verifications made by Facebook on people's identity, phone number, credibility of their overall behavior, let me tell you that we received basically 90% of bot participation in our apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But look at Twitter. They had an open API and are going backwards, partly because of bots. Aren't they however mistaking the means and the motive? Even if they can cut the harm done by bots but they will never cut the harm done by humans. The change they need to make is deeper.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It might sound stupid — and certainly is to be proven — but you could try to &lt;em&gt;sell&lt;/em&gt; the service you're providing. Not everyone can be an ads platform, it's ridiculous. There is a legion of innovative &lt;a href="https://a16z.com/2018/12/05/connie-chan-advertising-models-content-product-china/"&gt;business models&lt;/a&gt; we can copy from the world champion in consumption, China.&lt;/li&gt;
&lt;li&gt;Money is a far stronger trust platform than ReCAPTCHA. Netflix shows have to put money to be produced, people have to pay money to view it and creators are compensated when the content is successful. Whatever idiot with a phone can say anything they want on YouTube. Guess which platform is subject to the most criticism?&lt;/li&gt;
&lt;li&gt;Work against nefarious behaviors, not nefarious users. To stay on Twitter examples, every day I see "masculists" at war against feminists for what seems to be no good reason at all. If you can detect that a cluster is starting to attack another cluster — which is &lt;a href="https://demo.deeppavlov.ai/#/en/insult"&gt;almost trivial&lt;/a&gt; with current AI libs — then just silence the fuck out of those tweets. Deciding who is right is not your job, however you can decide to avoid harm to both parties by pulling a plug from time to time.&lt;/li&gt;
&lt;li&gt;Give no reason to commit those nefarious acts. Facebook's &lt;em&gt;likes&lt;/em&gt; have been debated over and over as they represent the social currency. But once again, they fit none of the characteristic of a currency except being a number that can be measured. By example, instead of measuring a creator's success in likes, measure it in money provided by its patrons.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically it comes down to this: if your business models is getting people engaged as long as possible on your app to show them as much ads as possible then you are abusing people and other people can leverage that too. If your business model is sane then you are less likely to get problems and abuses. Out of all the big tech names, which ones are considered to be a danger to democracy and which ones depend on ads to stay filthy rich? Is the solution to fake news to send a SMS verification code to keep the nasty Russian bots away?&lt;/p&gt;

&lt;p&gt;The real problem is that existing platforms, the main social media first in line, are ill-conceived and facilitate nefarious behavior. This is exactly why robots need to be granted a first-class citizenship on the web. Not only robots can build fantastic things but also if your platform is ready to absorb the nastiest behavior of the nastiest bot by the worst-intentioned human then there is a good chance that &lt;em&gt;humans&lt;/em&gt; on your platform &lt;em&gt;are&lt;/em&gt; properly protected and will stay protected no matter the direction into which technology evolves.&lt;/p&gt;

&lt;p&gt;By negating the worst at its largest scale, you get the strength to enable the best for both humans, robots and the whole ecosystem around you.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>ux</category>
    </item>
    <item>
      <title>CSS Patterns — Deep dive in grids</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Sun, 05 Jul 2020 20:54:34 +0000</pubDate>
      <link>https://dev.to/xowap/css-patterns-deep-dive-in-grids-4kh6</link>
      <guid>https://dev.to/xowap/css-patterns-deep-dive-in-grids-4kh6</guid>
      <description>&lt;p&gt;Over recent years, CSS grids became one of the main way to deal with layout. And for good reason: they will manage both your margins and automatically stack up content when in mobile mode. As a matter of fact, grids are one of the main component of CSS frameworks like Bootstrap and a lot of designers will lay 12-column grid guides over their designs.&lt;/p&gt;

&lt;p&gt;However, after working on many pixel-perfect integrations and layouts, I've come to realize that grids are a concept very easily setup and configured to match your custom requirements.&lt;/p&gt;

&lt;p&gt;The goal of this article is to dive into how grids work and how you can use them in your layouts. In substance, you'll learn that it takes very little effort to build your custom grid system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a grid?
&lt;/h2&gt;

&lt;p&gt;The Grid. The digital frontier. I tried to picture clusters of information as they moved through the computer. What do they look like? Ships? Motorcycles? Were the circuits like freeways?&lt;/p&gt;

&lt;p&gt;&lt;iframe width="100%" height="80px" src="https://open.spotify.com/embed/track/5z9v299A7qXXSk2VY0sUu0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Woops. Wrong grid.&lt;/p&gt;

&lt;p&gt;So here we're (unfortunately) not talking about the Grid from Tron. We're also not talking about the &lt;a href="https://caniuse.com/#feat=css-grid"&gt;actual CSS grid feature&lt;/a&gt; because its support does not cover IE 11 and a lot of us still need to deal with it. Although the applications are the same — the native grid being more powerful, of course — we're going to explain the workings of &lt;em&gt;old-school&lt;/em&gt;, &lt;em&gt;à la Bootstrap&lt;/em&gt; grids.&lt;/p&gt;

&lt;p&gt;The idea behind grids is fairly simple. Consider that your content fits into boxes, then you can create a grid on your page and fill each cell of the grid with one of those boxes.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/WNrMdrM?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The above example is a very simple form of grid: just images all of the same size, flowing in the page and wrapping to the next line when the page is too narrow.&lt;/p&gt;

&lt;p&gt;You can start seeing that while the content size stays the same, if the screen size then the content reorganizes itself to fit the screen width.&lt;/p&gt;

&lt;p&gt;In short, a grid is a way to flow your content that is automatically responsive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Down the gutter
&lt;/h2&gt;

&lt;p&gt;Next thing you want in a grid is to avoid getting everything sticking together. Indeed, in the previous example we're seeing all those pictures touching each other but in real life you usually expect to see a gutter between your content cells.&lt;/p&gt;

&lt;p&gt;Let's try to set a margin to each cell with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@gutter-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;gutter-size&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;gutter-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;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/JjGpMbj?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As you can see, a gutter forms but it also forms between the grid container and the cells. You can think of different tricks to counteract this but mostly you're going to run into various issues with the first row or the last row or alignment problems.&lt;/p&gt;

&lt;p&gt;Now, what if instead of puting the whole gutter on top, half of the gutter was on top and half of the gutter was on the buttom? And same thing for the left/right gutter?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@gutter-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;gutter-size&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&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;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/BajYJRm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Now you'll tell me: "but Rémy, you did only shift the cells, you didn't remove the gutter between the cells and the container. You are useless and I'll stop reading right now!"&lt;/p&gt;

&lt;p&gt;This is however just a first step! Because we're "radiating" margin in all directions, it becomes very easy to offset with a &lt;em&gt;negative&lt;/em&gt; margin on the grid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@gutter-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;gutter-size&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&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;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/xxZYpLb?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And boom, the images are perfectly aligned with the container while retaining their gutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cell structure
&lt;/h2&gt;

&lt;p&gt;At this point it's interesting to mention the way cells are made in HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cell"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inside"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;cell&lt;/code&gt; itself contains the gutter, so it's not really an element onto which you want to apply any other style.&lt;/p&gt;

&lt;p&gt;What is really going to be the &lt;em&gt;visual&lt;/em&gt; cell is the &lt;code&gt;inside&lt;/code&gt; element, on which you can apply all the style (borders, backgrounds, internal paddings and otherwise).&lt;/p&gt;

&lt;h2&gt;
  
  
  Sizing items
&lt;/h2&gt;

&lt;p&gt;In the previous examples, the grid items had a fixed size. But what if you want each cell to be a relative size?&lt;/p&gt;

&lt;p&gt;You might have noticed in the previous section that we're using padding instead of margin. The reason is that this simplifies a lot size computation when it comes down to relative sizes.&lt;/p&gt;

&lt;p&gt;If you want to make a grid where all items take up 1/4 of the width, it's as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25%&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;In the following example, we've got:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blue boxes which are the content boxes of random height&lt;/li&gt;
&lt;li&gt;Grey boxes are the cells&lt;/li&gt;
&lt;li&gt;Red border is the container delimitation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/oNbEqar?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The great advantage of doing the grids yourself is that you can invent things that are not strictly sticking to the 12-columns grid scheme. By example, using flexboxes, you can do a cell size that fills up remaining space by creating a flex-based sizing system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.s10p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.s20p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;20%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.sfill&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;100%&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;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/YzweadP?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Let's note that here the &lt;code&gt;.sfill&lt;/code&gt; sizing works only because the flex doesn't wrap. If you need this feature you cannot have a wrapping grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Without flexboxes
&lt;/h2&gt;

&lt;p&gt;Previous layouts work using flexboxes, which are not necessarily available on your platform. So you might need to go really old school on this one!&lt;/p&gt;

&lt;p&gt;No problem, let's use &lt;code&gt;inline-block&lt;/code&gt; display!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — If you're doing this, you need to make sure that your HTML code has no whitespace between grid cells, otherwise it'll create small spaces between blocks and you definitely do not want that&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@gutter-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;gutter-size&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;gutter-size&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;vertical-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;top&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.s40p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.s60p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60%&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;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/ExPQEJr?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Admittedly this isn't the most convenient but it works fine enough!&lt;/p&gt;

&lt;h2&gt;
  
  
  Stacking
&lt;/h2&gt;

&lt;p&gt;One of the main advantages of grids is the ability to stack items when the screen size requires it. Depending on the technique you're using (flexboxes or inline blocks) it's as simple as setting the width of all cells to 100%:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;For&lt;/span&gt; &lt;span class="err"&gt;flexboxes&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;For&lt;/span&gt; &lt;span class="err"&gt;inline&lt;/span&gt; &lt;span class="err"&gt;blocks&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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;Please note that doing so with flexboxes will require &lt;code&gt;flex-wrap: wrap&lt;/code&gt;, otherwise everything will stay on the same line.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/VweQxee?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Another option with flexboxes, especially if you didn't enable wrap, is to flip the flex direction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.cell&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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;&lt;iframe height="600" src="https://codepen.io/Xowap/embed/jOWZxWx?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In those examples, you can see how the stacking happens very naturally thanks to the gutter technique.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use
&lt;/h2&gt;

&lt;p&gt;I cannot stress enough the fact that I'm using those custom grids all the time and I never use framework grids anymore.&lt;/p&gt;

&lt;p&gt;The reason is that custom grids are so simple to make yet behave exactly like you need. Generic grid systems are tied to the framework you're using, their compatibility target and other elements which might not play in your interest.&lt;/p&gt;

&lt;p&gt;As the gutter system is so powerful, I use these kinds of systems litteraly all the time, even sometimes just to stack up elements!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;If you need to create your grid system, you first need to consider what underlying technique you're going to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flexboxes are going to be simpler and more powerful&lt;/li&gt;
&lt;li&gt;Inline blocks work on the most antiquated platforms you need to support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then determine how is the sizing going to work. If you want the ability to have cells that fill up remaining space, you need to disable flex wrapping, which is going to affect the way you stack things.&lt;/p&gt;

&lt;p&gt;And finally determine what kind of utility classes you are going to do. As I always do very ad-hoc grid systems, I usually don't do something too complicated. By example if I need only two sizes of cell, I'd create a &lt;code&gt;cell-small&lt;/code&gt; and &lt;code&gt;cell-big&lt;/code&gt; size class and that's it.&lt;/p&gt;

&lt;p&gt;To go further with this, next time that you see in a project something grid-like don't hesitate to start asking yourself what kind of custom grid you can build for this using what is described here and then write the few CSS lines that you need for it. All you need to do is to superpose CSS codes from this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gutter management&lt;/li&gt;
&lt;li&gt;Cells sizing&lt;/li&gt;
&lt;li&gt;Stacking&lt;/li&gt;
&lt;li&gt;Flexbox/Inline block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't hesitate to ask for help in the comments, in case things are not entirely clear!&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>IP-based localization, why oh why?</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Sat, 25 Apr 2020 10:22:04 +0000</pubDate>
      <link>https://dev.to/xowap/ip-based-localization-why-oh-why-4b2h</link>
      <guid>https://dev.to/xowap/ip-based-localization-why-oh-why-4b2h</guid>
      <description>&lt;p&gt;HTTP is a smart protocol and many headers help the browser to negotiate the content with the server. In particular, there is &lt;code&gt;Accept-Language&lt;/code&gt; that allows to list the user's languages by order of priority and it's a perfect way to determine in which language you are going to display your website to the user.&lt;/p&gt;

&lt;p&gt;Yet, as I travel(ed) a lot, I notice that a lot of websites actually will serve you content based on your IP address and the language that they guess is associated to it. That is not very noticeable when I go to countries that speak languages I speak but when I go to Czech Republic the Internet becomes suddenly much less usable to me.&lt;/p&gt;

&lt;p&gt;And this seems utterly stupid to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the browser is telling you via &lt;code&gt;Accept-Language&lt;/code&gt; why would you trust it less than a guesstimate based on the user's IP address' supposed location?&lt;/li&gt;
&lt;li&gt;You can more or less pin an IP address to a country but you can rarely pin a country to a language. France, Spain, India, Canada, the US, and so forth have several languages spoken on their territory and often officially. Sometimes there's not even common languages between all the people of the country. It's not the exception, it's the rule. Hence when doing IP-based localization you're naturally offending a lot of people.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, I understand that some websites like e-commerces have a complicated localization process since a lot more things will depend on the country rather than the language (available products, shipping costs, etc).&lt;/p&gt;

&lt;p&gt;But if your website exists in several languages, &lt;strong&gt;why oh why would you enforce one language on the user based on their IP's country?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Add progress bars to any command</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Mon, 06 Jan 2020 00:29:27 +0000</pubDate>
      <link>https://dev.to/xowap/add-progress-bars-to-any-command-1f58</link>
      <guid>https://dev.to/xowap/add-progress-bars-to-any-command-1f58</guid>
      <description>&lt;p&gt;Have you ever tried to compress a huge file, waited a bit in front of your terminal and wondered if you should go get a coffee or if it's just about to finish?&lt;/p&gt;

&lt;p&gt;After spending some time dealing with huge files and trying to figure out how much I advanced, I ended up discovering tricks that I decided to industrialize. Enter &lt;a href="https://github.com/Xowap/Spybar"&gt;Spybar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XN1gOB0Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iz3lys4p1q42xki0el0d.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XN1gOB0Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iz3lys4p1q42xki0el0d.gif" alt=" raw `spybar gzip -c that_big_file.dat` endraw "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a simple Python script that you can get with a&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install spybar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefix a command with it, it will start it and display the progress bar. You can also attach to an existing PID. Everything is explained &lt;a href="https://github.com/Xowap/Spybar"&gt;in the readme&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article is however not about the usage of the tool. It's about how it works.&lt;/p&gt;

&lt;p&gt;Let's first state that it's only compatible with Linux. It could be that there is some way to do this in other operating systems but this article is not about that.&lt;/p&gt;

&lt;p&gt;In Linux, there is a special directory, &lt;code&gt;/proc&lt;/code&gt; which contains a directory for each running process.&lt;/p&gt;

&lt;p&gt;Suppose that you are working on process &lt;code&gt;42&lt;/code&gt;, you can list all the files open by a process by doing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -lsh /proc/42/fd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you open a file, in C, you get an integer which is the file handler. All those integers are listed in the &lt;code&gt;fd&lt;/code&gt; directory. They all are symbolic links to the actual file that they open. Using &lt;code&gt;ls&lt;/code&gt;, once you've located the file you want, you can note down its number. Suppose that you're interested in number &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is a different folder which contains some meta-information about those handlers. Which you can simply access by doing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat /proc/42/fdinfo/3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get something alike of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pos:    569573376
flags:  0104000
mnt_id: 28
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Including the &lt;code&gt;pos&lt;/code&gt; line which indicates us where exactly the handler is pointing to.&lt;/p&gt;

&lt;p&gt;Then you just need to know the size of the file and there you go, you know both the current position and the file size, that's all you need to generate a progress bar.&lt;/p&gt;

&lt;p&gt;After doing this for a while I figured that I'd write that little tool so here we are. Thank you for reading!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>DEV-CLI 0.2.0</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Sun, 27 Oct 2019 19:42:39 +0000</pubDate>
      <link>https://dev.to/xowap/dev-cli-0-2-0-33a2</link>
      <guid>https://dev.to/xowap/dev-cli-0-2-0-33a2</guid>
      <description>&lt;p&gt;These last days I have been working on a &lt;a href="https://github.com/square/retrofit"&gt;Retrofit&lt;/a&gt;-inspired library for Python called &lt;a href="https://github.com/xowap/typefit"&gt;Typefit&lt;/a&gt;. Since it's clearly not ready for production use yet I'm not doing an introductory post just yet, however as I'm learning to use it in real life I've decided to migrate &lt;a href="https://github.com/xowap/dev-cli"&gt;DEV-CLI&lt;/a&gt; into using Typefit.&lt;/p&gt;

&lt;p&gt;This new version does not bring any particular feature (nor any feature at all actually), however two changes emerge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The internal API wrapper is now type-safe and much more complete&lt;/li&gt;
&lt;li&gt;Instead of some boilerplate code it uses the Typefit API client builder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is clearly not the news of the year but I also needed to test if this new version of the API works so I had to write an article :)&lt;/p&gt;

</description>
      <category>showdev</category>
    </item>
    <item>
      <title>What CAPTCHA do you use?</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Mon, 07 Oct 2019 08:53:41 +0000</pubDate>
      <link>https://dev.to/xowap/what-captcha-do-you-use-43ba</link>
      <guid>https://dev.to/xowap/what-captcha-do-you-use-43ba</guid>
      <description>&lt;p&gt;Anybody that has been on the web long enough knows that it is crawling with bots and nasty stuff just trying to spam you from all the possible channels. That's why CAPTCHA were invented.&lt;/p&gt;

&lt;p&gt;It quickly became a hurdle for developers to create such elements since you constantly need to do something up to speed to beat AI. The folks at reCAPTCHA quickly understood that and provided a great CAPTCHA service.&lt;/p&gt;

&lt;p&gt;What was pretty smart about it is that instead of generating stupid text they would use a state-of-the-art OCR to read scanned books and whenever they found something that the OCR could not read (thus that a bot could not read) then they would ask humans to read it in the form of a CAPTCHA. Genius!&lt;/p&gt;

&lt;p&gt;Google quickly understood that this is a huge opportunity to get free labor doing all the classification and training of your AI and bought reCAPTCHA a few years later.&lt;/p&gt;

&lt;p&gt;Now my problem is the following: I'm sick of working for Google for free and there is no way that I make my users work for Google for free.&lt;/p&gt;

&lt;p&gt;Most of the time I avoid CAPTCHAs by having a logic that just allows for bots to register/do whatever without hurting the real users. But sometimes you can't have that.&lt;/p&gt;

&lt;p&gt;Does anyone know any other form of Turing test that would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very quick&lt;/li&gt;
&lt;li&gt;Non-intrusive&lt;/li&gt;
&lt;li&gt;Not a SaaS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks everyone for your feedbacks!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Version and automate your dev.to publishing with GitHub Actions</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Tue, 01 Oct 2019 21:38:22 +0000</pubDate>
      <link>https://dev.to/xowap/version-and-automate-your-dev-to-publishing-with-github-actions-cn3</link>
      <guid>https://dev.to/xowap/version-and-automate-your-dev-to-publishing-with-github-actions-cn3</guid>
      <description>&lt;p&gt;I love publishing on &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;, yet there is a very big problem related to doing this: the web editor is not really dev-friendly since it doesn't allow you to version your content. There is also the constant fear of doing something wrong and losing an article that you wrote within the browser because of stupid computer crash or other kinds of CTRL+Q disasters.&lt;/p&gt;

&lt;p&gt;Yet, faced with a challenge, what do devs do? They dev!&lt;/p&gt;

&lt;p&gt;First things first, I've created a &lt;a href="https://github.com/Xowap/dev-blog"&gt;GitHub repo&lt;/a&gt; to manage all my upcoming Markdown files. Now I've got a place where to store my articles but how do I get them online?&lt;/p&gt;

&lt;p&gt;Apparently the hot new thing is GitHub actions. So I created one in my repo! The GitHub interface is surprisingly intuitive for doing so. I'm absolutely no expert on the matter since I wrote &lt;a href="https://github.com/Xowap/dev-blog/blob/master/.github/workflows/publish.yml"&gt;my first action ever&lt;/a&gt; about 5 minutes ago, but it's fairly easy to use since that same action published this very article!&lt;/p&gt;

&lt;p&gt;In order to be able to publish using GitHub actions, you need some kind of script. I couldn't find anybody who did that (but maybe did I search not enough?) so I created my own tool. Let me introduce &lt;a href="https://github.com/Xowap/DEV-CLI"&gt;DEV-CLI&lt;/a&gt;! 🎉 You'll find all the details in the README. You can of course use it right now to publish your Markdown, with or without GitHub actions 😇&lt;/p&gt;

&lt;p&gt;I must say that this was a fun evening project. It's still completely experimental and I'd love feedback from other people doing the same thing — or not doing this if they have alternative workflows!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>tutorial</category>
      <category>meta</category>
    </item>
    <item>
      <title>The way adults deliver software on time</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Sun, 29 Sep 2019 10:46:08 +0000</pubDate>
      <link>https://dev.to/xowap/the-way-adults-deliver-software-on-time-2bio</link>
      <guid>https://dev.to/xowap/the-way-adults-deliver-software-on-time-2bio</guid>
      <description>&lt;p&gt;Let's quickly introduce this post with a little bit of context. First of all, I'm the CTO of a web agency which deals with many short projects with usually one or two developers working on it. I've also worked on multi-year projects involving dozens of developers but that's a smaller part of my experience.&lt;/p&gt;

&lt;p&gt;Here we'll refer to the &lt;em&gt;client&lt;/em&gt; as the person whose money you are spending and whose hopes you are entrusted with. They could be the founder of your startup but for me it's literal clients.&lt;/p&gt;

&lt;p&gt;And finally, I hate all the technical project management and methodology gibberish so don't expect me to use complicated terms nor to deploy corporate methodology diagrams that make rocket engines look simple.&lt;/p&gt;

&lt;p&gt;The goal here is to give you my take on software planning, from the time where you have to give an estimate to the on-time delivery of the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimating
&lt;/h2&gt;

&lt;p&gt;It would be fair to assume that the immense majority of developers do not like to produce estimates. And while — as the &lt;a href="https://twitter.com/search?q=%23noestimates"&gt;#NoEstimates&lt;/a&gt; movements states — it's definitely impossible to estimate everything within a 10-minutes precision before doing the project, the client has a limited amount of money and an event to attend in 3 months where they'd like to present their product. Maybe you can't commit on everything but you definitely have to commit on &lt;em&gt;something&lt;/em&gt;. The "when it's ready" philosophy is a sweet delight best consumed by hobbyists and &lt;a href="https://en.wikipedia.org/wiki/The_Winds_of_Winter#Publication_history"&gt;GRRM&lt;/a&gt; while us, responsible adults, have to provide estimates.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — This requires a lot of experience both from the person(s) doing the estimate and the company projects. Personal experience will be a way to give good estimates and spot all the pitfalls while the Git history or previous time sheet will support the numbers with data. If you are not experienced enough, you need to recognize so and get help from someone more senior than you are, otherwise you'll be putting yourself in a complicated situation. If you don't know, you don't know, it's fine!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Get rough
&lt;/h3&gt;

&lt;p&gt;Usually the first time I have to estimate anything it's when my commercial team comes up to me with a new project. The specifications are more or less detailed, but you'll have to deal with them.&lt;/p&gt;

&lt;p&gt;The main idea is to create a first version of your &lt;a href="https://en.wikipedia.org/wiki/Work_breakdown_structure"&gt;work-breakdown structure&lt;/a&gt;. You can use a mind-mapping tool for that, like &lt;a href="https://coggle.it/"&gt;Coggle&lt;/a&gt; or simply a sheet of paper. Lay out all the different blocks of your program (like "front-end", "back-office", "API", "mobile app", etc). Then lay out all the features of each component. If a feature crosses several blocks, put the feature in each block.&lt;/p&gt;

&lt;p&gt;Then look at your tree and wonder what you forgot. There is a lot of usual suspects, like the login/logout page, password recovery, email change process, various emails, GDPR cookie consent, analytics tracking, etc.&lt;/p&gt;

&lt;p&gt;Then look at your tree and ask yourself what you forgot. Maybe it's an additional invisible block that you'll need, maybe some regulations will mess with your plans, maybe it's unexpressed implicit client needs, etc.&lt;/p&gt;

&lt;p&gt;Then look at your tree and ask yourself what you forgot. Until you run dry of ideas.&lt;/p&gt;

&lt;p&gt;Then look at your tree and ask yourself what you forgot.&lt;/p&gt;

&lt;p&gt;However rest assured, you &lt;em&gt;can&lt;/em&gt; get a rough estimate that is going to produce a realistic deadline. In that regard I always love to refer to the Apollo program — which included &lt;em&gt;inventing&lt;/em&gt; software engineering on top of doing the software itself. The deadline was very publicly announced by JFK in his "&lt;a href="https://www.youtube.com/watch?v=QAmHcdwKgtQ"&gt;We choose to go to the moon&lt;/a&gt;" speech given after his advisors &lt;a href="https://www.youtube.com/watch?v=e0ERXwhn-5w"&gt;studied different options&lt;/a&gt; of space prowesses that could be done within the deadline. Did it cost an insane amount of money? Yes. Was it successful? Also yes. That's something we'll cover later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assess your risks
&lt;/h3&gt;

&lt;p&gt;As explained in "&lt;a href="https://www.computer.org/10.1109/52.589226"&gt;Risk Management Is Project Management for Adults&lt;/a&gt;" by &lt;a href="https://en.wikipedia.org/wiki/Tim_Lister"&gt;Tim Lister&lt;/a&gt;, risk management is the very foundation of project management and especially software project management.&lt;/p&gt;

&lt;p&gt;At this state of your project, you must identify two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The core feature(s) which are the &lt;em&gt;raison d'être&lt;/em&gt; of this project&lt;/li&gt;
&lt;li&gt;The most risky features whose timing is difficult to assert, probably because you lack the experience in those specific tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interestingly enough it turns out that the most risky features are usually the core features. It makes sense because that's what makes this project &lt;em&gt;different&lt;/em&gt; from all the other projects.&lt;/p&gt;

&lt;p&gt;This risky part merits your utter attention. Try to talk to experts to help you estimate these tasks. Have a look at different libraries or frameworks that could help you doing this. Read blog posts and look for desperate Stack Overflow comments.&lt;/p&gt;

&lt;p&gt;Equipped with this knowledge you can probably estimate the magnitude of the task to come. Then prepare some buffer to make it twice and some more buffer for some polishing time.&lt;/p&gt;

&lt;p&gt;Please note that if you encounter some risky parts that also happen to be far from the core of the value proposition then you really should negotiate to move those parts out of the scope or at least to limit the commitment on them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — If your project includes research and development then the problem is completely different. Research can't provide warranties on the result nor the timing. Your project can implement features that emanate from academic research but don't ever bet on something that hasn't been discovered yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Estimate regular tasks
&lt;/h3&gt;

&lt;p&gt;Now is time to estimate the non-risky part of the business. For each small item in your tree, think how many &lt;em&gt;days&lt;/em&gt; it'll take. Don't be shy, everything takes a long time. I've made this mistake a lot in my first years, estimating that some tasks would take small hours or even minutes because I felt that a longer time would feel ridiculous. It's not ridiculous, you need time to work correctly. That's why I estimate everything in days and very rarely do I need to go beyond half a day.&lt;/p&gt;

&lt;p&gt;Then you get up your tree and for each node you can sum the work planned on each leave. Don't hesitate to add a small percentage of the sum to the leave. Suppose that you imagined something like this (which is a bit exaggerated for demonstration purposes):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My Project (8)

&lt;ul&gt;
&lt;li&gt;Login (4)

&lt;ul&gt;
&lt;li&gt;Form (1)&lt;/li&gt;
&lt;li&gt;Password recovery (2.5)

&lt;ul&gt;
&lt;li&gt;Form (0.5)&lt;/li&gt;
&lt;li&gt;Email (1)&lt;/li&gt;
&lt;li&gt;Password change (0.5)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Signup (3)

&lt;ul&gt;
&lt;li&gt;Form (1)&lt;/li&gt;
&lt;li&gt;Email confirmation (1)&lt;/li&gt;
&lt;li&gt;Email confirmation page (0.5)&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;As you can see, the project takes 8 days but if you look at just the leaves the sum is 5.5 days. There's 2.5 days added as you move up the hierarchy which account for the growing complexity of the project and thus the difficulty to deal with other parts. That is inspired from the &lt;a href="https://en.wikipedia.org/wiki/COCOMO"&gt;COCOMO&lt;/a&gt; model's exponential factor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Negotiate the deadline
&lt;/h3&gt;

&lt;p&gt;Quite often, the client — or your commercial team before them — will not be satisfied by the presented cost and/or deadline. In that case you must remember that you made those estimates for a reason in a first place and that there is no way to bring them down.&lt;/p&gt;

&lt;p&gt;One thing you can do to advance the deadline is work more. That is definitely not something that you can sustain but if the project is a very few weeks long then going home later and working on the week-ends might just save the situation.  The downside is that you'll burn out and you'll need a recovery period. I've done this before and I know that after this kind of sprints I'm useless for at least a whole week.&lt;/p&gt;

&lt;p&gt;You can also throw in more people, however this needs to be smartly done so that everyone can work on their own tasks without being impeded by the advance of other team members.&lt;/p&gt;

&lt;p&gt;Yet the main tool you have is scope reduction. If you want the project earlier then the only real leverage that you have is to reduce the scope. So you can take that tree and start cutting branches. It can be a few leaves here and there. It can also be big branches. You can usually skim the project quite a lot from a lot of fancy but not-so-useful requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't forget extremities
&lt;/h3&gt;

&lt;p&gt;Depending on the project size this is more or less noticeable but you'll of course need to worry about the project setup and deployment. Most of the time I'll advise to be feature-oriented but for those two it's incompressible technical tasks.&lt;/p&gt;

&lt;p&gt;Setup will be the creation of code repository(es), create the project scaffolding and get something basic running. Basically it's configuring the framework(s) that you'll use for the project.&lt;/p&gt;

&lt;p&gt;Deployment is the configuration of the development and production platforms, which is sometimes easy within your usual infrastructure but sometimes will be done according to your client's requirements and should not be overlooked. If the client has complicated IT processes and requires you to do deployment over a special VPN that stays up only 10 minutes at a time when the moon enters specific phases then you'd better worry about this and put it in your estimate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planning
&lt;/h2&gt;

&lt;p&gt;Next big stop on the hate track is planning. However, if you followed the methodology thus far you'll find that it's quite simple. This section is more for project managers than developers in theory but in practice everyone should know what is going on and why it's going on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic structure
&lt;/h3&gt;

&lt;p&gt;While on big projects you can start to parallelize things, it is far better to make your tasks as sequential as possible. When you start working on something, make sure that the previous task is done. My suggested order of big tasks is the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Functional specifications&lt;/strong&gt;. Using a prototyping tool like &lt;a href="https://www.axure.com/"&gt;Axure&lt;/a&gt;. This then gets translated into feature tickets. In my company, project managers do this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphic design&lt;/strong&gt;. Please &lt;em&gt;don't&lt;/em&gt; use Photoshop for that, as a developer I prefer to see &lt;a href="https://www.sketch.com/"&gt;Sketch&lt;/a&gt; designs. In the team we use &lt;a href="https://avocode.com/"&gt;Avocode&lt;/a&gt; to communicate designs which allows for a very high level of precision when integrating the designs. That's also not a developer's job since it's graphical designers doing this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical specifications&lt;/strong&gt;. It doesn't have to be a long formal document but at least sit around with the tech lead to decide the main technical choices (front-end and back-end frameworks usually) as well as the strategy to tackle the risky parts of the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development&lt;/strong&gt;. That's where you do your favorite Agile things and pop up your favorite tools and write beautiful code ✨.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data filling&lt;/strong&gt;. If the project needs data, then once the features exist the filling of that data can begin but not before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;. While testing happens every day on a per-feature basis, before the release a fair amount of testing will be done on the whole platform, which will lead to a round of bug fixing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release&lt;/strong&gt;. This means pushing your code onto the production platform. This needs to happen as early as possible. If you can, &lt;strong&gt;invert testing and release&lt;/strong&gt; so you release onto production a first time &lt;em&gt;before&lt;/em&gt; the testing process starts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Reviewing the specs
&lt;/h3&gt;

&lt;p&gt;Once a project manager hands you the specifications, you need to review them. The early estimate that you made earlier is your budget and now you need to make sure that the finished specifications match that budget.&lt;/p&gt;

&lt;p&gt;So take your WBS from the estimate and update it for the finished specs. This time you should have a pretty accurate idea of what you're going to produce.&lt;/p&gt;

&lt;p&gt;The trick is when the specs are not done well and that edge cases are forgotten. It's important to play with the specs and consider them under all aspects to make sure that they make sense and that all cases are accounted for. Otherwise you'll discover it during the development and that will be unfortunate.&lt;/p&gt;

&lt;p&gt;Another important part of reviewing the specs is pointing out what would take an inconsiderate amount of time to be done for little-to-none added value. UX designers and project managers don't always know how programs work under the hood and might choose very inefficient solutions for no reason at all. That's a developer's job to stay clear from these traps and push easy solutions instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TChk268b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgs.xkcd.com/comics/tasks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TChk268b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgs.xkcd.com/comics/tasks.png" alt="In CS, it can be hard to explain the difference between the easy and the virtually impossible"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— &lt;a href="https://xkcd.com/1425/"&gt;&lt;em&gt;XKCD, Tasks&lt;/em&gt;&lt;/a&gt; illustrating how counter-intuitive the abilities of computer science are&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — It's very easy to miss things. You don't know what you don't know. Missing things won't appear in your head magically. You need to setup a thought process that will help you highlight those missing things. This is the topic for another article, but some ideas include testing the wireframe extensively with different persona, starting to lay the data model or create sequence diagrams to make sure everything fits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Data filling
&lt;/h3&gt;

&lt;p&gt;One important aspect is to make sure that all data has a source. It's easy to get crazy in the design process and include lots of texts and images everywhere but every single bit of information has to be accounted for. The source of that data must be crystal clear at this point and the way that data is going to end up in your database &lt;strong&gt;must be&lt;/strong&gt; a part of the specs.&lt;/p&gt;

&lt;p&gt;Might be that you need a back-office to fill all the data. In that case you need also to think about how you are going to do the back office and include that in the time you'll have to spend. Usually I use the &lt;a href="https://docs.djangoproject.com/en/2.2/ref/contrib/admin/"&gt;Django admin website&lt;/a&gt; or &lt;a href="https://wagtail.io/"&gt;Wagtail&lt;/a&gt; to generate those interfaces because they are so damn efficient and I love them very much 💕.&lt;/p&gt;

&lt;p&gt;It might also be that you're going to inject data from CSV file or from an external API. In that case you need to make sure that your data source provides the data you need in a format you can exploit.&lt;/p&gt;

&lt;p&gt;In any case you cannot start filling the data before the data model of your app is done. If you start filling spreadsheet files "to win time" before the data model is ready then let me tell you that you'll be damn sure to do something wrong. Don't start filling your data until you have a nominal way to fill it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ordering development
&lt;/h3&gt;

&lt;p&gt;Let's say your Spotify-like app has 3 main branches with the two core features in bold.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User management

&lt;ul&gt;
&lt;li&gt;Login&lt;/li&gt;
&lt;li&gt;Logout&lt;/li&gt;
&lt;li&gt;Lost password&lt;/li&gt;
&lt;li&gt;Profile management&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Music

&lt;ul&gt;
&lt;li&gt;Explore catalog&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Play music&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Billing

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Credit card management&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Receipts download&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;In which order do you make those?&lt;/p&gt;

&lt;p&gt;You could split the thing technically and say that you create a music engine, a music catalog, a billing engine and then slap a GUI on top of that all.&lt;/p&gt;

&lt;p&gt;You could also do things by order of absolute importance of the feature, so start with the billing then the music and the the user management.&lt;/p&gt;

&lt;p&gt;But what happens in either of those cases if you're late and you have to release the project &lt;em&gt;right now&lt;/em&gt;? Most importantly, what happens if you want to test what you did? In neither those orders features are self-standing. Your client can't test music playback if they can't find music first.&lt;/p&gt;

&lt;p&gt;In your planning, put &lt;a href="https://www.youtube.com/watch?v=SqGRnlXplx0"&gt;first things first&lt;/a&gt;, take the big blocks and put them out there. In that case, playing music and billing users. Then you need to make sure that you can reach those blocks. So before you start working on the music playback, you need users to be able to navigate to the music. And before that you need them to be able to login. However you don't really need them to be able to change password nor to fill their profile.&lt;/p&gt;

&lt;p&gt;Supposing that, while billing is the reason why this app exists, the reason why people will use it is because it plays music. Additionally, you can release it without billing functional, as your commercial policy is probably going to include having a free trial period.&lt;/p&gt;

&lt;p&gt;Considering all of this, I'd plan features in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login&lt;/li&gt;
&lt;li&gt;Explore catalog&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Play music&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Logout&lt;/li&gt;
&lt;li&gt;Lost password&lt;/li&gt;
&lt;li&gt;Profile management&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Credit card management&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Receipts download&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This way, starting from step 3, you always work on a usable product. Certainly incomplete but definitely usable. And yes one of the core features is pushed to the end but that's a conscious decision taken with business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing
&lt;/h2&gt;

&lt;p&gt;Now we've reached the final part of this article. How to actually develop your software in a way that hits the deadline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stick to the specifications
&lt;/h3&gt;

&lt;p&gt;Let's take a paragraph to emphasize on the importance of specifications. If specifications are not precise enough or if they move during the project, the following advices become very complicated to follow. The main if not only reason to change specifications midway is to reduce scope, as explained below. &lt;strong&gt;Bad specifications &lt;em&gt;will&lt;/em&gt; double or triple the time of a project&lt;/strong&gt; even if the most senior and battle-hardened developer is on that job.&lt;/p&gt;

&lt;h3&gt;
  
  
  One thing at a time
&lt;/h3&gt;

&lt;p&gt;Because I'm a big &lt;a href="https://dev.to/xowap/8-tips-from-john-wick-for-10x-developers-366p"&gt;John Wick project management&lt;/a&gt; fan, let's review what happens in this video from the beginning of John Wick 3:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/363017107" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As you can see, John Wick is being chased by mean guys that want to kill him. He gets inside a room where he finds old guns. He could do a ton of things but instead of panicking he just spends some time to create his perfect gun for this exact shot. And kills the other guy. He had little time, made a &lt;a href="https://en.wikipedia.org/wiki/Minimum_viable_product"&gt;MVP&lt;/a&gt; and stayed alive.&lt;/p&gt;

&lt;p&gt;The single most useful advice from this article is &lt;strong&gt;finish 100% of your task before moving to the next one&lt;/strong&gt;. You cannot leave anything pending. You cannot have &lt;em&gt;TODO&lt;/em&gt; comments in your code. You cannot have something bleeding waiting to be repaired. Once your mind goes elsewhere you forget things and they keep on bleeding forever.&lt;/p&gt;

&lt;p&gt;Of course the feasibility of this lies greatly in the choice of what a task is. You need to make sure that your tasks are small and attainable. That they are sealed airtight and don't hang together halfway with another task. Sometimes it's difficult, sometimes interdependencies make the size if the task pretty big but I've never met a case where the task I have to do doesn't fit in my head.&lt;/p&gt;

&lt;p&gt;Eventually, when the deadline becomes tight or simply if the company process are approximate, someone will lose their shit and ask you to go in two different directions at a time. You must ignore it and keep on doing one task at a time. In doubt think about this: if someone is running in all directions at once then they will forget whatever they ask of you because they cannot keep track of their nonesense. This way you can do what you decide and they won't be able to reproach it to you.&lt;/p&gt;

&lt;p&gt;Another side-effect of this is that you should not write code that you don't need now. All the code that you write is focused to implementing the current feature and future features don't matter at all. Maybe you can keep them in mind to avoid shooting yourself in the foot and orient some decisions but certainly don't write any code for future features. You will loose your focus doing so and get confused. Those future features might even change before you get there.&lt;/p&gt;

&lt;p&gt;The technique I recommend using is somehow inspired by the &lt;a href="https://en.wikipedia.org/wiki/Pomodoro_Technique"&gt;Pomodoro technique&lt;/a&gt;, however it is not time based. Rather, you should set a goal for the current step you are taking and do not deviate from it until it is done. When done then take a small break and move onto the next thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing testing testing
&lt;/h3&gt;

&lt;p&gt;I won't go into any kind of automated/manual testing recommendations but one thing is for sure: you need to test the fuck out of your software. Test it in all the ways it's not intended to work.&lt;/p&gt;

&lt;p&gt;Any glitch you see should alarm you. Try to reproduce said glitch and look at the code to see what happens. From personal experience, 90% of "weird glitches" turn out to be damn annoying bugs in production.&lt;/p&gt;

&lt;p&gt;Also, you need to get the client or at least some project manager to test your code on a daily basis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By finding bugs early you make sure that you have a healthy foundation to work on. The more bugs you have the harder it is to see a single one!&lt;/li&gt;
&lt;li&gt;By having your project manager test your work you make sure that you stick to the spec and thus don't drive into a weeks-long tunnel of misdirection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real-life data
&lt;/h3&gt;

&lt;p&gt;We've seen in the previous section that data must only be filled when there is a nominal way to fill the data. This also applies the other way around: don't ever test with mock data. You must test with data that is as real as possible in quantities that are as big as possible.&lt;/p&gt;

&lt;p&gt;If possible, even test with larger data sets than what you expect in production, just to make sure that it fits.&lt;/p&gt;

&lt;p&gt;The use of real-life data relates to the previous point about bugs. It's very easy to let bugs appears if you only ever work with the same set of data all the time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't trip
&lt;/h3&gt;

&lt;p&gt;Something that is also incredibly easy to do when doing software development is to trip on some technical difficulties and loose days for no apparent reason. This will happen quietly without you noticing, eating up hours of your time.&lt;/p&gt;

&lt;p&gt;The best way to avoid this is to regularly review your work and take a step back. Maybe you do it with a coworker, your project manager, your boss but several times a week just look at what you are doing and try to see if you are stuck.&lt;/p&gt;

&lt;p&gt;Another technique is that when you know you might get stuck because you are working on something new, it's always good to time-box your work. This, once again, gives you the opportunity to rectify trajectory before it is too late.&lt;/p&gt;

&lt;p&gt;If you are stuck then maybe you can change approach, call for help, clear your head with a day off or anything that feels right to you but you definitely have to do something.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with a late project
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Project_management_triangle"&gt;project management triangle&lt;/a&gt; is a well-known concept in project management. It'll make sense with what I just wrote above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p2rB9A7E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0bg5twhrrlb4sksxfbfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p2rB9A7E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0bg5twhrrlb4sksxfbfa.png" alt="The Project Management Triangle, quality is constrainted by scope, cost and time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we've seen with the Apollo program earlier, they had a scope, a deadline and a minimal quality (aka not killing astronauts). The reason why it ended up costing a lot of money is because it's the only adjustment factor they had to fit the box. That's also the reason why recent NASA efforts to go back to the moon will certainly not meet the deadline, as American taxpayers are probably not ready to support a money-eating monster.&lt;/p&gt;

&lt;h4&gt;
  
  
  Increasing cost
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Adding &lt;a href="https://en.wikipedia.org/wiki/Human_resources"&gt;human resources&lt;/a&gt; to a late software project makes it later&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In spite of &lt;a href="https://en.wikipedia.org/wiki/Brooks%27s_law"&gt;Brooke's law&lt;/a&gt; quoted right above, you &lt;em&gt;can&lt;/em&gt; add resources to a late project but you have to be very careful. There is two considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It will increase the cost of the project, so someone is going to have to pay for this. Maybe your client, maybe your company, maybe a shared responsibility, that's not really the concern for developers. What matters is that the cost increases.&lt;/li&gt;
&lt;li&gt;It &lt;em&gt;will&lt;/em&gt; slow down the current team because more people to talk to equals to more time spent not coding. This is why if you do that you must have a project that is already properly loosely coupled and have the new developers to work on those remote parts that don't really interact a lot with existing parts. You want the extra workforce to be able to compensate the productivity loss.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Increasing cost is an option but it has to be carefully used. In a lot of cases and especially if the project is small it's not possible to do so.&lt;/p&gt;

&lt;h4&gt;
  
  
  Increasing time
&lt;/h4&gt;

&lt;p&gt;Pushing the deadline may seem like an intuitive option but there is actually a lot of problems to be considered before doing so.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the client ready to accept a late project?&lt;/li&gt;
&lt;li&gt;Does your company have the bandwidth for this extra time spent on this project?&lt;/li&gt;
&lt;li&gt;Considering that you are going to do free work rather than paid work, it costs your company money.&lt;/li&gt;
&lt;li&gt;Dragging a project is very bad for the team morale, especially if it's more than 50% above the initial estimate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like the increase of cost, the increase of time has some drawbacks, including actually the increase of cost. That's really not a scenario you want to see happening.&lt;/p&gt;

&lt;h4&gt;
  
  
  Decreasing scope
&lt;/h4&gt;

&lt;p&gt;The good news is that as advised above you've done things one by one &lt;em&gt;and&lt;/em&gt; in a meaningful order. Which means that every single commit is a finished product. Maybe not all commits are equally tested but at least the intent is that every commit is a finished product.&lt;/p&gt;

&lt;p&gt;Which means that you can, right now, push your product into production and not worry that some parts are bleeding. Everything is all right.&lt;/p&gt;

&lt;p&gt;Now that's a big clean scope reduction however that is not necessarily the only leverage you have. Indeed, during the specification process some things were specified in a certain way but maybe if you think about it with the current experience of the project and a developer eye you can find shortcuts which would impede a little bit on the UX but would allow you to finish on time.&lt;/p&gt;

&lt;p&gt;In short, when you are late, two options offer to you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cut the scope at the current advancement status at the release date. Maybe you will finish those features later, maybe not. It's down to commercial negotiations.&lt;/li&gt;
&lt;li&gt;Simplify the remaining features so they are potentially less user-friendly but faster to complete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's put this a different way. What is on the paper you signed with your client is the deadline, the price and the broad scope of the project. Meaning that as long as your project does what it has to do, for the price that was agreed and before the deadline that was fixed then the fine details of the specifications are by far the most negotiable part of the project.&lt;/p&gt;

&lt;p&gt;Considering this, when your project is late you should probably find a way to cut down the scope because it is by far the less painful way of crossing the finish line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While estimating and planning are complicated tasks, it is part of our job to accomplish them. In fact, if we call ourselves software &lt;em&gt;engineers&lt;/em&gt; we should be able to hold the same level of accountability than other kind of engineers. If a bridge can get built on time then software should be deliverable within the deadline as well.&lt;/p&gt;

&lt;p&gt;This starts with making rough estimates at the beginning of your project and then narrowing down the plan as the specifications and code get produced while making sure that the budget and deadline are still consistent with what is being actually done.&lt;/p&gt;

&lt;p&gt;A pattern we've seen emerging at different stages of the project is that if you want to deliver your project sooner you have different adjustment variables you can play on but the safest and most efficient is definitely to play on the scope.&lt;/p&gt;

&lt;p&gt;The most important lesson is to carefully arrange your tasks and to accomplish them one by one so you keep the complexity of your work manageable and your project in a healthy shape.&lt;/p&gt;

&lt;p&gt;In the end our developer work is often more landing page than robots and lasers. I see many juniors that are disappointed by this outlook and many seniors that just hope to move into management. However our industry is plagued with projects &lt;a href="https://erikbern.com/2019/04/15/why-software-projects-take-longer-than-you-think-a-statistical-model.html"&gt;blowing up&lt;/a&gt; their estimates. Then if our job is so easy, let's try at least to deliver it on time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used the following articles as inspiration and proofing for this article. There is a lot of content that I don't endorse 100% but they make other interesting reads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/pbeekums/tradeoffs-of-time-estimates"&gt;Tradeoffs Of Time Estimates&lt;/a&gt; — Professor Beekums&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/edelvalle/on-dealing-with-deadlines-and-estimates-30n1"&gt;On dealing with deadlines and estimates&lt;/a&gt; — Eddy Ernesto del Valle Pino&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/yaakovellis/project-management-for-developers-101-nc7"&gt;Project Management for Developers 101&lt;/a&gt; — Yaakov Ellis&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/defmyfunc/how-to-move-to-noestimates-26jj"&gt;How to move to #noestimates&lt;/a&gt; — defmyfunc&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/dmelidonis/how-long-will-it-take-git-knows--32hm"&gt;How long will it take? Git knows.&lt;/a&gt; — Dimitrios Melidonis&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/jaybazuzi/status/1110923931764981760"&gt;Ways to #NoEstimates&lt;/a&gt; — Jay Bazuzi&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://philippe.bourgau.net/how-we-used-the-improvement-kata-to-gain-25-percent-of-productivity-part-3/"&gt;How we used the improvement kata to gain 25% of productivity&lt;/a&gt; — Philippe Bourgau&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.linkedin.com/pulse/estimating-things-terry-brown/"&gt;On Estimating Things&lt;/a&gt; — Terry Brown&lt;/li&gt;
&lt;li&gt;Software Estimation, Enterprise-Wide &lt;a href="https://www.ibm.com/developerworks/rational/library/jun07/temnenco/temnenco-pdf.pdf"&gt;part 1&lt;/a&gt; and &lt;a href="https://www.ibm.com/developerworks/rational/library/jul07/temnenco/temnenco-pdf.pdf"&gt;part 2&lt;/a&gt; — Vitalie Temnenco&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.computer.org/10.1109/52.589226"&gt;Risk Management Is Project Management for Adults&lt;/a&gt; — Tim Lister&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://erikbern.com/2019/04/15/why-software-projects-take-longer-than-you-think-a-statistical-model.html"&gt;Why software projects take longer than you think – a statistical model&lt;/a&gt; — Erik Bernhardsson&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.extremeprogramming.org/"&gt;Extreme Programming - A gentle introduction&lt;/a&gt; — Don Wells&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>estimates</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Asking for review on non-string regular expressions</title>
      <dc:creator>Rémy 🤖</dc:creator>
      <pubDate>Fri, 26 Jul 2019 20:12:59 +0000</pubDate>
      <link>https://dev.to/xowap/asking-for-review-on-non-string-regular-expressions-382a</link>
      <guid>https://dev.to/xowap/asking-for-review-on-non-string-regular-expressions-382a</guid>
      <description>&lt;p&gt;There is this idea I have and that I want to push forward about "non-string regular expressions". I explained things as I could on the repo.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Xowap" rel="noopener noreferrer"&gt;
        Xowap
      &lt;/a&gt; / &lt;a href="https://github.com/Xowap/nsre" rel="noopener noreferrer"&gt;
        nsre
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Non-String Regular Expressions
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Non-String Regular Expressions&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://travis-ci.org/Xowap/nsre" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/97fbed37efa4bb5a32ff9bed83f66170d3ab9e2fc4b4ef23bd9f09d9f251faff/68747470733a2f2f7472617669732d63692e6f72672f586f7761702f6e7372652e7376673f6272616e63683d646576656c6f70" alt="Build Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;NSRE (Non-String Regular Expressions) is a new spin at regular expressions
It's really abstract, even compared to regular expressions as you know them
but it's also pretty powerful for some uses.&lt;/p&gt;
&lt;p&gt;Here's the twist: what if regular expressions could, instead of matching just
character strings, match any sequence of anything?&lt;/p&gt;
&lt;div class="highlight highlight-source-python notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;nsre&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt;

&lt;span class="pl-s1"&gt;re&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;RegExp&lt;/span&gt;.&lt;span class="pl-en"&gt;from_ast&lt;/span&gt;(&lt;span class="pl-en"&gt;seq&lt;/span&gt;(&lt;span class="pl-s"&gt;'hello, '&lt;/span&gt;) &lt;span class="pl-c1"&gt;+&lt;/span&gt; (&lt;span class="pl-en"&gt;seq&lt;/span&gt;(&lt;span class="pl-s"&gt;'foo'&lt;/span&gt;) &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-en"&gt;seq&lt;/span&gt;(&lt;span class="pl-s"&gt;'bar'&lt;/span&gt;)))
&lt;span class="pl-k"&gt;assert&lt;/span&gt; &lt;span class="pl-s1"&gt;re&lt;/span&gt;.&lt;span class="pl-en"&gt;match&lt;/span&gt;(&lt;span class="pl-s"&gt;'hello, foo'&lt;/span&gt;)&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The main goal here is matching NLU grammars when there is several possible
interpretations of a single word, however there is a lot of other things that
you could do. You just need to understand what NSRE is and apply it to
something.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; — This is inspired by
&lt;a href="https://swtch.com/~rsc/regexp/regexp1.html" rel="nofollow noopener noreferrer"&gt;this article&lt;/a&gt; from Russ Cox
which explains how Thompson NFA work, except that I…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Xowap/nsre" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I'm looking for all kinds of feedbacks&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you understand what this is?&lt;/li&gt;
&lt;li&gt;Do you see applications for this?&lt;/li&gt;
&lt;li&gt;Does the API look nice?&lt;/li&gt;
&lt;li&gt;What features would you want to see around that?&lt;/li&gt;
&lt;li&gt;What would you want before using this in production?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>python</category>
      <category>regex</category>
    </item>
  </channel>
</rss>
