<?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: Hector Castro</title>
    <description>The latest articles on DEV Community by Hector Castro (@hector).</description>
    <link>https://dev.to/hector</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%2F67625%2Fa4c0894f-7da5-47c5-bd51-e5441ffd384f.jpg</url>
      <title>DEV Community: Hector Castro</title>
      <link>https://dev.to/hector</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hector"/>
    <language>en</language>
    <item>
      <title>Parsing Data in Rust with Nom</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Fri, 23 Dec 2022 05:00:00 +0000</pubDate>
      <link>https://dev.to/hector/parsing-data-in-rust-with-nom-2hfc</link>
      <guid>https://dev.to/hector/parsing-data-in-rust-with-nom-2hfc</guid>
      <description>&lt;p&gt;This is my third year participating in &lt;a href="https://adventofcode.com" rel="noopener noreferrer"&gt;Advent of Code&lt;/a&gt;, but the first using Rust! Since I’m new to the Rust ecosystem, I’ve been dependent on others to steer my third-party library selections. As an example, &lt;a href="https://adventofcode.com/2022/day/15" rel="noopener noreferrer"&gt;Day 15&lt;/a&gt; (like most days) presented some interesting string parsing requirements. Luckily, I was guided toward an excellent parser combinator library, affectionately named &lt;a href="https://docs.rs/nom/latest/nom/" rel="noopener noreferrer"&gt;nom&lt;/a&gt;, via Chris Biscardi&lt;sup id="fnref:1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beacon exclusion zone
&lt;/h2&gt;

&lt;p&gt;The Day 15 challenge requires you to track sensors, beacons, and their coordinates. The raw input for this looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sensor at x=2, y=18: closest beacon is at x=-2, y=15
Sensor at x=9, y=16: closest beacon is at x=10, y=16
Sensor at x=13, y=2: closest beacon is at x=15, y=3
Sensor at x=12, y=14: closest beacon is at x=10, y=16
Sensor at x=10, y=20: closest beacon is at x=10, y=16
Sensor at x=14, y=17: closest beacon is at x=10, y=16
Sensor at x=8, y=7: closest beacon is at x=2, y=10
Sensor at x=2, y=0: closest beacon is at x=2, y=10
Sensor at x=0, y=11: closest beacon is at x=2, y=10
Sensor at x=20, y=14: closest beacon is at x=25, y=17
Sensor at x=17, y=20: closest beacon is at x=21, y=22
Sensor at x=16, y=7: closest beacon is at x=15, y=3
Sensor at x=14, y=3: closest beacon is at x=15, y=3
Sensor at x=20, y=1: closest beacon is at x=15, y=3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this text is parsable with regular expressions, or a combination of well-placed string splits, using a parsing library helps break things down in a structured way (which can sometimes be beneficial for part 2 challenges).&lt;/p&gt;

&lt;p&gt;Presuming we have structs for &lt;code&gt;Sensor&lt;/code&gt; and &lt;code&gt;Beacon&lt;/code&gt; that look like the ones below, we can start building out the parsing logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Sensor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Beacon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i64&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;h2&gt;
  
  
  Parsing with Nom
&lt;/h2&gt;

&lt;p&gt;First, we’ll parse out each line of input, along with the part of the line relevant to either a &lt;code&gt;Sensor&lt;/code&gt; or a &lt;code&gt;Beason&lt;/code&gt;. Second, we’ll parse out the coordinates and populate them into instances of &lt;code&gt;Sensor&lt;/code&gt; and &lt;code&gt;Beacon&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the first part, everything is contained in a function that takes the raw input as a string slice (&lt;code&gt;&amp;amp;str&lt;/code&gt;) and returns an &lt;code&gt;IResult&lt;/code&gt;. An &lt;code&gt;IResult&lt;/code&gt; is a container for the result of a &lt;code&gt;nom&lt;/code&gt; parsing function. The string slice component of an &lt;code&gt;IResult&lt;/code&gt; is the remaining unparsed input, and the &lt;code&gt;Vec(Sensor, Beacon)&lt;/code&gt; is our expected parsing result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;IResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sensor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Beacon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;separated_list1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;line_ending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;preceded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sensor at "&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;separated_pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)|&lt;/span&gt; &lt;span class="n"&gt;Sensor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
                &lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;": closest beacon is at "&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)|&lt;/span&gt; &lt;span class="n"&gt;Beacon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reports&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;Inside the &lt;code&gt;map&lt;/code&gt; function, we start off with &lt;code&gt;separated_list1&lt;/code&gt;, which helps us break up the input into lines. The first argument is &lt;code&gt;line_ending&lt;/code&gt;, which matches line endings of both the &lt;code&gt;\n&lt;/code&gt; and &lt;code&gt;\r\n&lt;/code&gt; variety. The second argument starts with &lt;code&gt;preceded&lt;/code&gt;, which isolates everything after the &lt;code&gt;Sensor at&lt;/code&gt; tag in the line and supplies it to &lt;code&gt;separated_pair&lt;/code&gt;. &lt;code&gt;separated_pair&lt;/code&gt; in turn helps parse out what is on either side of the &lt;code&gt;: closest beacon is at&lt;/code&gt; tag. In this case, those are the coordinate pairs for &lt;code&gt;Sensor&lt;/code&gt; and &lt;code&gt;Beacon&lt;/code&gt;, respectively. To parse them, we’ll define another function called &lt;code&gt;position&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;position&lt;/code&gt; function helps extract the values of coordinate pairs. As you can see, it has similar arguments to &lt;code&gt;map&lt;/code&gt;, and an &lt;code&gt;IResult&lt;/code&gt; return value. However, the types in the &lt;code&gt;IResult&lt;/code&gt; are a bit different here. The second argument is a tuple, for the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates, both &lt;code&gt;i64&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;IResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;separated_pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;preceded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x="&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nn"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;preceded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"y="&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nn"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;input&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;Right away, we jump into &lt;code&gt;separated_pair&lt;/code&gt; again. This parses out both sides of the &lt;code&gt;,&lt;/code&gt;, while &lt;code&gt;preceded&lt;/code&gt; isolates the value after either &lt;code&gt;x=&lt;/code&gt; or &lt;code&gt;y=&lt;/code&gt;. The second argument of &lt;code&gt;preceded&lt;/code&gt; is another parsing function—a &lt;code&gt;character::complete::i64&lt;/code&gt;, which matches the coordinate integer value.&lt;/p&gt;

&lt;p&gt;Coming back to the &lt;code&gt;map&lt;/code&gt; function, we (somewhat confusingly) call &lt;code&gt;map&lt;/code&gt; on the &lt;code&gt;position&lt;/code&gt; parsing result to get the parsed values. That allows us to destructure the tuple and use the values to construct the &lt;code&gt;Sensor&lt;/code&gt; and &lt;code&gt;Beacon&lt;/code&gt; struct literals.&lt;/p&gt;

&lt;p&gt;Now, if we use the &lt;code&gt;dbg!&lt;/code&gt; macro on the result of a call to &lt;code&gt;map&lt;/code&gt; with test input, we should see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Sensor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;Beacon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Sensor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;Beacon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Look at that beautifully structured data!&lt;/p&gt;

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

&lt;p&gt;Reasonably painless, and well-structured—that’s parsing data with Rust and Nom! If you’re interested in taking a closer look at Nom, I encourage you to review this handy &lt;a href="https://github.com/Geal/nom/blob/main/doc/choosing_a_combinator.md" rel="noopener noreferrer"&gt;list&lt;/a&gt; of its available parsers and combinators.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I highly recommend checking out Chris’ phenomenal Advent of Code solution &lt;a href="https://www.youtube.com/playlist?list=PLWtPciJ1UMuBNTifxm5ADY65SkAdwoQiL" rel="noopener noreferrer"&gt;videos&lt;/a&gt;. I could not have dreamt of a better resource to get up-to-speed quickly, with Rust. ↩
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>python</category>
      <category>adventofcode</category>
      <category>parsing</category>
    </item>
    <item>
      <title>Every Other Friday Off Work Schedule</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Mon, 27 Jun 2022 04:00:00 +0000</pubDate>
      <link>https://dev.to/hector/every-other-friday-off-work-schedule-3n33</link>
      <guid>https://dev.to/hector/every-other-friday-off-work-schedule-3n33</guid>
      <description>&lt;p&gt;For the last six months, I’ve adopted a work schedule where you tally up extra hours in the first nine days of a two-week range, and take the second Friday off (also known as a 9/80 work schedule&lt;sup id="fnref:1"&gt;1&lt;/sup&gt;). Below is a diagram that illustrates one way to do it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ECzAnm-f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hector.dev/assets/resized/example-alternate-work-week-schedule-800x492.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ECzAnm-f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hector.dev/assets/resized/example-alternate-work-week-schedule-800x492.png" alt="Alternate Work Week Schedule Example" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I first adopted this work schedule, I didn’t think I’d value it as much as my peers. For better or worse (increasingly, worse), I’ve come from a long line of work environments where long hours were rewarded. Every other Friday off? Sure, that’s nice—but that’s not for &lt;em&gt;leaders&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;However, after a month of practice, it &lt;em&gt;easily&lt;/em&gt; became one of the best work schedule arrangements I’ve ever participated in. Below are some supporting reasons why (from the perspective of an employee—me). If you consider yourself a forward-thinking leader, and have the authority to implement an alternative work schedule like this for your teams, please give it some serious consideration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Relief in knowing others are off too
&lt;/h2&gt;

&lt;p&gt;This may be a side effect of the previous work cultures I referenced above, but for me, I enjoy days off &lt;em&gt;much more&lt;/em&gt; when I know other members of the team are off too. The probability of having your day interrupted by a chat DM goes down significantly. There is also a reduced concern of missing previously scheduled meetings or timely emails.&lt;/p&gt;

&lt;h2&gt;
  
  
  More opportunity for decompression
&lt;/h2&gt;

&lt;p&gt;The older I get, the more responsibilities I assume outside of work. Increasingly, the weekend is less a block of time to unwind before the next work week, and more &lt;em&gt;the only&lt;/em&gt; opportunity to meet personal obligations that require more time than weekday evenings afford.&lt;/p&gt;

&lt;p&gt;Maybe it’s cleaning out the garage, or rearranging the home office, or picking out a nice gift for a loved one. Whatever it is, now there is a whole additional day every two weeks to get it done. That leaves the traditional weekend days with more of an opportunity for much-needed decompression.&lt;/p&gt;

&lt;h2&gt;
  
  
  More quality time with my kid
&lt;/h2&gt;

&lt;p&gt;This is a bit biased toward folks with younger kids (i.e., not yet in school, or not yet in a &lt;em&gt;serious&lt;/em&gt; grade), but the time I spend with my kid on these Fridays off is much higher quality than that of a usual weekend.&lt;/p&gt;

&lt;p&gt;When we plan to go somewhere, like the aquarium or a museum, it is much easier to get tickets. There are also generally a lot less people around (❤️ introverts). It’s enabled us to comfortably enjoy experiences like bowling, mini golfing, and wizarding&lt;sup id="fnref:2"&gt;2&lt;/sup&gt; during a pandemic.&lt;/p&gt;

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

&lt;p&gt;A 9/80-style work schedule is personally, very compelling. It unlocks a level of work/life balance I haven’t experienced in a work setting since I started working from home.&lt;/p&gt;

&lt;p&gt;It may not be for everyone, though. Work schedule changes require a base level of individual and team-level maturity. But, if that’s present, most software development teams should be able to adopt a work schedule change like this and not miss a beat.&lt;/p&gt;

&lt;p&gt;In 2022, hiring for software-focused roles is tough and differentiators are hard to come by. An alternative work schedule that improves work/life balance and doesn’t compromise the business can go a long way on the recruiting front.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://toggl.com/track/9-80-work-schedule/"&gt;What is a 9/80 work schedule?&lt;/a&gt; ↩&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.harrypotterexhibition.com"&gt;Harry Potter: The Exhibition&lt;/a&gt; ↩&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>productivity</category>
      <category>motivation</category>
      <category>management</category>
    </item>
    <item>
      <title>Turning Lemons into Topologically Sorted Lemonade</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Fri, 09 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/hector/turning-lemons-into-topologically-sorted-lemonade-5g9</link>
      <guid>https://dev.to/hector/turning-lemons-into-topologically-sorted-lemonade-5g9</guid>
      <description>&lt;p&gt;In a recent interview, I was asked to pair on a coding problem. Like most live coding exercises, I didn't do very well. So, in an effort to redeem myself (in the eyes of myself), I studied up on the problem and worked through several solutions.&lt;/p&gt;

&lt;p&gt;Hopefully, you don't find yourself in a similar situation. But, if you do, I hope reading through these solutions helps you fair better than I did!&lt;/p&gt;

&lt;h2&gt;
  
  
  Courses and prerequisites
&lt;/h2&gt;

&lt;p&gt;Without further ado, the pairing exercise problem statement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given a set of courses and a corresponding set prerequisites, produce a valid ordering of courses such that the courses can be taken in that order without bypassing any of the prerequisites (there are multiple correct solutions).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And, using a Python dictionary, the input data:&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="n"&gt;COURSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Algebra 1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="s"&gt;"Algebra 2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Algebra 1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"English 1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="s"&gt;"English 2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"English 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"History 1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"English 3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"English 2"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"English 4"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"English 3"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"History 1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="s"&gt;"History 2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"History 1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"Pre-Calculus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Algebra 2"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"Statistics 1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Algebra 1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"Statistics 2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Statistics 1"&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;Given the input above, a valid ordering of courses 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="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'History 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'History 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Algebra 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Statistics 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Statistics 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Algebra 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Pre-Calculus'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the problem defined, let’s look at some solutions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sorting out the correct terminology
&lt;/h2&gt;

&lt;p&gt;I had a sense that the solution to this problem involved modeling the data with a graph data structure, but I wasn’t sure what to do with it after that. So, I started looking for graph related libraries in Python, which led me to the most excellent &lt;a href="https://networkx.org/"&gt;NetworkX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After navigating the NetworkX &lt;a href="https://networkx.org/documentation/stable/reference/index.html"&gt;API documentation&lt;/a&gt; a bit, I noticed an entire section dedicated to &lt;a href="https://networkx.org/documentation/stable/reference/algorithms/index.html"&gt;algorithms&lt;/a&gt;. Under the algorithms section was a subsection specific to &lt;a href="https://networkx.org/documentation/stable/reference/algorithms/dag.html"&gt;Directed Acyclic Graphs (DAGs)&lt;/a&gt;. The reference to DAGs caught my eye because DAGs are often used to model data processing workflows with complex &lt;em&gt;dependencies&lt;/em&gt;. In the problem statement above, the course &lt;em&gt;prerequisites&lt;/em&gt; are a lot like data processing workflow &lt;em&gt;dependencies&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Continuing through the DAG related algorithms, the description for &lt;code&gt;topological_sort(G)&lt;/code&gt; stood out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A topological sort is a nonunique permutation of the nodes such that an edge from &lt;code&gt;u&lt;/code&gt; to &lt;code&gt;v&lt;/code&gt; implies that &lt;code&gt;u&lt;/code&gt; appears before &lt;code&gt;v&lt;/code&gt; in the topological sort order.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sounds promising! An edge can be produced by connecting a course &lt;code&gt;v&lt;/code&gt; to a prerequisite &lt;code&gt;u&lt;/code&gt;. If a topological sort can help ensure &lt;code&gt;u&lt;/code&gt; appears before &lt;code&gt;v&lt;/code&gt; in an ordering, then it aligns with our goal. Let’s give it a spin!&lt;/p&gt;

&lt;h2&gt;
  
  
  NetworkX to the rescue
&lt;/h2&gt;

&lt;p&gt;Following the guidance in the &lt;code&gt;topological_sort(G)&lt;/code&gt; description, I iterated over each combination of course and prerequisite and created an edge between them with the &lt;code&gt;add_edge&lt;/code&gt; method:&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;import&lt;/span&gt; &lt;span class="nn"&gt;networkx&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DiGraph&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prerequisites&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;COURSES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prerequisite&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prerequisites&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prerequisite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, the only thing left to do was to call &lt;code&gt;topological_sort(G)&lt;/code&gt; with the &lt;code&gt;DiGraph&lt;/code&gt; as an argument:&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="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;topological_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'History 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'History 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Algebra 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Statistics 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Statistics 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Algebra 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Pre-Calculus'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That ordering looks valid to me!&lt;/p&gt;

&lt;h2&gt;
  
  
  The standard library can do it too
&lt;/h2&gt;

&lt;p&gt;After identifying the class of algorithm necessary to solve the problem (i.e., topological sort), I began to use the term in searches for other types of solutions. Eventually, that led me to a module in the Python standard library called &lt;code&gt;graphlib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;According to the Python &lt;a href="https://github.com/python/cpython/commit/99e6c260d60655f3d2885af545cbc220b808d492"&gt;commit history&lt;/a&gt;, &lt;code&gt;graphlib&lt;/code&gt; is pretty new (added in Python 3.9). It promises to provide a set of functionality for operating on graph-like structures. But, right now it only appears to have one class worth of functionality. Luckily for us, that one class is called &lt;code&gt;TopologicalSorter&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Instantiating the class takes an argument, &lt;code&gt;graph&lt;/code&gt;, which:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;…must be a dictionary representing a directed acyclic graph where the keys are nodes and the values are iterables of all predecessors of that node in the graph (the nodes that have edges that point to the value in the key).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hm. That sounds very similar to the &lt;code&gt;COURSES&lt;/code&gt; data structure defined above. Let’s pass it through in the Python interpreter, and then call the &lt;code&gt;static_order&lt;/code&gt; method:&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;graphlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TopologicalSorter&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TopologicalSorter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COURSES&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;static_order&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'Algebra 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'History 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Algebra 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Statistics 1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'History 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Pre-Calculus'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'Statistics 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'English 4'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whoa! A different ordering from the one above, but still valid. We can even use another NetworkX function to confirm the &lt;code&gt;TopologicalSorter&lt;/code&gt; solution is valid by checking it against &lt;em&gt;all&lt;/em&gt; possible topological sorts, as reported by &lt;code&gt;all_topological_sorts(G)&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;solution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TopologicalSorter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COURSES&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;static_order&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;solution&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all_topological_sorts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent—looks like we are two-for-two so far!&lt;/p&gt;

&lt;h2&gt;
  
  
  Show your work
&lt;/h2&gt;

&lt;p&gt;Using algorithms built-in to libraries like NetworkX and &lt;code&gt;graphlib&lt;/code&gt; is fun and all, but how would we solve this problem algorithmically? Well, according to Wikipedia, there are two existing algorithms to draw from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm"&gt;Kahn’s algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search"&gt;Depth-first search algorithm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m going to focus on Kahn’s algorithm—primarily because it &lt;em&gt;doesn’t&lt;/em&gt; involve recursion. Although recursion is a fascinating technique, I don’t see it too often in day-to-day code, and I find that it creates confusion in most engineers (myself included).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If the variable names below become confusing, please refer to the pseudocode in the Wikipedia link for Kahn's algorithm. I'm trying to match the variable names to that, so it is easier to follow along.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To start things off, let’s use the Python interpreter to define &lt;code&gt;L&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt;. Here, &lt;code&gt;L&lt;/code&gt; is being set up to contain the final course ordering and &lt;code&gt;S&lt;/code&gt; made up of all the nodes in the graph with zero edges pointing to them (e.g., &lt;code&gt;in_degree == 0&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_degree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to create a &lt;code&gt;while&lt;/code&gt; loop set to run until &lt;code&gt;S&lt;/code&gt; is empty. Inside, we pop &lt;code&gt;n&lt;/code&gt; from &lt;code&gt;S&lt;/code&gt; and immediately append it to &lt;code&gt;L&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, we need to identify all nodes connected to &lt;code&gt;n&lt;/code&gt; so that we can remove each edge from the graph, one-by-one. As they’re removed, we check to see if there are any remaining edges pointing to the node &lt;code&gt;m&lt;/code&gt;. If not, append &lt;code&gt;m&lt;/code&gt; to &lt;code&gt;S&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="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;edges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;neighbors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_degree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After these steps are complete, &lt;code&gt;L&lt;/code&gt; should contain a valid course ordering. Again, we can confirm with &lt;code&gt;all_topological_sorts(G)&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all_topological_sorts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Now we have three different solutions to determine a valid ordering for a set of courses and prerequisites. Enjoy all the flavors of topologically sorted lemonade! 🍋&lt;/p&gt;

</description>
      <category>python</category>
      <category>career</category>
      <category>algorithms</category>
      <category>programming</category>
    </item>
    <item>
      <title>Twelve-Factor Methodology Applied to a Django App</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Tue, 16 Mar 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/hector/twelve-factor-methodology-applied-to-a-django-app-451g</link>
      <guid>https://dev.to/hector/twelve-factor-methodology-applied-to-a-django-app-451g</guid>
      <description>&lt;p&gt;In the past few weeks, I’ve participated in a handful of DevOps/Site Reliability Engineer (SRE) interviews. Several interviewers have asked for guidelines configuring and operating &lt;a href="https://en.wikipedia.org/wiki/Cloud_native_computing"&gt;cloud-native&lt;/a&gt; applications. My mind immediately goes to the &lt;a href="https://12factor.net"&gt;Twelve-Factor App methodology&lt;/a&gt;, originally created by the folks who built &lt;a href="https://www.heroku.com"&gt;Heroku&lt;/a&gt;—one of the first publicly accessible platforms as a service (PaaS).&lt;/p&gt;

&lt;p&gt;Combined, the points serve to abstract applications from the infrastructure they run on, paving the way for configurability, scalability, and reliability. To illustrate how this works in practice, I set up a Django application and use it to explain how each 12 Factor point applies. I hope you find it useful!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Codebase&lt;/li&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;li&gt;Config&lt;/li&gt;
&lt;li&gt;Backing services&lt;/li&gt;
&lt;li&gt;Build, release, run&lt;/li&gt;
&lt;li&gt;Processes&lt;/li&gt;
&lt;li&gt;Port binding&lt;/li&gt;
&lt;li&gt;Concurrency&lt;/li&gt;
&lt;li&gt;Disposability&lt;/li&gt;
&lt;li&gt;Dev/prod parity&lt;/li&gt;
&lt;li&gt;Logs&lt;/li&gt;
&lt;li&gt;Admin processes&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : The code snippets in the following sections do not chain together perfectly. The snippets are there primarily to help communicate what’s going on in ways that only code can.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Codebase
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;codebase&lt;/em&gt; is the complete source material of a given software program or application. Its structure will vary based on technology, but for a Django application called &lt;code&gt;mysite&lt;/code&gt; created with &lt;code&gt;django-admin startproject&lt;/code&gt;, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git init
Initialized empty Git repository in /home/hector/Projects/django-blog/.git/
$ git add .
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached &amp;lt;file&amp;gt;..." to unstage)
        new file: .gitignore
        new file: Pipfile
        new file: Pipfile.lock
        new file: mysite/manage.py
        new file: mysite/mysite/ __init__.py
        new file: mysite/mysite/asgi.py
        new file: mysite/mysite/settings.py
        new file: mysite/mysite/urls.py
        new file: mysite/mysite/wsgi.py
        new file: setup.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent—we have ourselves a &lt;em&gt;codebase&lt;/em&gt;! We’ll gradually cover converting &lt;em&gt;codebases&lt;/em&gt; into &lt;em&gt;deploys&lt;/em&gt; in the following sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

&lt;p&gt;Applications have &lt;em&gt;dependencies&lt;/em&gt;. 12 Factor wants us to explicitly declare these dependencies so they can be managed in a repeatable way. The first step toward achieving this happens with a &lt;code&gt;Pipfile&lt;/code&gt;. It was created by a Python dependency management tool called &lt;code&gt;pipenv&lt;/code&gt; after the following commands were run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipenv install django~=3.1
pipenv install black --dev --pre # --pre is needed because of black's versioning scheme
pipenv install flake8~=3.8 --dev
pipenv install isort~=5.7 --dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inside of a &lt;code&gt;Pipfile&lt;/code&gt; is written in &lt;a href="https://toml.io/"&gt;Tom’s Obvious Minimal Language (TOML)&lt;/a&gt; and contains a manifest of the Python dependencies needed for a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
django = "~=3.1"

[dev-packages]
black = "*"
flake8 = "~=3.8"
isort = "~=5.7"

[requires]
python_version = "3.8"

[pipenv]
allow_prereleases = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nowadays, we try to take this a step further by capturing &lt;em&gt;all&lt;/em&gt; the necessary application dependencies in a container image. In most cases, the pursuit of creating a container image leads to using &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;, which implies the addition of a &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.8

ENV PYTHONUNBUFFERED=1

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

COPY ./Pipfile* .
RUN pip install pipenv
RUN pipenv install --system --deploy --ignore-pipfile
COPY ./mysite .

ENTRYPOINT ["python", "manage.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure things are in working order, we can build and test the container image using the following commands. Here, the &lt;code&gt;runserver&lt;/code&gt; argument launches the Django development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t mysite .
$ docker run --rm mysite runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
March 01, 2021 - 20:45:33
Django version 3.1.7, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks good! We now have everything needed to spin up the application captured in a container image. In addition, we have all the associated instructions to build the image defined in a declarative way (e.g., &lt;code&gt;Pipfile&lt;/code&gt;, &lt;code&gt;Dockerfile&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Config
&lt;/h2&gt;

&lt;p&gt;In the Twelve-Factor world, &lt;em&gt;configuration&lt;/em&gt; is defined as anything that can vary between &lt;em&gt;deploys&lt;/em&gt; of a &lt;em&gt;codebase&lt;/em&gt;. This allows a single &lt;em&gt;codebase&lt;/em&gt; to be deployed into different environments without customization. Some examples of &lt;em&gt;configuration&lt;/em&gt; include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connection strings to the database, Memcached, and other backing services.&lt;/li&gt;
&lt;li&gt;Credentials to external services (e.g., Amazon S3, Google Maps, etc.).&lt;/li&gt;
&lt;li&gt;Information about the target environment (e.g., &lt;code&gt;Staging&lt;/code&gt; vs. &lt;code&gt;Production&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we’ve identified the configuration for our application, we need to work toward making it consumable via &lt;a href="https://en.wikipedia.org/wiki/Environment_variable"&gt;environment variables&lt;/a&gt;. In the example below, we focus on changing the way Django’s &lt;code&gt;SECRET_KEY&lt;/code&gt; and &lt;code&gt;DEBUG&lt;/code&gt; settings are set in &lt;code&gt;settings.py&lt;/code&gt; (the home for all Django configuration settings).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/mysite/mysite/settings.py b/mysite/mysite/settings.py
index d541c62..3a99d45 100644
--- a/mysite/mysite/settings.py
+++ b/mysite/mysite/settings.py
@@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/3.1/topics/settings/
 For the full list of settings and their values, see
 https://docs.djangoproject.com/en/3.1/ref/settings/
 """
-
+import os
 from pathlib import Path

 # Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -20,10 +20,10 @@ BASE_DIR = Path( __file__ ).resolve().parent.parent
 # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/

 # SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = "#v5hnkypk39qex@9zb2j2as3n9f7)jgvz05*9t&amp;amp;0@2y$kx$7lw"
+SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "secret")

 # SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
+DEBUG = os.getenv("DJANGO_ENV") == "Development"

 ALLOWED_HOSTS = []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we made use of the Python standard library &lt;code&gt;os&lt;/code&gt; module to help us read configuration from the environment. Now, the two settings can be more easily reconfigured across &lt;em&gt;deploys&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To prove it works, we can change the environment with the &lt;code&gt;-e&lt;/code&gt; flag of &lt;code&gt;docker run&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t mysite .
$ docker run --rm \
    -e DJANGO_SECRET_KEY="dev-secret" \
    -e DJANGO_ENV="Development" \
    mysite runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
March 01, 2021 - 21:25:57
Django version 3.1.7, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
^C%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK. Everything continued to work the way it was working before. Now, let’s see what happens if we try to make &lt;code&gt;DJANGO_ENV=Production&lt;/code&gt;, which will cause the &lt;code&gt;DEBUG&lt;/code&gt; setting to evaluate to &lt;code&gt;False&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run --rm \
    -e DJANGO_SECRET_KEY="prod-secret" \
    -e DJANGO_ENV="Production" \
    mysite runserver
CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aha! This &lt;code&gt;CommandError&lt;/code&gt; looks ominous, but it is an indicator that our change of &lt;code&gt;DJANGO_ENV&lt;/code&gt; made its way into the application’s execution environment successfully!&lt;/p&gt;

&lt;h2&gt;
  
  
  Backing services
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;backing service&lt;/em&gt; is any service the application consumes over the network as part of its normal operation. Emphasis is placed on minimizing the distinction between local and third-party backing services such that the application can’t tell the difference between them.&lt;/p&gt;

&lt;p&gt;As an example, say you have a PostgreSQL database instance running on your workstation that’s connected to your application to persist data. Later, when it comes time to deploy to production, the same approach to configuring the local PostgreSQL instance should work when it gets swapped out for an Amazon Relational Database Service (RDS) instance.&lt;/p&gt;

&lt;p&gt;To achieve this with Django, we need to change the way connectivity to the database is configured. That happens via the &lt;code&gt;DATABASES&lt;/code&gt; dictionary in &lt;code&gt;settings.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/mysite/mysite/settings.py b/mysite/mysite/settings.py
index 3a99d45..fcff52a 100644
--- a/mysite/mysite/settings.py
+++ b/mysite/mysite/settings.py
@@ -75,8 +75,12 @@ WSGI_APPLICATION = "mysite.wsgi.application"

 DATABASES = {
     "default": {
- "ENGINE": "django.db.backends.sqlite3",
- "NAME": BASE_DIR / "db.sqlite3",
+ "ENGINE": "django.db.backends.postgresql",
+ "NAME": os.getenv("POSTGRES_DB"),
+ "USER": os.getenv("POSTGRES_USER"),
+ "PASSWORD": os.getenv("POSTGRES_PASSWORD"),
+ "HOST": os.getenv("POSTGRES_HOST"),
+ "PORT": os.getenv("POSTGRES_PORT"),
     }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we modified &lt;code&gt;DATABASES&lt;/code&gt; so that all the necessary settings for the &lt;code&gt;default&lt;/code&gt; database are pulled from the environment. Now, it doesn’t matter if the application is launched with &lt;code&gt;HOST&lt;/code&gt; equal to &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;mysite.123456789012.us-east-1.rds.amazonaws.com&lt;/code&gt;. In either case, the application should be able to connect to the database successfully using the settings found in the environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build, release, run
&lt;/h2&gt;

&lt;p&gt;In the Dependencies section we produced a &lt;em&gt;build&lt;/em&gt; in the form of a container image. But, we also need a unique label to identify and differentiate between versions of the container image. Uniqueness can come in the form of a timestamp, or an incrementing number, but I personally like to use Git revisions. Below is an example that uses the current Git revision to tag a container image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ # Get a reference to the latest commit of the current
$ # branch and make it short (only 7 characters long).
$ export GIT_COMMIT="$(git rev-parse --short HEAD)"
$ docker build -t "mysite:$GIT_COMMIT" .
$ docker images | grep mysite
mysite e87b8c4 4f3dc2772c57 2 minutes ago 978MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the output, the reference &lt;code&gt;mysite:e87b8c4&lt;/code&gt; is unique to the container image we built. If we make additional changes to the &lt;em&gt;codebase&lt;/em&gt; and commit them to the underlying Git repository, following these same steps will result in a new container image with a new unique reference.&lt;/p&gt;

&lt;p&gt;Next, we need to combine the container image &lt;em&gt;build&lt;/em&gt; above with a relevant set of &lt;em&gt;configuration&lt;/em&gt; to produce a &lt;em&gt;release&lt;/em&gt;. Here, we’ll use a lightweight &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt; configuration file to describe the connection between the two (&lt;em&gt;builds&lt;/em&gt; and &lt;em&gt;releases&lt;/em&gt;) in a declarative way. In a production system, you’d likely do something similar using a Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;deployment&lt;/a&gt; or an Amazon ECS &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html"&gt;task definition&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3"
services:
  web:
    image: mysite:e87b8c4
    environment:
      - POSTGRES_HOST=mysite.123456789012.us-east-1.rds.amazonaws.com
      - POSTGRES_PORT=5432
      - POSTGRES_USER=mysite
      - POSTGRES_PASSWORD=mysite
      - POSTGRES_DB=mysite
      - DJANGO_ENV=Staging
      - DJANGO_SECRET_KEY=staging-secret
    command:
      - runserver
      - "0.0.0.0:8000"
    ports:
      - "8000:8000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bit of Docker Compose configuration ties together the &lt;code&gt;mysite:e87b8c4&lt;/code&gt; &lt;em&gt;build&lt;/em&gt; with a set of environment specific &lt;em&gt;configuration&lt;/em&gt; to produce a &lt;em&gt;release&lt;/em&gt;. If the container image and Docker Compose configuration snippet are available on the same host, then the application is ready for immediate execution on that host.&lt;/p&gt;

&lt;p&gt;Lastly, we have the &lt;em&gt;run&lt;/em&gt; stage. For Docker Compose, that’s as simple as using &lt;code&gt;docker-compose up&lt;/code&gt; to launch the &lt;code&gt;web&lt;/code&gt; service. For a more sophisticated container orchestration system, several more steps would likely be involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The container image is published to a centrally accessible container registry.&lt;/li&gt;
&lt;li&gt;The deployment manifest is submitted for evaluation to a container scheduler.&lt;/li&gt;
&lt;li&gt;Compute is connected to the container scheduler with adequate resources to place instances of the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Processes
&lt;/h2&gt;

&lt;p&gt;The Twelve-Factor methodology emphasizes applications as stand-alone &lt;em&gt;processes&lt;/em&gt; because when they share nothing, they can be made to more easily scale horizontally. Therefore, striving to store all dynamic state in a &lt;em&gt;backing service&lt;/em&gt; (e.g., a database) to make a process stateless is important.&lt;/p&gt;

&lt;p&gt;However, sometimes whole components of an application need to be dynamically built, like its associated CSS and JavaScript. To be truly stateless, we want to generate those components during the &lt;em&gt;build&lt;/em&gt; phase and capture them in the container image.&lt;/p&gt;

&lt;p&gt;Django has &lt;a href="https://docs.djangoproject.com/en/3.1/howto/static-files/deployment/"&gt;several built-in mechanisms&lt;/a&gt; to handle static assets, but I prefer to use a third-party library named &lt;a href="http://whitenoise.evans.io/en/stable/index.html"&gt;WhiteNoise&lt;/a&gt;. Primarily, because it helps package both the application and its supporting static assets together in a way that enables thinking about a &lt;em&gt;deploy&lt;/em&gt; as an atomic operation.&lt;/p&gt;

&lt;p&gt;After installing WhiteNoise using &lt;code&gt;pipenv&lt;/code&gt; with a command similar to the one we used in Dependencies to install Django, we need to configure the Django application to use WhiteNoise for static asset management. Here, we inject WhiteNoise into the Django &lt;code&gt;INSTALLED_APPS&lt;/code&gt; and &lt;code&gt;MIDDLEWARE&lt;/code&gt; hierarchy to take over static asset management in development and non-development environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/mysite/mysite/settings.py b/mysite/mysite/settings.py
index 216452b..f4e32c6 100644
--- a/mysite/mysite/settings.py
+++ b/mysite/mysite/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+ "whitenoise.runserver_nostatic",
     "django.contrib.admin",
     "django.contrib.auth",
     "django.contrib.contenttypes",
@@ -41,6 +42,7 @@ INSTALLED_APPS = [

 MIDDLEWARE = [
     "django.middleware.security.SecurityMiddleware",
+ "whitenoise.middleware.WhiteNoiseMiddleware",
     "django.contrib.sessions.middleware.SessionMiddleware",
     "django.middleware.common.CommonMiddleware",
     "django.middleware.csrf.CsrfViewMiddleware",
@@ -122,3 +124,7 @@ USE_TZ = True
 # https://docs.djangoproject.com/en/3.1/howto/static-files/

 STATIC_URL = "/static/"
+
+STATIC_ROOT = "/static"
+
+STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two settings at the bottom (&lt;code&gt;STATIC_ROOT&lt;/code&gt; and &lt;code&gt;STATICFILES_STORAGE&lt;/code&gt;) tell Django where to store the collected files on the container image file system and what preprocessing operations to apply.&lt;/p&gt;

&lt;p&gt;Next, we need to ensure that Django preprocesses all static assets as part of the container image build process. For Django, that means adding an invocation of the &lt;code&gt;collectstatic&lt;/code&gt; command to the container image build instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/Dockerfile b/Dockerfile
index 4653278..6420680 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,4 +10,6 @@ RUN pip install pipenv
 RUN pipenv install --system --deploy --ignore-pipfile
 COPY ./mysite .

+RUN python manage.py collectstatic --no-input
+
 ENTRYPOINT ["python", "manage.py"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Statelessness achieved!&lt;/p&gt;

&lt;h2&gt;
  
  
  Port binding
&lt;/h2&gt;

&lt;p&gt;Now that we have the application source code, dependencies, and supporting static assets inside a container image, we need a way to expose the entirety of it in a self-contained way. Since this is a web application, our goal is to use the HTTP protocol instead of lower level APIs like CGI, FastCGI, Servlets, etc.&lt;/p&gt;

&lt;p&gt;We’ve seen our application bound to a port over HTTP several times already via the &lt;code&gt;docker run&lt;/code&gt; invocations above, but they were all using a development-grade HTTP application server (e.g., &lt;code&gt;runserver&lt;/code&gt;). How do we achieve something similar in a production-grade way?&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://gunicorn.org/"&gt;Gunicorn&lt;/a&gt; and &lt;a href="https://www.uvicorn.org/"&gt;Uvicorn&lt;/a&gt;. Gunicorn is a production-grade Python application server for UNIX based systems, and Uvicorn provides a Gunicorn worker implementation with &lt;a href="https://asgi.readthedocs.io/en/latest/index.html"&gt;Asynchronous Server Gateway Interface (ASGI)&lt;/a&gt; compatibility.&lt;/p&gt;

&lt;p&gt;After installing Gunicorn and Uvicorn using &lt;code&gt;pipenv install&lt;/code&gt;, we need to tweak the Docker Compose configuration from Build, release, run to use Gunicorn as the &lt;code&gt;entrypoint&lt;/code&gt;. We also add a few command-line options to ensure that the ASGI API is used (between Gunicorn and Django) along with the Uvicorn worker implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/docker-compose.yml b/docker-compose.yml
index f5f693d..bac885d 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -20,8 +20,12 @@ services:
     build:
       context: .
       dockerfile: Dockerfile
+ entrypoint: gunicorn
     command:
- - runserver
- - "0.0.0.0:8000"
+ - "mysite.asgi:application"
+ - "-b 0.0.0.0:8000"
+ - "-k uvicorn.workers.UvicornWorker"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all of these changes, Docker Compose should be able to bring the service up bound to port &lt;code&gt;8000&lt;/code&gt; using Gunicorn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose up web
Starting django-blog_web_1 ... done
Attaching to django-blog_web_1
web_1 | [2021-03-06 19:57:43 +0000] [1] [INFO] Starting gunicorn 20.0.4
web_1 | [2021-03-06 19:57:43 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
web_1 | [2021-03-06 19:57:43 +0000] [1] [INFO] Using worker: uvicorn.workers.UvicornWorker
web_1 | [2021-03-06 19:57:43 +0000] [8] [INFO] Booting worker with pid: 8
web_1 | [2021-03-06 19:57:43 +0000] [8] [INFO] Started server process [8]
web_1 | [2021-03-06 19:57:43 +0000] [8] [INFO] Waiting for application startup.
web_1 | [2021-03-06 19:57:43 +0000] [8] [INFO] ASGI 'lifespan' protocol appears unsupported.
web_1 | [2021-03-06 19:57:43 +0000] [8] [INFO] Application startup complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can confirm by creating a second terminal session, hitting the &lt;code&gt;/admin/&lt;/code&gt; endpoint, and inspecting the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ http localhost:8000/admin/
HTTP/1.1 302 Found
cache-control: max-age=0, no-cache, no-store, must-revalidate, private
content-length: 0
content-type: text/html charset=utf-8
date: Sat, 06 Mar 2021 19:59:36 GMT
expires: Sat, 06 Mar 2021 19:59:36 GMT
location: /admin/login/?next=/admin/
referrer-policy: same-origin
server: uvicorn
vary: Cookie
x-content-type-options: nosniff
x-frame-options: DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s alive!&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency
&lt;/h2&gt;

&lt;p&gt;As load against an application increases, the ability to address it by quickly and reliably adding more stateless &lt;em&gt;processes&lt;/em&gt; is desirable. Gunicorn has built-in support for a process level &lt;a href="https://docs.gunicorn.org/en/stable/design.html"&gt;worker model&lt;/a&gt;, but using it to scale an application in cloud based environments can cause contention with higher level distributed process managers. This is because both want to manage the processes, but only the distributed process manager has a wholistic view of resources across machines. Instead, we can set the number of Gunicorn worker processes low and defer process management to a higher level supervisor.&lt;/p&gt;

&lt;p&gt;Specifying different &lt;em&gt;process types&lt;/em&gt; can’t really be done with Gunicorn either. Usually, that’s more tightly coupled with the container orchestration engine you use. Later on in Dev/prod parity we’ll see a Docker Compose configuration with both a &lt;code&gt;database&lt;/code&gt; and &lt;code&gt;web&lt;/code&gt; process type. Within a more production-oriented container orchestration system like Kubernetes, you’d achieve something similar by creating separate sets of &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/"&gt;pods&lt;/a&gt;—one for each &lt;em&gt;process type&lt;/em&gt; to enable independent scaling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disposability
&lt;/h2&gt;

&lt;p&gt;In cloud environments, application &lt;em&gt;disposability&lt;/em&gt; is important because it increases agility during releases, scaling events, and failures. An application exhibits &lt;em&gt;disposability&lt;/em&gt; when it properly handles certain types of asynchronous notifications called &lt;a href="https://en.wikipedia.org/wiki/Signal_(IPC)"&gt;signals&lt;/a&gt;. Signals help local supervisory services (e.g., &lt;a href="https://www.freedesktop.org/wiki/Software/systemd/"&gt;systemd&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/"&gt;Kubelet&lt;/a&gt;) manage an application’s lifecycle externally.&lt;/p&gt;

&lt;p&gt;Gunicorn has built-in support for &lt;a href="https://docs.gunicorn.org/en/stable/signals.html"&gt;signal handling&lt;/a&gt;. If you use it as your application server, it will automatically handle signals like &lt;code&gt;SIGTERM&lt;/code&gt; to facilitate a graceful shutdown of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dev/prod parity
&lt;/h2&gt;

&lt;p&gt;Configuration allows a single &lt;em&gt;build&lt;/em&gt; of a &lt;em&gt;codebase&lt;/em&gt; to run locally, in staging, and in production. Leveraging that to maintain parity across environments keeps incompatibilities from cropping up as software is being developed. This results in a higher degree confidence that the application will function the same way in production, as it did locally.&lt;/p&gt;

&lt;p&gt;Still, maintaining development and production parity is an ongoing challenge. Much like speed and security, you have to be constantly thinking about it, or else you lose it.&lt;/p&gt;

&lt;p&gt;Nowadays, operating system support for namespacing resources through containerization, along with higher level tooling like Docker and Docker Compose, go a long way toward making this pursuit easier to achieve. As an example, see the following Docker Compose configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3"
services:
  database:
    image: postgres:12.6
    environment:
      - POSTGRES_USER=mysite
      - POSTGRES_PASSWORD=mysite
      - POSTGRES_DB=mysite

  web:
    image: mysite
    environment:
      - POSTGRES_HOST=database
      - POSTGRES_PORT=5432
      - POSTGRES_USER=mysite
      - POSTGRES_PASSWORD=mysite
      - POSTGRES_DB=mysite
      - DJANGO_ENV=Development
      - DJANGO_SECRET_KEY=secret
      - DJANGO_LOG_LEVEL=DEBUG
    build:
      context: .
      dockerfile: Dockerfile
    entrypoint: gunicorn
    command:
      - "mysite.asgi:application"
      - "-b 0.0.0.0:8000"
      - "-k uvicorn.workers.UvicornWorker"
    ports:
      - "8000:8000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within this relatively small file, we have defined all services needed to run our application locally. Each service (&lt;code&gt;database&lt;/code&gt; and &lt;code&gt;web&lt;/code&gt;) run as separate processes within their own containers, but are networked together. From the perspective of our Django application, this setup differs minimally from a true production container orchestration setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logs
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Logs&lt;/em&gt; emitted by an application provide visibility into its behavior. However, in cloud environments you cannot reliably predict where your application is going to run. This makes it difficult to get visibility into the application’s behavior—unless, you treat application logging as a &lt;em&gt;stream&lt;/em&gt;. Treating application logs as a stream makes it easier for other services to aggregate and archive log output for centralized viewing.&lt;/p&gt;

&lt;p&gt;Django uses Python’s built-in &lt;a href="https://docs.python.org/3/library/logging.html#module-logging"&gt;logging&lt;/a&gt; module to perform system logging, which allows it to be set up in some pretty sophisticated ways. However, all we want is for Django to log everything as a &lt;em&gt;stream&lt;/em&gt; to standard out. We can make that happen by specifying a custom logging configuration dictionary in &lt;code&gt;settings.py&lt;/code&gt; that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "level": "WARNING",
            "propagate": False,
        },
    },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configures the parent root logger to send messages with the &lt;code&gt;WARNING&lt;/code&gt; level and higher to the console handler (e.g., standard out). It also has support to tune the default Django log levels via the &lt;code&gt;DJANGO_LOG_LEVEL&lt;/code&gt; environment variable. A dynamic override like this can be extremely helpful when troubleshooting because it allows logging settings to be modified without requiring a new &lt;em&gt;release&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Admin processes
&lt;/h2&gt;

&lt;p&gt;Administrative tasks are essential to every application. It is important for the code associated them to ship with the application to avoid synchronization issues as they are invoked in the same execution environment as the application.&lt;/p&gt;

&lt;p&gt;Most of Django’s supporting administrative tasks, like applying database migrations, sending test emails, and adding users, can already be executed as one-off processes. In addition, Django provides a &lt;a href="https://docs.djangoproject.com/en/3.1/howto/custom-management-commands/"&gt;robust framework&lt;/a&gt; for adding more that are specific to your application (e.g., toggling feature flags, orchestrating data imports, etc.).&lt;/p&gt;

&lt;p&gt;As an example, we can apply outstanding database migrations (there should be some for a newly initialized Django project) with the built-in &lt;code&gt;migrate&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose run --rm --entrypoint "python manage.py" web migrate
Creating django-blog_web_run ... done
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we dynamically override the previously referenced Docker Compose configuration with &lt;code&gt;--entrypoint&lt;/code&gt; set to &lt;code&gt;python manage.py&lt;/code&gt; instead of &lt;code&gt;gunicorn&lt;/code&gt;. We also specify that we want the &lt;code&gt;migrate&lt;/code&gt; subcommand to be run. This execution leads to a series of cross-container communications that ensure our database schema aligns with the current state of Django’s data model.&lt;/p&gt;




&lt;p&gt;That’s it! Whether you were aware of the 12 Factor methodology before or not, I hope that seeing it applied to a Django application enables you to more easily integrate it with whatever web framework you use. May it lead to more configurable, scalable, and reliable applications. &lt;em&gt;Amen&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;small&gt;&lt;em&gt;Thanks to &lt;a href="https://twitter.com/davekonopka" rel="noopener"&gt;Dave Konopka&lt;/a&gt; for providing thoughtful feedback on my drafts of this post.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>How I Make Slack Work for Me</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Sat, 13 Feb 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/hector/how-i-make-slack-work-for-me-17om</link>
      <guid>https://dev.to/hector/how-i-make-slack-work-for-me-17om</guid>
      <description>&lt;p&gt;As a daily Slack user for the last seven years, I’ve spent a lot of time exploring ways to get the most out of it as a collaboration tool. While I have mixed feelings about its impact on productivity, I figure Slack isn’t going away any time soon, so I may as well learn how to make it work for me.&lt;/p&gt;

&lt;p&gt;The sections below capture some Slack features and general tactics I’ve employed to make the most out of Slack as a tech lead and engineering leader in a software development focused organization. I hope you find some of them useful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All unread entrypoint&lt;/li&gt;
&lt;li&gt;Follow thread&lt;/li&gt;
&lt;li&gt;Reminders on messages to track commitments&lt;/li&gt;
&lt;li&gt;Strategic keyword notifications&lt;/li&gt;
&lt;li&gt;Saved items as source for feedback&lt;/li&gt;
&lt;li&gt;Quiet hours&lt;/li&gt;
&lt;li&gt;Team &lt;code&gt;CHANGELOG&lt;/code&gt; channel&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. All unread entrypoint
&lt;/h2&gt;

&lt;p&gt;By default, the &lt;a href="https://slack.com/help/articles/226410907-View-all-your-unread-messages"&gt;all unread&lt;/a&gt; feature of Slack is disabled. When enabled, it adds a new top level entry to the left-hand Slack navigation that allows you to browse all of your unread messages, grouped by channel, in a single view. While in this mode, you can scan messages, but also access all of the individual message shortcuts.&lt;/p&gt;

&lt;p&gt;I use this feature as the entrypoint for digesting all Slack messages because it enables the move &lt;a href="https://twitter.com/deniseyu21"&gt;Denise Yu&lt;/a&gt; so eloquently summarized as, &lt;em&gt;The Art of the Rollup&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;enter what i've been mentally noting as "the art of the rollup". read the backscroll, take a deep breath, wait 5 mins, and write to entire channel:  &lt;/p&gt;

&lt;p&gt;"To summarize: the problem is X. Possible paths forward are A, B, C. Sounds like we're leaning towards A. have I missed anything?"&lt;/p&gt;

&lt;p&gt;— Denise Yu (@deniseyu21) &lt;a href="https://twitter.com/deniseyu21/status/1357832806932561920?ref_src=twsrc%5Etfw"&gt;February 5, 2021&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The ability to digest top level channel discussion as it develops, while still leaving it all marked an unread, allows me to bookend the context necessary to assemble an effective rollup.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Follow thread
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://slack.com/help/articles/115000769927-Use-threads-to-organize-discussions-#manage-the-threads-you-follow"&gt;follow thread&lt;/a&gt; feature is easily the Slack feature I use most on this list. It allows you to subscribe to messages published in a thread &lt;em&gt;without&lt;/em&gt; contributing any messages to the thread. I use it pretty liberally on any interesting message that pops up in a channel, then mark the channel as read via the All unread view referenced above.&lt;/p&gt;

&lt;p&gt;It is worth cautioning that heavy use of this feature can quickly escalate into behavior that becomes indistinguishable from micromanagement—especially, if you use it to inject yourself into lots of conversations where people are trying to build their own problem solving skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Reminders on messages to track commitments
&lt;/h2&gt;

&lt;p&gt;The Kahn Academy &lt;a href="https://docs.google.com/document/d/1qr0d05X5-AsyDYqKRCfgGGcWSshTMd_vfTggfhDpbls/edit"&gt;career development guide&lt;/a&gt; emphasizes a top level attribute called &lt;em&gt;Maturity&lt;/em&gt;. An important indicator of maturity they cite is following through on your commitments (i.e., doing what you say you are going to do).&lt;/p&gt;

&lt;p&gt;As a typical work day progresses, tons of micro commitments come up, and many of them occur in chat. &lt;a href="https://slack.com/help/articles/208423427-Set-a-reminder#set-a-reminder-for-a-message"&gt;Setting a reminder on a message&lt;/a&gt; provides an effective in-context way to track, snooze, and reschedule commitments so that they don’t get lost in the shuffle.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Strategic keyword notifications
&lt;/h2&gt;

&lt;p&gt;Most folks are familiar with (and possibly loathe) Slack notifications. Most of the time, notifications happen when you get a direct message, when someone mentions you, or when someone mentions a group alias you’re a member of. But, Slack also provides a way to &lt;a href="https://slack.com/help/articles/201355156-Configure-your-Slack-notifications#keyword-notifications"&gt;set up an open-ended list of keywords&lt;/a&gt; that trigger notifications.&lt;/p&gt;

&lt;p&gt;In the past I’ve taken advantage of this feature to target certain keywords that have a tendency to lead to &lt;em&gt;architecturally significant&lt;/em&gt; events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bad idea&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cache&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lock&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;redis&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;should work&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trivial&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Saved items as source for feedback
&lt;/h2&gt;

&lt;p&gt;It has been said that feedback is a gift. But, as with any great gift, feedback can be difficult to identify and deliver.&lt;/p&gt;

&lt;p&gt;One way to make the feedback delivery process more effective is to connect it to specific events. For example, telling someone that they did a really good job at disambiguating a complex topic &lt;em&gt;in a meeting last week&lt;/em&gt;. Or, that their testing instructions &lt;em&gt;on a pull request from yesterday&lt;/em&gt; were detailed and easy to follow.&lt;/p&gt;

&lt;p&gt;Neither of these examples are tied to chat, but many others are. To persist these events across different Slack channels, I repurpose the Slack &lt;a href="https://slack.com/help/articles/360042650274-Save-messages-and-files-"&gt;save messages and files&lt;/a&gt; feature solely to track examples of both exemplary and poor communication. Any time I see a good candidate, I don’t have to think—I just click on the bookmark (used to be ⭐) icon. Later, I draw upon that list to support feedback in venues like one-on-ones, performance reviews, calls for kudos, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Quiet hours
&lt;/h2&gt;

&lt;p&gt;A couple of years back, I was exposed to the concept of Slack quiet hours by &lt;a href="https://twitter.com/kepioo"&gt;Nassim Kammah&lt;/a&gt; in a talk about &lt;a href="https://www.youtube.com/watch?v=RMsZbchAwoY"&gt;remote-first team practices&lt;/a&gt;. Quiet hours are periods of blocked-off time, mutually agreed upon in advance by the team, when the team does not actively engage in Slack conversations. Colleagues are encouraged to save questions, requests, and conversations for outside of these periods.&lt;/p&gt;

&lt;p&gt;Reserved blocks of time off of Slack aim to help enable deep work and mitigate the amount of context switching and &lt;a href="https://en.wikipedia.org/wiki/Fear_of_missing_out"&gt;FOMO&lt;/a&gt; that can occur as we bounce between completing tasks and keeping up with the never-ending Slack firehose.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Team &lt;code&gt;CHANGELOG&lt;/code&gt; channel
&lt;/h2&gt;

&lt;p&gt;Also sourced from Nassim’s talk above is the use of &lt;a href="https://reacji-channeler.builtbyslack.com"&gt;Reacji Channeler&lt;/a&gt;. Reacji Channeler is a Slack application that routes messages annotated with specific reactions to a designated channel. It can be configured in many ways to target a wide variety of use cases, but the use case described in the talk is particularly interesting: using it to produce a team &lt;code&gt;CHANGELOG&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As significant events occur throughout a team’s day-to-day, someone summarizes (or rolls up) the event into one message that includes the surrounding context. When the appropriate reaction is applied to the message, it gets routed to a team &lt;code&gt;CHANGELOG&lt;/code&gt; channel (e.g., &lt;code&gt;#sre-team-changelog&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The goal is to produce a log such that if someone goes on vacation for a week, they can come back, read just that channel’s backscroll (not all of the actual team channel backscroll), and be caught up.&lt;/p&gt;




&lt;p&gt;&lt;small&gt;&lt;em&gt;Special thanks to &lt;a href="https://twitter.com/rajadain" rel="noopener"&gt;Terence Tuhinanshu&lt;/a&gt; for encouraging me to write this.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>slack</category>
      <category>productivity</category>
      <category>remote</category>
    </item>
    <item>
      <title>Creating Go Application Releases with GoReleaser</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Mon, 18 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/hector/creating-go-application-releases-with-goreleaser-1hac</link>
      <guid>https://dev.to/hector/creating-go-application-releases-with-goreleaser-1hac</guid>
      <description>&lt;p&gt;A few weeks ago, I set out to upgrade the version of Go (1.6 to 1.15) used to build an old command-line utility I developed, named &lt;a href="https://github.com/hectcastro/heimdall"&gt;Heimdall&lt;/a&gt;. Heimdall provides a way to wrap an executable program inside of an exclusive lock provided by a central PostgreSQL instance via &lt;code&gt;pg_try_advisory_lock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, Heimdall is nice little utility and all (if you’re intrigued, check out the &lt;code&gt;README&lt;/code&gt;), but the most interesting part of the upgrade process came after I got everything working and started to think about how to create a new release. That’s when I came across &lt;a href="https://goreleaser.com"&gt;GoReleaser&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  GoReleaser
&lt;/h2&gt;

&lt;p&gt;GoReleaser is a release automation tool specifically for Go projects. With a few bits of &lt;a href="https://github.com/hectcastro/heimdall/blob/1ffc81c5457ae8692b18117a8329e3e3997e80e1/.goreleaser.yml"&gt;YAML configuration&lt;/a&gt;, GoReleaser provided me with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hooks into the Go module system for managing library dependencies&lt;/li&gt;
&lt;li&gt;The ability to easily produce a set of &lt;a href="https://github.com/hectcastro/heimdall/releases/tag/1.0.0"&gt;build artifacts&lt;/a&gt; for multiple operating systems and computer architectures&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hectcastro/heimdall/releases/download/1.0.0/checksums.txt"&gt;Checksums&lt;/a&gt; for each of the build artifacts&lt;/li&gt;
&lt;li&gt;Easy &lt;a href="https://github.com/hectcastro/heimdall/blob/1ffc81c5457ae8692b18117a8329e3e3997e80e1/.github/workflows/goreleaser.yml"&gt;integration with GitHub Actions&lt;/a&gt; to automate publishing releases on tagged commits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are responsible for Go applications that are in need of a uniform release process, I find it really hard to beat GoReleaser.&lt;/p&gt;

</description>
      <category>go</category>
      <category>devops</category>
      <category>github</category>
      <category>actions</category>
    </item>
    <item>
      <title>Validating Data in Python with Cerberus</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Tue, 29 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/hector/validating-data-in-python-with-cerberus-1j73</link>
      <guid>https://dev.to/hector/validating-data-in-python-with-cerberus-1j73</guid>
      <description>&lt;p&gt;This year was my first participating in &lt;a href="https://adventofcode.com"&gt;Advent of Code&lt;/a&gt;—and I’m glad I did, because solving one of the &lt;a href="https://adventofcode.com/2020/day/4"&gt;challenges&lt;/a&gt; exposed me to an excellent data validation library for Python named &lt;a href="https://docs.python-cerberus.org/en/stable/"&gt;Cerberus&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s in a valid passport
&lt;/h2&gt;

&lt;p&gt;Below are some excerpts from the challenge, along with specific field level validation rules:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You arrive at the airport only to realize that you grabbed your North Pole Credentials instead of your passport. While these documents are extremely similar, North Pole Credentials aren’t issued by a country and therefore aren’t actually valid documentation for travel in most of the world.&lt;/p&gt;

&lt;p&gt;It seems like you’re not the only one having problems, though; a very long line has formed for the automatic passport scanners, and the delay could upset your travel itinerary.&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;The line is moving more quickly now, but you overhear airport security talking about how passports with invalid data are getting through. Better add some data validation, quick!&lt;/p&gt;

&lt;p&gt;You can continue to ignore the &lt;code&gt;cid&lt;/code&gt; field, but each other field has strict rules about what values are valid for automatic validation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;byr&lt;/code&gt; (Birth Year) - four digits; at least &lt;code&gt;1920&lt;/code&gt; and at most &lt;code&gt;2002&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iyr&lt;/code&gt; (Issue Year) - four digits; at least &lt;code&gt;2010&lt;/code&gt; and at most &lt;code&gt;2020&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eyr&lt;/code&gt; (Expiration Year) - four digits; at least &lt;code&gt;2020&lt;/code&gt; and at most &lt;code&gt;2030&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hgt&lt;/code&gt; (Height) - a number followed by either &lt;code&gt;cm&lt;/code&gt; or &lt;code&gt;in&lt;/code&gt;: 

&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;cm&lt;/code&gt;, the number must be at least &lt;code&gt;150&lt;/code&gt; and at most &lt;code&gt;193&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;in&lt;/code&gt;, the number must be at least &lt;code&gt;59&lt;/code&gt; and at most &lt;code&gt;76&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hcl&lt;/code&gt; (Hair Color) - a # followed by exactly six characters &lt;code&gt;0-9&lt;/code&gt; or &lt;code&gt;a-f&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ecl&lt;/code&gt; (Eye Color) - exactly one of: &lt;code&gt;amb&lt;/code&gt; &lt;code&gt;blu&lt;/code&gt; &lt;code&gt;brn&lt;/code&gt; &lt;code&gt;gry&lt;/code&gt; &lt;code&gt;grn&lt;/code&gt; &lt;code&gt;hzl&lt;/code&gt; &lt;code&gt;oth&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pid&lt;/code&gt; (Passport ID) - a nine-digit number, including leading zeroes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cid&lt;/code&gt; (Country ID) - ignored, missing or not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your job is to count the passports where all required fields are both &lt;strong&gt;present&lt;/strong&gt; and &lt;strong&gt;valid&lt;/strong&gt; according to the above rules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For completeness, here are some invalid passports (delimited by &lt;code&gt;\n\n&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eyr:1972 cid:100
hcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926

iyr:2019
hcl:#602927 eyr:1967 hgt:170cm
ecl:grn pid:012533040 byr:1946

hcl:dab227 iyr:2012
ecl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, some valid passports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980
hcl:#623a2f

eyr:2029 ecl:blu cid:129 byr:1989
iyr:2014 pid:896056539 hcl:#a97842 hgt:165cm

hcl:#888785
hgt:164cm byr:2001 iyr:2015 cid:88
pid:545766238 ecl:hzl
eyr:2022
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the validation rules look straightforward in isolation, but less so when you think about composing them all together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating passports with Cerberus
&lt;/h2&gt;

&lt;p&gt;Step one involved getting familiar with Cerberus &lt;a href="https://docs.python-cerberus.org/en/stable/validation-rules.html"&gt;validation rules&lt;/a&gt;. The library supports rules like the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;contains&lt;/code&gt; - This rule validates that the a container object contains all of the defined items.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"states"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"peace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"love"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"inity"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"states"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"contains"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"peace"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;regex&lt;/code&gt; - The validation will fail if the field’s value does not match the provided regular expression.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="s"&gt;"regex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"john@example.com"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;required&lt;/code&gt; - If &lt;code&gt;True&lt;/code&gt; the field is mandatory. Validation will fail when it is missing.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step two involved converting the passports into Cerberus documents. This was mostly an exercise in parsing uniquely assembled text into Python dictionaries.&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;# Split the batch file records by double newline.
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;batch_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Split the fields within a record by a space or newline.
&lt;/span&gt;    &lt;span class="n"&gt;record_field_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s"&gt;"\s"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That leaves &lt;code&gt;record_field_list&lt;/code&gt; looking like:&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="n"&gt;record_field_list&lt;/span&gt;
&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s"&gt;'ecl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'gry'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'pid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'860033327'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'eyr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'2020'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hcl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'#fffffd'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'byr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'1937'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'iyr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'2017'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'cid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'147'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hgt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'183cm'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, &lt;code&gt;dict&lt;/code&gt; converts the list of tuples into a proper Cerberus document:&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="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record_field_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'byr'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'1937'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'cid'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'147'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'ecl'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'gry'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'eyr'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'2020'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'hcl'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'#fffffd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'hgt'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'183cm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'iyr'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'2017'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s"&gt;'pid'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'860033327'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Equipped with a better understanding of what’s possible with Cerberus, and a list of Python dictionaries representing passports, below is the schema I put together to enforce the passport validation rules of the challenge. Only one of the rules (&lt;code&gt;hgt&lt;/code&gt;) required a custom function (&lt;code&gt;compare_hgt_with_units&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="n"&gt;SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"byr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"1920"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2002"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"iyr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2010"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2020"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"eyr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2020"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2030"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"hgt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"anyof"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"allof"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;"regex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"[0-9]+cm"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"check_with"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;compare_hgt_with_units&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"allof"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;"regex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"[0-9]+in"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"check_with"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;compare_hgt_with_units&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"hcl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"regex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"#[0-9a-f]{6}"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"ecl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"allowed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"amb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"blu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"brn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"grn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"oth"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="s"&gt;"pid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"regex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"[0-9]{9}"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"cid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Provide a custom field validation function for a height with units.
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compare_hgt_with_units&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&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;value&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;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&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;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&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;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cm"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cm"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;193&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"out of range"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;59&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"out of range"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"missing units"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a schema in place, all that’s left to do is instantiate a &lt;code&gt;Validator&lt;/code&gt; and validate each document:&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="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SCHEMA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;require_all&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

</description>
      <category>python</category>
      <category>adventofcode</category>
      <category>cerberus</category>
      <category>validation</category>
    </item>
    <item>
      <title>Centralized Scala Steward with GitHub Actions</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Wed, 18 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/hector/centralized-scala-steward-with-github-actions-5gfa</link>
      <guid>https://dev.to/hector/centralized-scala-steward-with-github-actions-5gfa</guid>
      <description>&lt;p&gt;Keeping project dependencies up-to-date is a challenging problem. Services like GitHub’s automated dependency updating system, &lt;a href="https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/"&gt;Dependabot&lt;/a&gt;, go a long way to help make things easier, but that is only helpful if your package manager’s ecosystem is &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem"&gt;supported&lt;/a&gt;. In the case of Scala based projects, it is not.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://github.com/scala-steward-org/scala-steward"&gt;Scala Steward&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Scala Steward provides a similar, low-effort way to keep project dependencies up-to-date. You simply open a pull request against the Scala Steward repository and add a reference to &lt;em&gt;your&lt;/em&gt; project’s GitHub repository inside of a specially designated Markdown file. After that, Scala Steward (which manifests itself as a robot user on GitHub) keeps your project dependencies up-to-date via pull requests.&lt;/p&gt;

&lt;p&gt;Unfortunately, this easy-mode option requires that your repository be publicly accessible. There are &lt;a href="https://engineering.avast.io/running-scala-steward-on-premise/"&gt;options&lt;/a&gt; for running Scala Steward as a service for yourself, but that path is less trodden and requires a bit more effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scala Steward and GitHub Actions
&lt;/h2&gt;

&lt;p&gt;So what other options do you have if your Scala project is inside a private repository? Well, if your project is on GitHub, then you likely have access to their workflow automation service, &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions"&gt;GitHub Actions&lt;/a&gt;. Scala Steward’s maintainers created a &lt;a href="https://github.com/scala-steward-org/scala-steward-action"&gt;GitHub Action&lt;/a&gt; that lowers the bar to adding Scala Steward support to projects via the GitHub Actions execution model.&lt;/p&gt;

&lt;p&gt;By default, the Action supports dependency detection through a workflow defined inside of your project’s repository. This approach makes it easy to simulate the public instance of Scala Steward on a per repository basis. &lt;em&gt;But&lt;/em&gt;, there is also a &lt;a href="https://github.com/scala-steward-org/scala-steward-action#updating-multiple-repositories"&gt;centralized mode&lt;/a&gt; that allows you to mimic the way the centrally managed instance of Scala Steward works.&lt;/p&gt;

&lt;p&gt;This centralized mode gives us an opportunity to have the best of both worlds: a low-effort way to keep multiple project dependencies up-to-date (similar to the public instance of Scala Steward), &lt;em&gt;and&lt;/em&gt; the ability to do so across both public and private repositories!&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting things together
&lt;/h2&gt;

&lt;p&gt;First, create a GitHub repository for your instance of Scala Steward and put a file in it at &lt;code&gt;.github/workflows/scala-steward.yml&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scala Steward&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Schedule to run every Sunday @ 12PM UTC. Replace this with&lt;/span&gt;
    &lt;span class="c1"&gt;# whatever seems appropriate to you.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0"&lt;/span&gt;
  &lt;span class="c1"&gt;# Provide support for manually triggering the workflow via GitHub.&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scala-steward&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scala-steward&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# This is necessary to ensure that the most up-to-date version of&lt;/span&gt;
      &lt;span class="c1"&gt;# REPOSITORIES.md is used.&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Execute Scala Steward&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scala-steward-org/scala-steward-action@vX.Y.Z&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# A GitHub personal access token tied to a user that will create&lt;/span&gt;
          &lt;span class="c1"&gt;# pull requests against your projects to update dependencies. More&lt;/span&gt;
          &lt;span class="c1"&gt;# on this under the YAML snippet.&lt;/span&gt;
          &lt;span class="na"&gt;github-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SCALA_STEWARD_GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="c1"&gt;# A Markdown file with a literal Markdown list of repositories&lt;/span&gt;
          &lt;span class="c1"&gt;# Scala Steward should monitor.&lt;/span&gt;
          &lt;span class="na"&gt;repos-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REPOSITORIES.md&lt;/span&gt;
          &lt;span class="na"&gt;author-email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scala-steward@users.noreply.github.com&lt;/span&gt;
          &lt;span class="na"&gt;author-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scala Steward&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully, the inline comments help minimize any ambiguity in the GitHub Actions workflow configuration file. For completeness, below is an example of the Markdown file as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- organization/repository1
- organization/repository2
- organization/repository3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last step is to ensure that any private repositories add the user associated with the GitHub personal access token as a collaborator with the &lt;strong&gt;Write&lt;/strong&gt; role permissions. Also, to slightly improve usability and maintainability, consider the following suggestions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot"&gt;Dependabot support&lt;/a&gt; to your Scala Steward repository to keep the Scala Steward GitHub Action up-to-date.&lt;/li&gt;
&lt;li&gt;Avoid tying Scala Steward to an individual user GitHub account. Consider creating a &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/types-of-github-accounts#personal-user-accounts"&gt;bot account&lt;/a&gt; first, then create a personal access token with it to use with Scala Steward.&lt;/li&gt;
&lt;li&gt;Create a custom Scala Steward team (e.g., &lt;strong&gt;@organization/scala-steward&lt;/strong&gt; ) and add the bot account above to it. Now, instead of remembering to add the bot account to your Scala project repository as a collaborator, you can add the more intuitive Scala Steward team.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>scala</category>
      <category>github</category>
      <category>actions</category>
      <category>dependencies</category>
    </item>
    <item>
      <title>Haskell Code Katas: Counting Duplicates</title>
      <dc:creator>Hector Castro</dc:creator>
      <pubDate>Sun, 17 Dec 2017 00:00:00 +0000</pubDate>
      <link>https://dev.to/hector/haskell-code-katas-counting-duplicates-7mk</link>
      <guid>https://dev.to/hector/haskell-code-katas-counting-duplicates-7mk</guid>
      <description>&lt;p&gt;For the past few weeks, I’ve been starting off my days with Haskell flavored code katas from &lt;a href="https://www.codewars.com/"&gt;Codewars&lt;/a&gt;. Today I started with the kata below and figured it would be a good exercise to walk through my solution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write a function that will return the count of &lt;em&gt;distinct&lt;/em&gt; case-insensitive alphabetic characters and numeric digits that occur more than once in the input string. The input string can be assumed to contain only alphabets (both uppercase and lowercase) and numeric digits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To help clarify the specifications for this kata, the &lt;a href="https://hspec.github.io/"&gt;Hspec&lt;/a&gt; test suite is below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Codwars.Kata.Duplicates.Test&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Codwars.Kata.Duplicates&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;duplicateCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;nub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Test.Hspec&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Test.QuickCheck&lt;/span&gt;

&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hspec&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s"&gt;"duplicateCount"&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s"&gt;"should work for some small tests"&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="s"&gt;"abcde"&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="s"&gt;"aabbcde"&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="s"&gt;"aaBbcde"&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="s"&gt;"Indivisibility"&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="s"&gt;"Indivisibilities"&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="sc"&gt;'z'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="sc"&gt;'z'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'A'&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="sc"&gt;'Z'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;
    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s"&gt;"should work for some random lists"&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;forAll&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listOf&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;elements&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="sc"&gt;'z'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nub&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
        &lt;span class="kr"&gt;in&lt;/span&gt; &lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replicate&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=?=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;
  &lt;span class="kr"&gt;where&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;=?=&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sorting &amp;amp; Grouping
&lt;/h2&gt;

&lt;p&gt;To start things off, we are given the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Codwars.Kata.Duplicates&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My first step is to figure out how to deal with case-insensitivity. Within &lt;code&gt;Data.Char&lt;/code&gt; is &lt;code&gt;toLower&lt;/code&gt;, which can be used to map over each character in the input &lt;code&gt;String&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kt"&gt;Prelude&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"aaBbcde"&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="s"&gt;"aaBbcde"&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Char&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;toLower&lt;/span&gt;
&lt;span class="n"&gt;toLower&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;toLower&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="s"&gt;"aabbcde"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I want to group like characters together. To do that, I need to &lt;code&gt;sort&lt;/code&gt; and then &lt;code&gt;group&lt;/code&gt; the characters together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.List&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt;
&lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;toLower&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="s"&gt;"aabbcde"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sort&lt;/code&gt; doesn’t do very much in this case because the input string was already sorted. Either way, now we can work on grouping like characters with &lt;code&gt;group&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;
&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Eq&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;toLower&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"aa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"bb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"e"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Home Stretch
&lt;/h2&gt;

&lt;p&gt;Now, how do we go from a list of &lt;code&gt;[Char]&lt;/code&gt; to an &lt;code&gt;Int&lt;/code&gt; length that can be used for filtering characters that only occur once? &lt;code&gt;filter&lt;/code&gt;, with a &lt;code&gt;&amp;gt;1&lt;/code&gt; condition applied to the &lt;code&gt;length&lt;/code&gt;, should get us there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;toLower&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;&amp;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="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"aa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"bb"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the &lt;code&gt;.&lt;/code&gt; allows us to compose &lt;code&gt;length&lt;/code&gt; and &lt;code&gt;&amp;gt;1&lt;/code&gt; together so that both can be applied to the &lt;code&gt;[Char]&lt;/code&gt; provided to &lt;code&gt;filter&lt;/code&gt;. The result rids the list of any characters that only occur once in the original input.&lt;/p&gt;

&lt;p&gt;Lastly, we need the count of distinct characters from the input &lt;code&gt;String&lt;/code&gt; that occur more than one, which is as simple as getting the &lt;code&gt;length&lt;/code&gt; of the filtered list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;&amp;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="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;span class="kt"&gt;Prelude&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting it all together, and breaking out some of the pipelined functions into a variable in the &lt;code&gt;where&lt;/code&gt; clause, we get the &lt;code&gt;duplicateCount&lt;/code&gt; function below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Codwars.Kata.Duplicates&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Char&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;toLower&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="n"&gt;duplicateCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;&amp;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="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;grouped&lt;/span&gt;
  &lt;span class="kr"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;grouped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;toLower&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>haskell</category>
      <category>codewars</category>
      <category>kata</category>
    </item>
  </channel>
</rss>
