<?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: Nicola Apicella</title>
    <description>The latest articles on DEV Community by Nicola Apicella (@napicella).</description>
    <link>https://dev.to/napicella</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%2F36214%2F886fdbdc-7719-4cf4-b5f7-8d1e80ca2f9d.jpg</url>
      <title>DEV Community: Nicola Apicella</title>
      <link>https://dev.to/napicella</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/napicella"/>
    <language>en</language>
    <item>
      <title>Golang error updates</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Fri, 29 Sep 2023 15:12:09 +0000</pubDate>
      <link>https://dev.to/napicella/golang-error-updates-4odp</link>
      <guid>https://dev.to/napicella/golang-error-updates-4odp</guid>
      <description>&lt;p&gt;Since go 1.20, golang has improved the ergonomics of error handling. Noteworthy improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;errors.Is&lt;/code&gt; and &lt;code&gt;errors.As&lt;/code&gt; have been updated to work on tree of errors&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fmt.Errorf&lt;/code&gt; accepts multiple %w format verbs, allowing to join multiple errors&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Unwrap []error&lt;/code&gt; function allows traversing through tree of errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow some tests demonstrating the practical application of these enhancements (also available in the &lt;a href="https://go.dev/play/p/LCBbcvRUXAD"&gt;golang playground&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"testing"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestErrorfForWrapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;err1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// the parentheses in the string are arbitrary&lt;/span&gt;
    &lt;span class="n"&gt;wrap&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%w (%w)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err2&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;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&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="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestStatusCodeInErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="c"&gt;// client errors&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="n"&gt;clientError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="c"&gt;// client error causes&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="n"&gt;unauthorized&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unauthorized"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;resourceNotFound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"resource not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="c"&gt;// server errors&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="n"&gt;serverError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"server error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="c"&gt;// server error causes&lt;/span&gt;
        &lt;span class="c"&gt;//&lt;/span&gt;
        &lt;span class="n"&gt;badGatewayError&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"badGateway"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;internalServerError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"internal server error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;getStatusCode&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&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="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientError&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="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"unauthorized"&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;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resourceNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"resource not found"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"no error type"&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverError&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="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;badGatewayError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"bad gateway"&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;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;internalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"internal server error"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"no error type"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%w (%w)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unauthorized&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;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mex&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;mex&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"unauthorized"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%w (%w)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resourceNotFound&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;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mex&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;mex&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"resource not found"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%w (%w)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;internalServerError&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;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mex&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getStatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;mex&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"internal server error"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&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="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;customError&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wrapped&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;customError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrapped&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;customError&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="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"custom error"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestCustomErrorWrapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;err1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;customError&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;wrapped&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;err1&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="c"&gt;// it's much more convenient to do this&lt;/span&gt;
    &lt;span class="c"&gt;// wrap := fmt.Errorf("%w (%w)", err1, err2) and you do not need to implement Unwrap&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&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="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&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="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unwrap&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;err1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>go</category>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>What is a containerd snapshotter?</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Tue, 14 Feb 2023 17:38:45 +0000</pubDate>
      <link>https://dev.to/napicella/what-is-a-containerd-snapshotters-3eo2</link>
      <guid>https://dev.to/napicella/what-is-a-containerd-snapshotters-3eo2</guid>
      <description>&lt;p&gt;I have recently invested some time to understand how containerd works, in particular the containerd snapshotters. During the process, I took various notes that helped me along the way. Shortly after, I realized my notes could make for a boring article for whoever is curious about containers, containerd, containerd snapshotter and would like to know more. The main questions this article tries to answer are: What is a containerd snapshotter? And how does it work?&lt;br&gt;
The article is a bit long, so make sure you are comfortable before diving in.&lt;/p&gt;

&lt;p&gt;Disclaimer: I make no promise that these notes are 100% accurate or complete. The opposite, I am sure they are not, but they should be an ok account of how things work.&lt;/p&gt;
&lt;h4&gt;
  
  
  ToC
&lt;/h4&gt;



&lt;ul&gt;
&lt;li&gt;Concepts&lt;/li&gt;
&lt;li&gt;What's a snapshotter?&lt;/li&gt;
&lt;li&gt;Snapshot key and parent strings&lt;/li&gt;
&lt;li&gt;Snapshot ParentIDs&lt;/li&gt;
&lt;li&gt;Mounts&lt;/li&gt;
&lt;li&gt;containerd client.Pull&lt;/li&gt;
&lt;li&gt;ctr run&lt;/li&gt;
&lt;li&gt;What's a remote snapshotter?&lt;/li&gt;
&lt;li&gt;
Conclusions
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Concepts
&lt;/h3&gt;

&lt;p&gt;The section contains concepts mentioned in the containerd documentation and code that we will be utilizing in the rest of this document. I found that once I better understood them, everything else was much easier to digest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serialized Mount&lt;/strong&gt;&lt;br&gt;
It's a representation of the parameters of a Linux mount command/syscall. For example, if you were to mount a device on your system you will type something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &amp;lt;options &lt;span class="k"&gt;if &lt;/span&gt;any&amp;gt; &amp;lt;device&amp;gt; &amp;lt;mount-point&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The serialization of the mount is represented in containerd with a &lt;a href="https://github.dev/containerd/containerd/blob/97480afdac09c947d48f5e3a134db86c78f4bfa6/mount/mount.go#L17" rel="noopener noreferrer"&gt;go struct&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;// Mount is the lingua franca of containerd. A mount represents a
// serialized mount syscall. Components either emit or consume mounts.
type Mount struct {
    // Type specifies the host-specific of the mount.
    Type string
    // Source specifies where to mount from. Depending on the host system, this
    // can be a source path or device.
    Source string
    // Options contains zero or more fstab-style mount options. Typically,
    // these are platform specific.
    Options []string
}

func All(mounts []Mount, target string) error {
// omitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the target is missing from the struct because it's up to the caller to decide the target dir of the mount. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note: the Target field was recently added (Feb 2023 circa) as an optional field to the struct. The snapshotters currently baked in containerd do not use/do not need it so for now/rest of the document we will just say that the field was added because some future snapshotters might need it. The &lt;a href="https://github.com/containerd/containerd/issues/7839" rel="noopener noreferrer"&gt;github issue&lt;/a&gt; and PR contain more details on the reasoning&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;containerd architecture&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc4d20095nbgv4gv1o9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc4d20095nbgv4gv1o9t.png" alt="containerd-architecture.png" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;containerd is a daemon that can be interacted with via socket with grpc calls. What we will be focusing on in this document are the Snapshotter Service and the Snapshotter module. &lt;br&gt;
containerd supports some default snapshotters (overlay, native, btrfs, etc.) but also allows the implementation of new snapshotters without having to recompile the containerd binary, by building a snapshotter plugin.&lt;br&gt;
containerd uses a plugin architecture for the Snapshotter. To build one, you need to build a grpc service exposed via a socket that can receive requests from containerd. If you build yours in golang, this is much easier since containerd provides bindings for grpc, so "all needed" is to implement an interface and pass that to the grpc server binder.&lt;br&gt;
To make it more concrete, this is how creating a snapshotter looks like in code (error handling omitted for clarity, the complete example is available in the &lt;a href="https://github.com/containerd/containerd/blob/main/docs/PLUGINS.md" rel="noopener noreferrer"&gt;containerd repo&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"net"&lt;/span&gt;

    &lt;span class="s"&gt;"google.golang.org/grpc"&lt;/span&gt;

    &lt;span class="n"&gt;snapshotsapi&lt;/span&gt; &lt;span class="s"&gt;"github.com/containerd/containerd/api/services/snapshots/v1"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/containerd/containerd/contrib/snapshotservice"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create a gRPC server&lt;/span&gt;
    &lt;span class="n"&gt;rpc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Configure your custom snapshotter&lt;/span&gt;
    &lt;span class="n"&gt;sn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMyCoolSnapshotter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;// Convert the snapshotter to a gRPC service,&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;snapshotservice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromSnapshotter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// Register the service with the gRPC server&lt;/span&gt;
    &lt;span class="n"&gt;snapshotsapi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterSnapshotsServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Listen and serve&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;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/var/my-cool-snapshooter-socket"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serve&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="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: when using a custom plugin snapshotter, &lt;code&gt;ctr snapshot list&lt;/code&gt; does not show the snapshots that were created by the custom snapshotter.&lt;br&gt;
To see the snapshots created by the plugin, use the &lt;code&gt;--snapshotter&lt;/code&gt; flag, e.g. &lt;code&gt;ctr snapshot --snapshotter mysnapshotter list&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Snapshots Service (also depicted in the architecture diagram above) is the component that takes care of redirecting requests to the right Snapshotter among other things. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;NewMyCoolSnapshotter&lt;/code&gt; needs to return a type that implements the &lt;code&gt;Snapshotter&lt;/code&gt; interface. How this interface works is the focus of the rest of the document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Snapshotter&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Stat returns the info for an active or committed snapshot by name or&lt;/span&gt;
    &lt;span class="c"&gt;// key.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// Should be used for parent resolution, existence checks and to discern&lt;/span&gt;
    &lt;span class="c"&gt;// the kind of snapshot.&lt;/span&gt;
    &lt;span class="n"&gt;Stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Update updates the info for a snapshot.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// Only mutable properties of a snapshot may be updated.&lt;/span&gt;
    &lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldpaths&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Usage returns the resource usage of an active or committed snapshot&lt;/span&gt;
    &lt;span class="c"&gt;// excluding the usage of parent snapshots.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// The running time of this call for active snapshots is dependent on&lt;/span&gt;
    &lt;span class="c"&gt;// implementation, but may be proportional to the size of the resource.&lt;/span&gt;
    &lt;span class="c"&gt;// Callers should take this into consideration. Implementations should&lt;/span&gt;
    &lt;span class="c"&gt;// attempt to honer context cancellation and avoid taking locks when making&lt;/span&gt;
    &lt;span class="c"&gt;// the calculation.&lt;/span&gt;
    &lt;span class="n"&gt;Usage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Usage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Mounts returns the mounts for the active snapshot transaction identified&lt;/span&gt;
    &lt;span class="c"&gt;// by key. Can be called on an read-write or readonly transaction. This is&lt;/span&gt;
    &lt;span class="c"&gt;// available only for active snapshots.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// This can be used to recover mounts after calling View or Prepare.&lt;/span&gt;
    &lt;span class="n"&gt;Mounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Prepare creates an active snapshot identified by key descending from the&lt;/span&gt;
    &lt;span class="c"&gt;// provided parent.  The returned mounts can be used to mount the snapshot&lt;/span&gt;
    &lt;span class="c"&gt;// to capture changes.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// If a parent is provided, after performing the mounts, the destination&lt;/span&gt;
    &lt;span class="c"&gt;// will start with the content of the parent. The parent must be a&lt;/span&gt;
    &lt;span class="c"&gt;// committed snapshot. Changes to the mounted destination will be captured&lt;/span&gt;
    &lt;span class="c"&gt;// in relation to the parent. The default parent, "", is an empty&lt;/span&gt;
    &lt;span class="c"&gt;// directory.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// The changes may be saved to a committed snapshot by calling Commit. When&lt;/span&gt;
    &lt;span class="c"&gt;// one is done with the transaction, Remove should be called on the key.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// Multiple calls to Prepare or View with the same key should fail.&lt;/span&gt;
    &lt;span class="n"&gt;Prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// View behaves identically to Prepare except the result may not be&lt;/span&gt;
    &lt;span class="c"&gt;// committed back to the snapshot snapshotter. View returns a readonly view on&lt;/span&gt;
    &lt;span class="c"&gt;// the parent, with the active snapshot being tracked by the given key.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// This method operates identically to Prepare, except that Mounts returned&lt;/span&gt;
    &lt;span class="c"&gt;// may have the readonly flag set. Any modifications to the underlying&lt;/span&gt;
    &lt;span class="c"&gt;// filesystem will be ignored. Implementations may perform this in a more&lt;/span&gt;
    &lt;span class="c"&gt;// efficient manner that differs from what would be attempted with&lt;/span&gt;
    &lt;span class="c"&gt;// `Prepare`.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// Commit may not be called on the provided key and will return an error.&lt;/span&gt;
    &lt;span class="c"&gt;// To collect the resources associated with key, Remove must be called with&lt;/span&gt;
    &lt;span class="c"&gt;// key as the argument.&lt;/span&gt;
    &lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Commit captures the changes between key and its parent into a snapshot&lt;/span&gt;
    &lt;span class="c"&gt;// identified by name.  The name can then be used with the snapshotter's other&lt;/span&gt;
    &lt;span class="c"&gt;// methods to create subsequent snapshots.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// A committed snapshot will be created under name with the parent of the&lt;/span&gt;
    &lt;span class="c"&gt;// active snapshot.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// After commit, the snapshot identified by key is removed.&lt;/span&gt;
    &lt;span class="n"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

    &lt;span class="c"&gt;// Remove the committed or active snapshot by the provided key.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// All resources associated with the key will be removed.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// If the snapshot is a parent of another snapshot, its children must be&lt;/span&gt;
    &lt;span class="c"&gt;// removed before proceeding.&lt;/span&gt;
    &lt;span class="n"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

    &lt;span class="c"&gt;// Walk will call the provided function for each snapshot in the&lt;/span&gt;
    &lt;span class="c"&gt;// snapshotter which match the provided filters. If no filters are&lt;/span&gt;
    &lt;span class="c"&gt;// given all items will be walked.&lt;/span&gt;
    &lt;span class="c"&gt;// Filters:&lt;/span&gt;
    &lt;span class="c"&gt;//  name&lt;/span&gt;
    &lt;span class="c"&gt;//  parent&lt;/span&gt;
    &lt;span class="c"&gt;//  kind (active,view,committed)&lt;/span&gt;
    &lt;span class="c"&gt;//  labels.(label)&lt;/span&gt;
    &lt;span class="n"&gt;Walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;WalkFunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

    &lt;span class="c"&gt;// Close releases the internal resources.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// Close is expected to be called on the end of the lifecycle of the snapshotter,&lt;/span&gt;
    &lt;span class="c"&gt;// but not mandatory.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// Close returns nil when it is already closed.&lt;/span&gt;
    &lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;containerd "smart" client&lt;/strong&gt;&lt;br&gt;
containerd provides an opinionated client (written in go) which abstract grpc calls to containerd as well as providing utilities, that's why it's called a "smart" client. The &lt;code&gt;ctr&lt;/code&gt; command line, uses the client to interact with containerd and its utilities. Other tools that want to build on top of containerd, e.g. nerdctl leverage the smart client utils to perform operations that otherwise they would need to implement themself.&lt;br&gt;
The main point and the reason why I am mentioning the smart client is that I was expecting to find some of the snapshot-related operations in containerd (the agent), but they were not. In particular, at the beginning of my research, I thought snapshotters (or containerd) would perform the download of the layers, before discovering that the download is implemented as util in the "smart client".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DiffService&lt;/strong&gt;&lt;br&gt;
DiffService also referred to as DiffApplierService or simply DiffApplier is a service registered in containerd. The service (applies) untar the content of the layer into the mount. &lt;br&gt;
This will be much clear later in the doc.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;What's a snapshotter&lt;/strong&gt;?
&lt;/h3&gt;

&lt;p&gt;The main job of a snapshotter is to create a folder that can be used by containerd to unpack a layer. The folder must be prepared so that the act of unpacking layer n (applying in the containerd terminology) into the folder results in having a folder with the content of the layer n plus all other layers that proceeds the current one (n-1...1). The resulting folder is then a filesystem snapshot for the layers 1...n. &lt;br&gt;
During the process of creating the filesystem snapshot, the snapshotter also stores some metadata in the containerd metadata storage to reflect the status of the snapshot.&lt;/p&gt;

&lt;p&gt;Let's give an example. The client calls the snapshotter for the first time to "Prepare" a Snapshot for the first layer. Since this is the first layer, it does not have a parent Snapshot. So the snapshotter will create an empty dir, store the key and parent for this snapshot in the containerd metadata storage and return the dir to the caller (it does not return "just" a dir, but a Mount array. Bear with me a little longer). The client will then call the DiffApplier which untar the layer and merges it with the dir returned by the snapshotter (which was an empty dir). After this step, the dir contains the untarred first layer.&lt;br&gt;&lt;br&gt;
At this point, the client is ready for the second layer. It calls the snapshotter, this time providing a parent. The snapshotter will create an empty dir, then will store the key and parent for this snapshot (exactly as before). &lt;br&gt;
This time since the parent is not empty, the snapshotter will take an additional action: copy all the files from the parent dir (which is the one that contains the uncompressed layer) to the empty dir. Finally, it returns the dir to the caller.&lt;br&gt;
The client will then call the DiffApplier which uncompresses the layer and merge it with the dir returned by the snapshotter. This dir now contains the untarred first layer merged with the untarred second layer.&lt;br&gt;
The process goes on for all the layers, and the result is going to be a snapshot that contains all the layers untarred. &lt;br&gt;
Note how in the process we created N Snapshot, with N being the number of layers. The first snapshot contains one untarred layer, the second snapshot contains the first and the second, etc. &lt;br&gt;
This seems to be a little space inefficient. If you have an image with 4 layers, each one 10MB uncompressed, the snapshotter will be creating&lt;br&gt;
4 Snapshots with (10MB, 20MB, 30MB and 40MB respectively) for a total of 100 MB. In other words, we started with an image with 40MB big uncompressed and booked 100MB worth of space on the device.&lt;br&gt;
What we have just described is the &lt;a href="https://github.com/containerd/containerd/blob/main/snapshots/native/native.go" rel="noopener noreferrer"&gt;"native" snapshotter&lt;/a&gt;. Other snapshotters utilize different strategies that will mitigate or completely eliminate space inefficiency. We are going to look into one of them later, the overlay snapshotter.&lt;/p&gt;

&lt;p&gt;Before going a little deeper, there is an easy way to see this space inefficiency issue. Create a docker file with 4 layers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM alpine:latest  # alpine image is made of a single layer
RUN dd if=/dev/zero of=file_a bs=1024 count=10240 # 10MB of rubbish
RUN dd if=/dev/zero of=file_b bs=1024 count=10240 # 10MB of rubbish
RUN dd if=/dev/zero of=file_c bs=1024 count=10240 # 10MB of rubbish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After building and pushing the image, we can ask "ctr" to pull it, passing as a parameter the native snapshotter. Then we can see the size of the snapshots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# snapshot with alpine layer
&amp;gt; ls -lh /tmp/root-dir-example-snapshot/snapshots/1 | head -n 1
total 68K

# snapshot with alpine layer + 10 MB
&amp;gt; ls -lh /tmp/root-dir-example-snapshot/snapshots/16 | head -n 1
total 11M

# snapshot with alpine layer + 10 MB + 10 MB
&amp;gt; ls -lh /tmp/root-dir-example-snapshot/snapshots/17 | head -n 1
total 21M

# snapshot with alpine layer + 10 MB + 10 MB + 10MB
&amp;gt; ls -lh /tmp/root-dir-example-snapshot/snapshots/18 | head -n 1
total 31M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we look at the folders, we will see exactly what we expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -la /tmp/root-dir-example-snapshot/snapshots/1
total 10316
drwxr-xr-x 19 root     root             4096 Feb 10 18:48 .
drwx------ 16 nicola domain^users     4096 Feb 10 18:49 ..
drwxr-xr-x  2 root     root             4096 Feb 10 18:47 bin
drwxr-xr-x  2 root     root             4096 Jan  9 13:46 dev
drwxr-xr-x 17 root     root             4096 Feb 10 18:27 etc
... omitted

ls -la /tmp/root-dir-example-snapshot/snapshots/16
total 10316
drwxr-xr-x 19 root     root             4096 Feb 10 18:49 .
drwx------ 16 nicola domain^users     4096 Feb 10 18:49 ..
drwxr-xr-x  2 root     root             4096 Feb 10 18:49 bin
drwxr-xr-x  2 root     root             4096 Jan  9 13:46 dev
drwxr-xr-x 17 root     root             4096 Feb 10 18:49 etc
-rw-r--r--  1 root     root         10485760 Feb 10 18:27 file_a
... omitted

ls -la /tmp/root-dir-example-snapshot/snapshots/17

total 20556
drwxr-xr-x 19 root     root             4096 Feb 10 18:49 .
drwx------ 16 nicola domain^users     4096 Feb 10 18:49 ..
drwxr-xr-x  2 root     root             4096 Feb 10 18:49 bin
drwxr-xr-x  2 root     root             4096 Jan  9 13:46 dev
drwxr-xr-x 17 root     root             4096 Feb 10 18:27 etc
-rw-r--r--  1 root     root         10485760 Feb 10 18:27 file_a
-rw-r--r--  1 root     root         10485760 Feb 10 18:27 file_b
... omitted

 ls -la /tmp/root-dir-example-snapshot/snapshots/18
total 30796
drwxr-xr-x 19 root     root             4096 Feb 10 18:52 .
drwx------ 17 nicola domain^users     4096 Feb 10 18:51 ..
drwxr-xr-x  2 root     root             4096 Feb 10 18:51 bin
drwxr-xr-x  2 root     root             4096 Jan  9 13:46 dev
drwxr-xr-x 17 root     root             4096 Feb 10 18:27 etc
-rw-r--r--  1 root     root         10485760 Feb 10 18:27 file_a
-rw-r--r--  1 root     root         10485760 Feb 10 18:27 file_b
-rw-r--r--  1 root     root         10485760 Feb 10 18:27 file_c
... omitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Snapshot key and parent strings
&lt;/h3&gt;

&lt;p&gt;Looking at the Snapshotter interface, we see that most methods receive a &lt;code&gt;key&lt;/code&gt; and a &lt;code&gt;parent&lt;/code&gt; string. From the snapshotter point of view, these strings are completely opaque and together make up a token to be exchanged for an object. Snapshotter uses the &lt;code&gt;storage.CreateSnapshot(ctx, kind, key, parent, opts...)&lt;/code&gt; which returns a Snapshot record object. The method creates one if it does not exist, otherwise it returns the existing one. One of the fields of the Snapshot object is &lt;code&gt;ID&lt;/code&gt;, which identifies the snapshot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snapshot ParentIDs
&lt;/h3&gt;

&lt;p&gt;The Snapshot object returned by the &lt;code&gt;storage&lt;/code&gt; contains an array called &lt;code&gt;ParentIDs&lt;/code&gt;. The first element in the array is always the parent &lt;code&gt;ID&lt;/code&gt; of the current snapshot (being Prepared, being Committed, etc.)&lt;br&gt;
In the previous example, the Snapshot with ID &lt;code&gt;3&lt;/code&gt; will have the following &lt;code&gt;ParentIDs&lt;/code&gt; list, with &lt;code&gt;2&lt;/code&gt; being its parent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ParentIDs
0 = {string} "2"
1 = {string} "1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mounts
&lt;/h3&gt;

&lt;p&gt;Earlier I mentioned the snapshotter does not just return a dir. The Snapshotter.Prepare API returns an array of mounts. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looking at some of the existing snapshotter (like native and overlay), I would assume that most snapshotter only ever need a single mount. The Prepare API interface return type is an array, probably to make the API more generic for snapshotters that might need multiple mounts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Something obvious, but still worth mentioning. The &lt;code&gt;Type&lt;/code&gt; in the Mount returned by the Snapshotter needs to be a filesystem type supported by the underlying kernel. For example, assume someone wants to write a Snapshotter to expose the container filesystem as a RaiserFS. The snapshotter would need to return "raiserfs" as the mount type. The client will call the Unix mount and that will succeed only if raiserfs is enabled in the kernel.&lt;/p&gt;

&lt;h3&gt;
  
  
  containerd smart client client.Pull
&lt;/h3&gt;

&lt;p&gt;This section answers the question - what happens when calling  the smart client "client.Pull"?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"docker.io/library/redis:alpine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;containerd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPullUnpack&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following the chain of calls from &lt;a href="https://github.com/containerd/containerd/blob/main/pull.go#L42" rel="noopener noreferrer"&gt;client.Pull&lt;/a&gt;, we see: &lt;a href="https://github.com/containerd/containerd/blob/5c37d3827b01bff9b67bc9b73a60cc35f4bfb96a/pkg/unpack/unpacker.go#L161" rel="noopener noreferrer"&gt;unpacker.Unpack&lt;/a&gt; -&amp;gt; &lt;a href="https://github.com/containerd/containerd/blob/main/diff/proxy/differ.go#L47" rel="noopener noreferrer"&gt;diffRemote&lt;/a&gt; -&amp;gt; over grpc -&amp;gt; &lt;a href="https://github.com/containerd/containerd/blob/5c37d3827b01bff9b67bc9b73a60cc35f4bfb96a/services/diff/service.go#L66" rel="noopener noreferrer"&gt;diffService&lt;/a&gt; -&amp;gt; &lt;a href="https://github.com/containerd/containerd/blob/5c37d3827b01bff9b67bc9b73a60cc35f4bfb96a/diff/apply/apply.go#L50" rel="noopener noreferrer"&gt;Applier&lt;/a&gt; -&amp;gt; which on linux eventually calls the apply function in the &lt;a href="https://github.com/containerd/containerd/blob/5c37d3827b01bff9b67bc9b73a60cc35f4bfb96a/diff/apply/apply_linux.go#L31C1-L31C1" rel="noopener noreferrer"&gt;apply_linux&lt;/a&gt; file. That calls the Apply function in the &lt;a href="https://github.com/containerd/containerd/blob/main/archive/tar.go#L140" rel="noopener noreferrer"&gt;archive/tar.go&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;Note that containerd only performs the mount for the first layer (see &lt;a href="https://github.com/containerd/containerd/blob/main/diff/apply/apply_linux.go#L31" rel="noopener noreferrer"&gt;code&lt;/a&gt;). That's because the overlay snapshotter returns a bind mount for the first layer so the apply function ends up in this &lt;a href="https://github.com/containerd/containerd/blob/main/diff/apply/apply_linux.go#L55" rel="noopener noreferrer"&gt;branch&lt;/a&gt;. All the mounts returned by the snapshotter.Prepare after that are of type &lt;code&gt;overlay&lt;/code&gt; and are not mounted by containerd.&lt;/p&gt;

&lt;p&gt;Also note that tar.go does not just untar the content of the layer, but also converts the OCI &lt;a href="https://github.com/opencontainers/image-spec/blob/main/layer.md" rel="noopener noreferrer"&gt;whiteouts and opaque whiteouts&lt;/a&gt; to &lt;a href="https://github.com/opencontainers/image-spec/blob/main/layer.md" rel="noopener noreferrer"&gt;Overlay whiteouts&lt;/a&gt;. This is a topic for a different article, but in short: suppose the file called &lt;code&gt;my-file&lt;/code&gt; was removed in layer N. This is stored in the layer as &lt;code&gt;.wh.my-file&lt;/code&gt;. During untar for that layer, when the function encounters the OCI whiteout &lt;code&gt;.wh.my-file&lt;/code&gt;, it is going to store it in the snapshot folder as an overlay whiteout (a character file with device 0 0 with the same name as the original file, eg. &lt;code&gt;my-file&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Simplifying the chain of calls and leaving out details, we can summarize the process of pulling and creating snapshots with the following pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for each layer in the image manifest
  m, err = snapshotter.Prepare
  if prepare returns ErrAlreadyExist continue 
  // this check allows remote snapshots.
  // In reality, the client will also call 
  // snapshotter.Stat before skipping the creation of the snapshot.
  // Will be discussed in the section about remote snapshotter

  download layer
  load layer in the content store
  if m is overlay
    untar + convert from OCI spec to overlay in the overlay upperdir
  else if bind mount
    create temp mount 
    untar + convert from OCI spec to overlay in the temp mount
  snapshotter.Commit
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note 1: &lt;code&gt;ctr pull&lt;/code&gt; uses the client differently, which results in a slightly different behavior. In particular, all the layers are first downloaded and loaded in the containerd content store, before asking the snapshotter to create new snapshots.&lt;/p&gt;

&lt;p&gt;Note 2: In the example, we use the &lt;code&gt;containerd.WithPullUnpack&lt;/code&gt; so that we not only fetch and download the content into containerd's content store but also unpack it into a snapshotter for use as a root filesystem.&lt;br&gt;&lt;br&gt;
If you do not pass that parameter, the snapshotter is not called.&lt;/p&gt;
&lt;h3&gt;
  
  
  ctr run
&lt;/h3&gt;

&lt;p&gt;This section answers the question, what happens when running &lt;code&gt;ctr run&lt;/code&gt; from a snapshotter point of view?&lt;br&gt;
During container start, the client is going to call the snapshotter again to create an Active Snapshot that serves as a read-write layer for the container. The active Snapshot is going to be mounted before the container starts and unmounted when the container exit. If we start a long-running container in one terminal, we can hop into another terminal and see the mounts. For example, for an overlay snapshotter we will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; mount | grep overlay
overlay on /run/containerd/io.containerd.runtime.v2.task/default/example/rootfs type overlay (rw,relatime,lowerdir=/tmp/root-dir-example-snapshot/snapshots/22/fs:/tmp/root-dir-example-snapshot/snapshots/21/fs:/tmp/root-dir-example-snapshot/snapshots/20/fs:/tmp/root-dir-example-snapshot/snapshots/19/fs,upperdir=/tmp/root-dir-example-snapshot/snapshots/24/fs,workdir=/tmp/root-dir-example-snapshot/snapshots/24/work,xino=off)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for a native snapshotter we will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mount
....
/dev/mapper/vg0--aed748-root on /run/containerd/io.containerd.runtime.v2.task/default/example/rootfs type ext4 (rw,relatime,errors=remount-ro)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you add something to the mount folder, the container is also gonna see it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At this point, we are in a much better position to understand the documentation of the &lt;a href="https://github.com/containerd/containerd/blob/main/snapshots/snapshotter.go#L139" rel="noopener noreferrer"&gt;Snapshotter interface&lt;/a&gt;.&lt;br&gt;
Besides the Prepare, Commit, Stat and Mount that we have already mentioned above, the interface also contains Remove which is called when deleting a snapshot (eg &lt;code&gt;ctr snapshot rm &amp;lt;key&amp;gt;&lt;/code&gt;) and during container clean up (to remove the active snapshot).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What's a remote snapshotter?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/containerd/containerd/blob/main/docs/remote-snapshotter.md" rel="noopener noreferrer"&gt;containerd docs&lt;/a&gt; description of remote snapshotters is really clear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Containerd allows snapshotters to reuse snapshots existing somewhere managed by them.&lt;/p&gt;

&lt;p&gt;Remote Snapshotter is a snapshotter that leverages this functionality and reuses snapshots that are stored in a remotely shared place. These remotely shared snapshots are called remote snapshots. Remote snapshotter allows containerd to prepare these remote snapshots without pulling layers from registries, which hopefully shortens the time to take for image pull.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;containerd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPullUnpack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;containerd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPullSnapshotter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-remote-snapshotter"&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;client.Pull with the option "WithPullUnpack" informs the client that it needs to call Snapshotter.Prepare before downloading the layers. If the call returns &lt;code&gt;ErrAlreadyExist&lt;/code&gt;, the client calls the Snapshotter.Stat to confirm. If the call returns no error, it skips downloading the layer and skips loading it into the content store.&lt;br&gt;
If the Snapshotter.Prepare instead returns nil, the client is going to download the layer and load it to the content store before calling the DiffApplierService.&lt;/p&gt;

&lt;p&gt;This behavior allows us to set up snapshots out of band, that is outside the "normal" workflow. One such example is the &lt;a href="https://github.com/awslabs/soci-snapshotter" rel="noopener noreferrer"&gt;soci snapshotter&lt;/a&gt; which allows image lazy loading. The snapshotter ships with a "rpull" command which performs this out of band prep. During rpull, the command calls the soci snapshotter which creates fuse mounts for each layer that has an index (remote snapshot). For layers that do not have an index, it downloads them as usual (local snapshot).&lt;br&gt;
The local snapshot is created with overlay mount. Anyway, this is just a detail, the important bit is that folders created for a local snapshot only contain that layer, which is exactly what overlay does.&lt;br&gt;
For example, after rpull we'll see something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; mount
soci on /var/lib/soci-snapshotter-grpc/snapshotter/snapshots/1/fs type fuse.rawBridge (rw,nodev,relatime,user_id=0,group_id=0,allow_other)

soci on /var/lib/soci-snapshotter-grpc/snapshotter/snapshots/4/fs type fuse.rawBridge (rw,nodev,relatime,user_id=0,group_id=0,allow_other)

soci on /var/lib/soci-snapshotter-grpc/snapshotter/snapshots/6/fs type fuse.rawBridge (rw,nodev,relatime,user_id=0,group_id=0,allow_other)

... omitted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During ctr run, the client asks the snapshotter (Prepare) for one more Active snapshot (which is going to be used as a read-write layer for the container). The snapshotter returns an overlay mount that overlays the fuse mounts and the local snapshots together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; mount
soci on /var/lib/soci-snapshotter-grpc/snapshotter/snapshots/1/fs type fuse.rawBridge (rw,nodev,relatime,user_id=0,group_id=0,allow_other)

soci on /var/lib/soci-snapshotter-grpc/snapshotter/snapshots/4/fs type fuse.rawBridge (rw,nodev,relatime,user_id=0,group_id=0,allow_other)

soci on /var/lib/soci-snapshotter-grpc/snapshotter/snapshots/6/fs type fuse.rawBridge (rw,nodev,relatime,user_id=0,group_id=0,allow_other)

overlay on /run/containerd/io.containerd.runtime.v2.task/default/sociExample/rootfs type overlay (rw,relatime,lowerdir=/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/10/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/9/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/8/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/7/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/6/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/5/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/4/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/3/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/2/fs:/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/1/fs,upperdir=/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/11/fs,workdir=/var/lib/soci-snapshotter-grpc/snapshotter/snapshots/11/work,xino=off)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;A special thanks to &lt;a href="https://github.com/majd" rel="noopener noreferrer"&gt;Majd&lt;/a&gt; for reviewing and helping me come up with the right questions. &lt;br&gt;
Kudos to &lt;a href="https://github.com/djdongjin" rel="noopener noreferrer"&gt;Jin&lt;/a&gt;, who works on the &lt;a href="https://github.com/awslabs/soci-snapshotter" rel="noopener noreferrer"&gt;soci-snapshotter&lt;/a&gt; and has been so kind to clarify some of the doubts I had.&lt;br&gt;
Thanks to &lt;a href="https://dev.to/qiutongs"&gt;qiutongs&lt;/a&gt; for the comment, which accurately highlighted that containerd performs only one mount operation when performing a pull operation. I have updated the article to reflect that. &lt;/p&gt;

&lt;p&gt;If you liked the article you might want to also check out &lt;a href="https://dev.to/napicella/how-are-docker-images-built-a-look-into-the-linux-overlay-file-systems-and-the-oci-specification-175n"&gt;How are docker images built?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For same history on containerd snapshotters, see the &lt;a href="https://blog.mobyproject.org/where-are-containerds-graph-drivers-145fc9b7255" rel="noopener noreferrer"&gt;moby project blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Check out &lt;a href="https://twitter.com/napicellatwit" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to get new posts in your feed.&lt;br&gt;
Credit for the cover image to &lt;a href="https://unsplash.com/@joshua_j_woroniecki" rel="noopener noreferrer"&gt;Joshua Woroniecki&lt;/a&gt; via &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




</description>
      <category>programming</category>
      <category>webdev</category>
      <category>snippet</category>
    </item>
    <item>
      <title>Your laptop is a distributed system</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Tue, 22 Nov 2022 15:59:12 +0000</pubDate>
      <link>https://dev.to/napicella/your-laptop-is-a-distributed-system-21ea</link>
      <guid>https://dev.to/napicella/your-laptop-is-a-distributed-system-21ea</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SSPU9NxV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xgvvejrawzjjsflt7um3.jpeg" alt="Cover image, integrated circuit" width="720" height="565"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Photo by &lt;a href="https://unsplash.com/@lazycreekimages?utm_source=medium&amp;amp;utm_medium=referral"&gt;Michael Dziedzic&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When we think about distributed systems, we picture multiple machines communicating to each other to fulfill a request, perhaps a cluster of nodes storing data in a distributed database, or more abstract concepts like consistency in the &lt;a href="https://en.wikipedia.org/wiki/CAP_theorem"&gt;CAP theorem&lt;/a&gt;[1].&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But have you have ever seen a distributed system with your eyes?&lt;/em&gt;&lt;/strong&gt; I do not mean an architecture diagram, I mean REALLY seeing it.&lt;br&gt;&lt;br&gt;
I bet you do and it is likely the thing you are using to read this post, your laptop. If the last sentence surprised you, keep reading and we shall see why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistency
&lt;/h2&gt;

&lt;p&gt;As you already know, memory is the device used to store data used by a computer. Memory varies for its speed, cost, and volatility. At the one end of the spectrum, we have fast but expensive memory like &lt;a href="https://en.wikipedia.org/wiki/Static_random-access_memory"&gt;SRAM&lt;/a&gt;. At the other end, we have hard drives, slow (when compared to the CPU speed), cheap, and non-volatile as they do not require power to keep data stored. In the middle, we have the &lt;a href="https://en.wikipedia.org/wiki/Dynamic_random-access_memory"&gt;DRAM&lt;/a&gt; technology, popular due to its cost-effectiveness.&lt;br&gt;&lt;br&gt;
When talking about speed, it’s good to have an idea of the numbers involved to access data stored in memory:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IUzWNVFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2xaashi1ep8fzu0q2r1.png" alt="Latency numbers" width="880" height="440"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Latency numbers — credit to &lt;a href="https://gist.github.com/hellerbarde/2843375"&gt;hellerbarde&lt;/a&gt; via Github&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;  Accessing data in an SRAM takes 0.5 nanosecond&lt;/li&gt;
&lt;li&gt;  Accessing data from the DRAM takes 1000 nanoseconds. Roughly a thousand times slower than access in SRAM&lt;/li&gt;
&lt;li&gt;  Accessing a date from a hard drive takes 2 million nanoseconds, that is 2 milliseconds. Roughly a million time slower than accessing an SRAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s assume you have a 1 GHz CPU, granted some simplifications [2] This means your CPU can run one instruction per nanosecond. In a naïve implementation, if we were to access a DRAM, the CPU would need to wait for about 1000 cycles doing basically nothing instead of running 1000 instructions. &lt;strong&gt;&lt;em&gt;Lots of wasted compute capacity.&lt;/em&gt;&lt;/strong&gt; If instead, we were to use an SRAM, the memory would be able to keep up with the CPU, thus not wasting precious cycles.&lt;/p&gt;

&lt;p&gt;Ideally, we would love to equip our computers with the biggest amount of high-speed memory, but unfortunately, high-speed memory is expensive which brings us to a fundamental theorem of engineering: &lt;strong&gt;&lt;em&gt;there is no such thing as free lunch&lt;/em&gt;&lt;/strong&gt;. Engineering is a matter of finding the best trade-off between costs and performance.&lt;/p&gt;

&lt;p&gt;Luckily two properties help computer designers with this trade-off: spatial and temporal locality. Spatial locality property says that instructions that are stored nearby to the recently executed instruction have high chances of execution. Temporal locality instead expresses the tendency of programs to use the same piece of data multiple times during the course of their execution. Those emergent properties do not surprise us since the CPU runs instructions one after the other and programs tend to access data multiple times during execution. These somewhat intuitive properties suggest organizing our memory hierarchically, with faster memory close to the CPU (SRAM commonly used for caches and CPU registers) and slower memory in the upper layer of the hierarchy (DRAM also referred to as main memory).&lt;br&gt;&lt;br&gt;
During a read operation, the CPU tries to access the data from the cache and use it if it’s there. In case it is not, the data needs to be copied from the main memory to the cache before the CPU can use it. The CPU does not copy just the data it needs, but a whole block of contiguous addresses (spatial locality).&lt;br&gt;&lt;br&gt;
Because the cache must be smaller than the main memory, at some point it will become full and some of the entries in the cache must be evicted. Which ones? The policy most commonly used is to remove the &lt;a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)"&gt;least recently used item&lt;/a&gt; (temporal locality), usually with some approximation depending again on a cost-benefit trade-off.&lt;/p&gt;

&lt;p&gt;To gain better performance, system designers build systems with multiple CPUs interconnected on the same physical bus. Each CPU access the same memory which means that memory becomes a shared resource.&lt;br&gt;&lt;br&gt;
To recap, we have multiple CPUs interconnected on a bus, each CPU has its own cache and they all access the same shared memory:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8R9rvaqg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8a3i9kgnztva1cxcyce.png" alt="Cache organization" width="429" height="294"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cache organization — credit to Kunal Buch, &lt;a href="https://creativecommons.org/licenses/by-sa/4.0"&gt;CC BY-SA 4.0&lt;/a&gt;, via Wikimedia Commons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each CPU populates the cache based on the data it needs to access, the so-called &lt;em&gt;local copy&lt;/em&gt; of the data. The same data could be used by different CPUs which means we could end up with multiple copies of the same data in different caches. But what happens when one of the CPU updates its local copy of the data? We have a typical &lt;strong&gt;distributed system&lt;/strong&gt; problem: keeping multiple copies of the same data synchronized when one of the processors updates its local copy. This is what distributed systems folks call a &lt;strong&gt;consistency problem&lt;/strong&gt;. For hardware engineers instead, this is the &lt;a href="https://en.wikipedia.org/wiki/Cache_coherence"&gt;cache coherence&lt;/a&gt; problem. [3]&lt;/p&gt;

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

&lt;p&gt;Our laptop is a highly coupled distributed system where each node is a CPU.&lt;br&gt;&lt;br&gt;
Albeit hardware engineers need to solve a class of problems frequently encountered in a multi-machine distributed system, a major distinction separates the way the problems are solved. In a computer, components are physically connected via each other on the bus, if not even collocated on the same chip. Multi CPUs systems do not need to deal with network partitions, message reordering, partial failure, and all the other properties of an unreliable network.&lt;/p&gt;

&lt;p&gt;Turns out that the network is really &lt;a href="https://www.usenix.org/conference/lisa19/presentation/yu"&gt;what makes distributed systems hard&lt;/a&gt; [4].&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/napicellatwit"&gt;Twitter&lt;/a&gt; to get new posts in your feed.&lt;br&gt;
Credit for the cover image to &lt;a href="https://unsplash.com/@lazycreekimages?utm_source=medium&amp;amp;utm_medium=referral"&gt;Michael Dziedzic&lt;/a&gt; via &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;[1] &lt;strong&gt;Consistency - Availability - Partition tolerance tradeoffs.&lt;/strong&gt; The extension to the CAP theorem is called &lt;a href="https://en.m.wikipedia.org/wiki/PACELC_theorem"&gt;PACELC&lt;/a&gt;&lt;br&gt;
[2] The simplification does not account for pipelining, multiple cores, instructions that take multiple cycles to complete&lt;br&gt;
[3] &lt;strong&gt;Out of order execution.&lt;/strong&gt; The similarities between our laptop and distributed systems do not stop here. Modern CPUs adopts another trick to improve CPU performance: they are allowed to run instructions in an order different from the specified in the program. Of course, this means increasing the complexity of the system which now needs to guarantee consistency in the presence of instructions executed out of order&lt;br&gt;
[4] Of course, this does not mean that CPU design is easy&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The API Gateway Impossibility (the story of an API migration)</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Thu, 24 Mar 2022 16:11:13 +0000</pubDate>
      <link>https://dev.to/napicella/the-api-gateway-impossibility-1pho</link>
      <guid>https://dev.to/napicella/the-api-gateway-impossibility-1pho</guid>
      <description>&lt;p&gt;At work we had to migrate an API Gateway from one AWS account to another. The migration had to be without downtime and be gradual (slowly shifting traffic from the old API to the new one). Since we do not control clients, we can't ask them to gradual shift calls to a new endpoint.&lt;br&gt;
We thought it would easy - and we were quite wrong.&lt;/p&gt;

&lt;p&gt;The idea was to point our existing domain name (app.domain.name.com in the following examples) to two different API Gateways, the old and new. And we were quite sure that could be achieved with some simple DNS changes. This is how we thought we would approach it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate the API Gateway in the new account&lt;/li&gt;
&lt;li&gt;Add a weight to the existing DNS A record (in Route53 hosted zone) which points to the old API Gateway&lt;/li&gt;
&lt;li&gt;Insert a new A record with a weight in the Route53 hosted zone which resolves to the new API Gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, we wanted to move from this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record Type&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;app.domain.name.com&lt;/td&gt;
&lt;td&gt;API-GW-domain-old&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record Type&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;255&lt;/td&gt;
&lt;td&gt;app.domain.name.com&lt;/td&gt;
&lt;td&gt;API-GW-domain-old&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;255&lt;/td&gt;
&lt;td&gt;app.domain.name.com&lt;/td&gt;
&lt;td&gt;API-GW-domain-new&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Adding the same weight (255 in the example), makes Route53 select with equal probability between the two records. We expected that to be how we could slowly shift traffic from the old api to the new. What follows is the result of trying to implement what described above, failed attempts and misunderstandings in the API Gateway and Route53 integration.&lt;/p&gt;

&lt;p&gt;We first created the new API in a new AWS account, just to stumble upon a CloudFormation failure. The API Gateway custom domain resource failed because a custom domain with that domain already existed in another account. Turns out the domain assigned to the regional endpoint &lt;strong&gt;must&lt;/strong&gt; be unique. Thus the CloudFormation failure above. The old API already had an API Gateway custom domain resource with that domain (app.domain.name.com).&lt;/p&gt;

&lt;p&gt;Cool, so that does not work. We tried another approach. Create the new API Gateway and assign it a different custom domain, one that would differ only for the left most subdomain. Namely: &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record Type&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;255&lt;/td&gt;
&lt;td&gt;app.domain.name.com&lt;/td&gt;
&lt;td&gt;api-old.domain.name.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;255&lt;/td&gt;
&lt;td&gt;app.domain.name.com&lt;/td&gt;
&lt;td&gt;api-new.domain.name.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;api-old.domain.name.com&lt;/td&gt;
&lt;td&gt;API-GW-domain-old&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;api-new.domain.name.com&lt;/td&gt;
&lt;td&gt;API-GW-domain-new&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Having the api domains differ only in the subdomain makes it simple to create a certificate that covers them both (*.domain.name.com).&lt;/p&gt;

&lt;p&gt;So, we updated the certificate, we deployed the new API in the new account (this time that worked), created the DNS records as described in the table above. It was time to verify it was working. It's here that we realized that accessing the old api via the "api-old.domain.name.com" worked, accessing the new api via the "api-new.domain.name.com" worked...but accessing the app via "app.domain.name.com" did not. What we were expecting was a round robin between the old and the new domain, what we got instead was the always eloquent API GW error message: &lt;code&gt;{"message":"Forbidden"}&lt;/code&gt;.&lt;br&gt;
Why though? It &lt;em&gt;seems&lt;/em&gt; API Gateway service checks the Host header in the request and expects that to be one of the API Gateway custom domain. In other words, the following does not work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl https://app.domain.name.com/demo
{"message":"Forbidden"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl -H "Host: api-old.domain.name.com" https://app.domain.name.com/demo
{"message":"Hello, World!"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we thought about using a &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html#wildcard-custom-domain-names"&gt;wildcard custom domain&lt;/a&gt;. That also does not work because as the docs say: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can't create a wildcard custom domain name if a different AWS account has created a custom domain name that conflicts with the wildcard custom domain name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The docs also say that it is possible to request an exception for that.&lt;/p&gt;

&lt;p&gt;The lesson learned is that pointing a domain name to two API Gateways is impossible. So how do you migrate it? Some options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;have the new API issue a 301 redirect to the old one&lt;/li&gt;
&lt;li&gt;old API proxies to the new one&lt;/li&gt;
&lt;li&gt;deploy a proxy which shift traffic between the old API to the new&lt;/li&gt;
&lt;li&gt;request exception for wildcard domain to be used in two different AWS accounts&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>aws</category>
    </item>
    <item>
      <title>How to monitor your system dependencies</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Sun, 03 Jan 2021 18:03:46 +0000</pubDate>
      <link>https://dev.to/napicella/how-to-monitor-your-dependencies-15ii</link>
      <guid>https://dev.to/napicella/how-to-monitor-your-dependencies-15ii</guid>
      <description>&lt;p&gt;In the past few years, the &lt;a href="https://services.google.com/fh/files/misc/state-of-devops-2019.pdfhttps://services.google.com/fh/files/misc/state-of-devops-2019.pdf" rel="noopener noreferrer"&gt;DevOps reports&lt;/a&gt; have shown how high-performance teams use monitoring to quickly detect errors, reduce downtime and costs. &lt;br&gt;
Monitoring a complex application is a significant engineering endeavor in and of itself, but thanks to effort of cloud providers, developers can instrument their system with few clicks or configurations.&lt;/p&gt;

&lt;p&gt;When it comes to your system health, monitoring your dependencies is just as important as monitoring your system. If your system is experiencing issues, your system dashboards and alarms should highlight if one of your dependency is having a bad time. &lt;br&gt;
In the rest of the article I show a simple pattern to monitor your dependencies. The examples use &lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/11/amazon-cloudwatch-launches-embedded-metric-format/" rel="noopener noreferrer"&gt;CloudWatch Embedded Metric Format &lt;/a&gt;and golang but the underlying idea can be applied to other cloud platforms or languages.&lt;/p&gt;
&lt;h2&gt;
  
  
  What’s EMF?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/11/amazon-cloudwatch-launches-embedded-metric-format/" rel="noopener noreferrer"&gt;CloudWatch Embedded Metric Format&lt;/a&gt; allows to automatically generate metrics from logs. As a developer you need to log the metrics as JSON to stdout and Cloud Watch takes care of publishing the metric on your behalf.&lt;br&gt;
You can use one of the &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Libraries.html" rel="noopener noreferrer"&gt;AWS client libraries&lt;/a&gt; for NodeJS, Python or Java to build and log an embedded metric structure. Sadly, at the moment AWS has not published a client library for Golang which means you need to create the JSON object yourself following the schema described in the &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html" rel="noopener noreferrer"&gt;specification&lt;/a&gt;. It’s relatively straightforward and not surprisingly a few community maintained projects do exactly that, for example &lt;a href="https://github.com/prozz/aws-embedded-metrics-golang" rel="noopener noreferrer"&gt;aws-embedded-metrics-golang&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I find EMF very convenient but it’s usage is not strictly necessary to monitor your dependencies. You can publish the metric yourself if that’s what you want to do.&lt;/p&gt;
&lt;h2&gt;
  
  
  Instrumenting the client
&lt;/h2&gt;

&lt;p&gt;The main idea is to instrument the client you use to call your service. Regardless of the language or the library you use, it’s almost certain that the client you use to call your dependencies allows to plug in custom logic. You can leverage it to log the EMF metric. &lt;br&gt;
For example, assuming you use a golang http client, you can implement the RoundTripper interface:&lt;/p&gt;


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


&lt;p&gt;The code implements the RoundTripper interface with the logic to log details about the http calls. In the example, we log the Duration and the result of the call (client error or server error ). Once I deployed the code (in my case to Lambda) and triggered it a few times, we can see the metrics published to CloudWatch:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcka8b3e2b3hl0p541imb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcka8b3e2b3hl0p541imb.png" alt="Cloud Watch Metrics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instrumenting a different client is very similar. For example, here we instrument the golang AWS SDK to log calls made to other AWS services:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;p&gt;With &lt;a href="https://aws.amazon.com/cloudwatch/?&amp;amp;trk=ta_a134p000003yEACAA2&amp;amp;trkCampaign=pac-edm-2020_M&amp;amp;GAdoption_CloudWatchProductLandingPage&amp;amp;sc_channel=ta&amp;amp;sc_campaign=pac-edm-2020_M&amp;amp;G_Website_UseCase_MonitoringObservability_CloudWatchPDP&amp;amp;sc_outcome=Enterprise_Digital_Marketing&amp;amp;sc_geo=NAMER&amp;amp;sc_country=mult" rel="noopener noreferrer"&gt;AWS CloudWatch&lt;/a&gt; you can easily set up monitoring for your AWS resources and applications. EMF is a very convenient way to plug custom metrics in your system. As I mentioned, the pattern is quite generic and you can easily port it in other languages or even other monitoring systems.&lt;/p&gt;

&lt;p&gt;If you develop on AWS take also a look at &lt;a href="https://aws.amazon.com/xray/?&amp;amp;trk=ta_a134p000003yEA7AAM&amp;amp;trkCampaign=pac-edm-2020_M&amp;amp;GAdoption_XRayPDP&amp;amp;sc_channel=ta&amp;amp;sc_campaign=pac-edm-2020_M&amp;amp;G_Website_UseCase_MonitoringObservability_XRayPDP&amp;amp;sc_outcome=Enterprise_Digital_Marketing&amp;amp;sc_geo=NAMER&amp;amp;sc_country=mult" rel="noopener noreferrer"&gt;AWS X-Ray&lt;/a&gt;. With AWS X-Ray you can visualize and analyze the health, performance, and availability of your applications in one location, and see an end-to-end view of requests as they travel through your application.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/napicellatwit" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to get new posts in your feed.&lt;br&gt;
Credit for the cover image to &lt;a href="https://unsplash.com/@lukechesser?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Luke Chesser&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/analytics?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>devops</category>
      <category>aws</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>This article has 86 reactions and 11 comments</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Wed, 13 May 2020 11:57:35 +0000</pubDate>
      <link>https://dev.to/napicella/this-article-has-x-reactions-and-y-comments-1jlf</link>
      <guid>https://dev.to/napicella/this-article-has-x-reactions-and-y-comments-1jlf</guid>
      <description>&lt;p&gt;Tom Scott made a video recently where the title contains the number of views that the video has. The title is kept up to date with the number of views by a program which calls the youtube API. It's a great video if &lt;a href="https://www.youtube.com/watch?v=BxV14h0kFs0"&gt;you haven't seen it yet&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Anyway, I really liked that idea and wanted to try it with this dev.to article. The article is different from Tom's video in two ways: the title of the article contains the number of reactions and comments instead of the views. The second thing is the topic. Tom talks about the invention of the APIs. What I want to talk about is how to &lt;em&gt;use APIs&lt;/em&gt; to call the dev.to service.&lt;/p&gt;

&lt;p&gt;The title of the article won't exactly match the number of comments and reactions because the program that updates the title only runs every minute. Also, I suspect the dev.to api is eventual consistent, which means the number of reactions and comments will be updated at some point, but it does not happen in real time. If, when you are reading the article, the title matches or is close to the number of reactions or comments, it means that the code is still working; but it could stop working any time. The &lt;a href="https://docs.dev.to/api/"&gt;dev.to api&lt;/a&gt; I am using is in beta and could change or being dismissed in the future.&lt;/p&gt;

&lt;p&gt;In the remainder of the article I am going to talk about the steps I took to use the dev.to api.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenAPI specification
&lt;/h3&gt;

&lt;p&gt;The first step to build a program which interacts with a service is to discover its api.&lt;/p&gt;

&lt;p&gt;OpenAPI specification defines a standard which allows both developers and machines to discover and understand the capabilities of a service. It serves as documentation and as a contract.&lt;/p&gt;

&lt;p&gt;An API defined via an OpenApi document is &lt;em&gt;machine friendly&lt;/em&gt;, which means we can build programs that interpret that document and do something useful with it. For example, the &lt;a href="https://github.com/deepmap/oapi-codegen"&gt;oapi-codegen&lt;/a&gt; is able to read the document and generate a golang http client to interact with the api. It can also generate a stub service which can be used both as starter or as a mock for your tests.&lt;/p&gt;

&lt;p&gt;The other benefit of OpenAPI is that is also &lt;em&gt;human friendly&lt;/em&gt;. It can be used as documentation, hosted on a server and browsed via a browser. This is what dev.to uses and anybody can read and download its OpenApi spec &lt;a href="https://docs.dev.to/api/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  AuthN and AuthZ
&lt;/h3&gt;

&lt;p&gt;The second step to build the program is getting valid credentials.&lt;/p&gt;

&lt;p&gt;Services need a way to know that you are who you say you are (authentication). Once they have established that, they might also need to verify that you can carry on the action you want to perform (authorization).&lt;br&gt;
In the case of dev.to, the &lt;em&gt;api key&lt;/em&gt; is used for authN and authZ. An &lt;em&gt;api key&lt;/em&gt; is a pseudo random service generated string which is associated with your account. To obtain one for dev.to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visit &lt;a href="https://dev.to/settings/account"&gt;https://dev.to/settings/account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;in the "DEV API Keys" section create a new key by adding a description and clicking on "Generate API Key" &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Generate the client
&lt;/h3&gt;

&lt;p&gt;Once we have the OpenAPI document and the &lt;em&gt;api key&lt;/em&gt;, we are ready to make good use of them.  &lt;/p&gt;

&lt;p&gt;I have used the &lt;a href="https://github.com/deepmap/oapi-codegen"&gt;oapi-codegen&lt;/a&gt; tool to generate a golang client to interact with the api. The process was quick. I downloaded the OpenAPI document for dev.to and run the &lt;em&gt;oapi-codegen&lt;/em&gt;, and that was it, no errors or warnings. Sometimes you need to tweak a bit the document or the code generator to make it work, but not in this case. The result was a golang struct with the following methods:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XC9esNUr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/890u073f0yldc055pbsy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XC9esNUr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/890u073f0yldc055pbsy.png" alt="client" width="615" height="526"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Use the client
&lt;/h3&gt;

&lt;p&gt;After generating the client, I just had to write some code which:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;retrieves the number of reactions and comments for the article&lt;/li&gt;
&lt;li&gt;updates the title if the number of reactions or comments changed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second step require a little bit more work than the first one. &lt;br&gt;
Since, I use front matter when writing on dev.to, updating the title means updating the &lt;em&gt;title&lt;/em&gt; field in markdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="s"&gt;title:This article has x reactions and y comments&lt;/span&gt;
&lt;span class="na"&gt;published&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gist of the program is here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;programEnv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ClientWithResponses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getArticleActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArticleID&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;e&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debugf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Article reactions: %[1]d - Article comments: %[2]d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReactionsCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommentsCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;newTitle&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;generateNewTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReactionsCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommentsCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmEditor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;newFrontMatterEditor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BodyMarkdown&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;e&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&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="n"&gt;fmEditor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;newTitle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Article title is already up to date with the number of reactions and comments"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Updating the title of the article"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmEditor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updateTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newTitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;saveArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArticleID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmEditor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markdown&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;e&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Article with the new title has been saved"&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;You can find the rest of the source code is on &lt;a href="https://github.com/napicella/this-article-has-x-reactions-and-y-comments"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Profit
&lt;/h3&gt;

&lt;p&gt;Lastly, I create a cron job which runs the program every minute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHELL=/bin/bash
* * * * * /home/napicella/wrapper.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;em&gt;wrapper.sh&lt;/em&gt; is a bash script which calls the program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;source&lt;/span&gt; .env
./update-article &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /home/napicella/ua.log 2&amp;gt;&amp;amp;1 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dev.to documentation does not mention any service quota for the api. I did not get throttled so far, so I guess it's a reasonable rate for the service.&lt;/p&gt;

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

&lt;p&gt;I want to conclude by thanking Tom Scott for the idea. If you haven't seen it, this is the link to the &lt;a href="https://www.youtube.com/watch?v=BxV14h0kFs0"&gt;video&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/napicellatwit"&gt;Twitter&lt;/a&gt; to get new posts in your feed.&lt;br&gt;
Credit for the cover image to &lt;a href="https://pixabay.com/users/graphicmama-team-2641041/"&gt;GraphicMama-team&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>meta</category>
      <category>showdev</category>
      <category>go</category>
    </item>
    <item>
      <title>Test Driven Development by Example</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Sun, 10 May 2020 18:54:05 +0000</pubDate>
      <link>https://dev.to/napicella/test-driven-development-by-example-29g8</link>
      <guid>https://dev.to/napicella/test-driven-development-by-example-29g8</guid>
      <description>&lt;p&gt;Among the few positive aspects of the lock-down, having more time to read is definitely one of them. Two weeks ago I started reading again the Test Driven Development (TDD) bible written by &lt;a href="https://en.wikipedia.org/wiki/Kent_Beck" rel="noopener noreferrer"&gt;Kent Beck&lt;/a&gt;, who is considered by most the father of TDD. Regardless of what your thoughts are about TDD, the book is a gold mine on testing. I highly recommend it.&lt;/p&gt;

&lt;p&gt;In the same spirit of the book, this article is going to be a practical walk through on how to develop code driven completely by tests; an example from start to end on how to apply TDD. I am going to start with a brief recap on TDD, then I'll walk you through an example in which we are going to code a &lt;strong&gt;throttle&lt;/strong&gt; the TDD way. Last, I'll share some resources that can be used to practice TDD.&lt;/p&gt;

&lt;p&gt;The target audience for this post is people who are considering using TDD in the development process. If you have already delved into TDD or you are already using it, this post probably won't add any new information to your knowledge. However it might still be useful as a reference you can share with others curious about the topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;TDD is one of the software engineering practice which has stood the test of time. At the beginning of 2000s Kent Beck came out with the book "&lt;a href="https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530" rel="noopener noreferrer"&gt;Test Driven Development: By Example&lt;/a&gt;". The book is twenty years old, though TDD as a concept it's probably older than that. It was Kent Beck himself to say that he did not "invent" TDD, but rather "rediscover" it from old articles and papers. &lt;a href="https://dl.acm.org/doi/pdf/10.1145/355604.361591?download=true" rel="noopener noreferrer"&gt;The humble programmer, Dijkstra (1972)&lt;/a&gt; and the &lt;a href="http://homepages.cs.ncl.ac.uk/brian.randell/NATO/nato1968.PDF" rel="noopener noreferrer"&gt;Report of The Nato Software Engineering Conference (1968)&lt;/a&gt; both described the process of testing the specification &lt;em&gt;before&lt;/em&gt; writing the code. While, Kent Beck might not have been the one who invented, he definitely was the one who made it popular.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a 20+ engineering practice still relevant today?
&lt;/h3&gt;

&lt;p&gt;Everything we do is built upon layer of abstractions and decisions made decades ago. People who made those decisions lived in a different context, had different constraints and problems to solve. What they did, is what we do today: they came up with the best solution they could think of at the time.&lt;br&gt;
Their decisions live with us. But most often, their reasons do not. &lt;br&gt;
Technology changed, the problems we need need to solve changed, the world has changed. &lt;/p&gt;

&lt;p&gt;As software engineer one of the most valuable skills I picked up is to question everything, understand why things are the way they are. Searching for the context in which these decisions were made is the key to understand if the same decisions are applicable in the current world.&lt;/p&gt;

&lt;p&gt;So, is TDD still relevant today? I think it is, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we still need to write unit tests to prove that our code respect the specification&lt;/li&gt;
&lt;li&gt;we still want to reduce the number of bugs which make it all the way to production &lt;/li&gt;
&lt;li&gt;we still want to iterate fast and integrate changes often&lt;/li&gt;
&lt;li&gt;we still want to build highly cohesive and loosely coupled components &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I believe the premises of TDD are still valid in the context we live in. &lt;/p&gt;
&lt;h3&gt;
  
  
  TDD is controversial
&lt;/h3&gt;

&lt;p&gt;Not everybody thinks TDD is useful. I couldn't agree more - not everybody must use it. During the years, a few research studies were made to determine the effectiveness of TDD in the software development process, but they were largely inconclusive. I think that's because quantitative measurements about source code quality and speed of iterations are too noisy and dependent on social factors - all things which are difficult to take into account in a research study. &lt;/p&gt;

&lt;p&gt;I want to conclude this quite lengthy preface by saying that I am not religious about TDD - and I hope neither will you. It's like any other tool we have in our toolbox - it allows to see the problem from a different point of view.&lt;/p&gt;
&lt;h2&gt;
  
  
  TDD
&lt;/h2&gt;

&lt;p&gt;TDD is a predictable way to develop code which relies on the following three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;RED&lt;/strong&gt; - Write a unit test, run it an watch it fail. 
The unit test should be short and focus on a single behavior of the system under test. By writing the failing test you ensure that your test is calling the correct code and that the code is not working by accident. It's a meaningful failure, and you expect it to fail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GREEN&lt;/strong&gt; - Write the minimum amount of code needed to make the test pass&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REFACTOR&lt;/strong&gt; - Eliminate the duplication (both in the test and in the code, including duplication between test and code). More in general this is the step in which you would perform refactoring&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhcwwu30olhngky9pvp7d.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhcwwu30olhngky9pvp7d.PNG" alt="TDD cycle" width="468" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There isn't much else you need to know to start using TDD. Using it effectively  is just a matter of practicing it over and over again. Project after project you become better at it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why TDD?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;you are always one test away from functional code&lt;/li&gt;
&lt;li&gt;tests are more expressive; the result is usually tests that cover the behavior of the module instead of the underlying implementation&lt;/li&gt;
&lt;li&gt;increased test coverage and reduced coupling between test and production code&lt;/li&gt;
&lt;li&gt;it's very useful when you know what you have to build, but have no idea where to start; a situation quite common when you need to add or change a new feature in a piece of the code base you are not familiar with&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Throttling  example
&lt;/h2&gt;

&lt;p&gt;In this section we will build a &lt;em&gt;throttle&lt;/em&gt;. The end goal of throttling is to limit how many times a function can be called in a given interval of time. It's generally used to avoid overloading the receiver with too many calls (for example a remote server) or because a sample of the events is sufficient to carry on with the functionality.   &lt;/p&gt;

&lt;p&gt;To sum it up &lt;em&gt;to throttle a function&lt;/em&gt; means to ensure that the function is called at most &lt;em&gt;X&lt;/em&gt; times in a specified time period (for instance, at most three times every second). The throttle we are going to build, is a slightly simpler version which only allows at most &lt;em&gt;one&lt;/em&gt; call in a specified period. This is the spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;throttle returns a function which is called at most once in a specified time period. 
It takes as input the function to throttle and the period. 
If the period is less or equal than zero, then no throttle is applied.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try to build it. Since we are using TDD, this means writing our test first.&lt;/p&gt;

&lt;h4&gt;
  
  
  First test
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Given the throttle time is 0&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runs the function when we call it&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; 
                &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the test, we defined a simple function called &lt;em&gt;fun&lt;/em&gt; which simply increments a variable called &lt;em&gt;count&lt;/em&gt; every time we invoke the function. We call our &lt;em&gt;throttle&lt;/em&gt; function giving it as parameter the function we just defined and a throttle period of zero. According to the specification, if the throttle period is zero, the function must be invoked when we call it. We called &lt;em&gt;funT&lt;/em&gt; (as in fun Throttled) the result of applying &lt;em&gt;throttle&lt;/em&gt; to &lt;em&gt;fun&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Run the test and watch it fail. Now, we have to make it pass by writing the minimum amount of code necessary. So. let's create the &lt;em&gt;throttle&lt;/em&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &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="err"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;fun&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;throttle&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the test again, and it's green! To make the test green, we just had to create the &lt;em&gt;throttle&lt;/em&gt; function and make it invoke &lt;em&gt;fun&lt;/em&gt;. At this point there is nothing to refactor, so we'll move the next test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second test
&lt;/h3&gt;

&lt;p&gt;According to the spec, if the throttle period is zero, the function must be invoked "every" time we call it because no throttle is applied. Let's test that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Given the throttle time is 0&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runs the function 'every' time we call it&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; 
                &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nx"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;    
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of calling &lt;em&gt;funT&lt;/em&gt; once like in the previous test, now we are calling it ten times and we expect the &lt;em&gt;count&lt;/em&gt; variable to be ten at the end.&lt;/p&gt;

&lt;p&gt;Run the tests and...it's green. We did not even have to add any code for it, good. Before we go ahead with the next test, we are going to refactor: the second test includes the first one so we can remove it, which leaves us with the following suite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;throttle suite&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Given the throttle period is 0&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runs the function 'every' time we call it&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; 
                &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;    
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Third test
&lt;/h4&gt;

&lt;p&gt;Let's add another test when the throttle period is negative:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Given the throttle period is negative&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runs the function 'every' time we call it&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt;
                &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;    
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, it passes and we did not have to add any code. We can refactor since the test for the negative period and the zero period are very similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;throttle suite&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runFun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;throttlePeriod&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;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Runs the function 'every' time we call it&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; 
                &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttlePeriod&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;    
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;calls&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Given the throttle period is 0&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="nf"&gt;runFun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Given the throttle period is negative&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="nf"&gt;runFun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&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;h4&gt;
  
  
  Fourth test
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Given the throttle period is positive&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When the throttle period has not passed&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Then `fun` is not called&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt;
                    &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the test and watch it fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failures:
1) throttle suite 

   Given the throttle period is positive 
   When the throttle period has not passed 
   Then `fun` is not called
     Message:
       Expected 2 to be 1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's happening here? We expect the first call to &lt;em&gt;funT&lt;/em&gt; to go through because the throttle does not apply to the first call. Thus in the first expectation we check if the variable &lt;em&gt;count&lt;/em&gt; is equal to one. The second time we call &lt;em&gt;funtT&lt;/em&gt; must be throttled because at least one minute needs to pass between the first and the second call; that's why we expect &lt;em&gt;count&lt;/em&gt; still to be one in the second expectation. Except it isn't. The &lt;em&gt;count&lt;/em&gt; variable is two because we haven't implement any throttling logic yet. &lt;/p&gt;

&lt;p&gt;What's the smallest step to make the test pass? What I have come up with is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;check if it's the first time we are calling the function &lt;/li&gt;
&lt;li&gt;differentiate between a positive throttle period and a less than zero period
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;firstInvocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return &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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;throttleTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstInvocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;firstInvocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The introduction of &lt;code&gt;firstInvocation&lt;/code&gt; and the &lt;code&gt;if statement&lt;/code&gt; was enough to make the test pass.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fifth test
&lt;/h4&gt;

&lt;p&gt;Next one is interesting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;        &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When the throttle period has passed&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Then `fun` is called&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt;
                    &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="c1"&gt;// 1 minute later ...&lt;/span&gt;
                &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this test we want to verify that after one minute has passed, the function won't be throttled. But how do we model time? We need to have something that allows to keep track of time, like a timer or something similar. More importantly, we need to manipulate the state of the timer in the test. Let's assume we already have what we need and change the test accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;        &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When the throttle period has passed&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Then `fun` is called&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt;
                    &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="c1"&gt;// fast forward 1 minute in the future&lt;/span&gt;
                &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
                &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diff between this version of the test and the previous is the introduction of the &lt;em&gt;MockTimer&lt;/em&gt;. It's initialized with the rest of the variables at the beginning of the test. Right after the first expectation the timer &lt;em&gt;tick&lt;/em&gt; method is called to move the timer one minute in the future. Since the throttle timeout is one minute, we expect the next call to &lt;em&gt;funT()&lt;/em&gt; to go through. &lt;/p&gt;

&lt;p&gt;Let's run the test. Not surprisingly, it fails because the MockTimer does not exist. We need to create it. &lt;/p&gt;

&lt;p&gt;Before doing that, let's figure how we would use the timer in the throttle function. You can come up with different ways of using it. In my case I decided I needed to have a way to start the timer and check if it is expired or not. With that in mind let's change the &lt;em&gt;throttle&lt;/em&gt; function to make use of a Timer that does not exist yet. Using a function before implementing it seems stupid, but in fact it's quite useful because you get to see the usability of the api before writing the code for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;firstInvocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
    &lt;span class="k"&gt;return &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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;throttleTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;return&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstInvocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;firstInvocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isExpired&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Established the api, let's implement a mock timer for our test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MockTimer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ticks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numberOfTicks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ticks&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;numberOfTicks&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;numberOfTicks&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;isExpired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ticks&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the test again, and boom, tests are green!&lt;/p&gt;

&lt;p&gt;Let's change our test and make it richer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;When the throttle period has passed&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Then `fun` is called&lt;/span&gt;&lt;span class="dl"&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;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt;
            &lt;span class="nx"&gt;fun&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;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;funT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&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;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;funT&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we just need to plug in an actual timer which we could build with a similar process, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;isExpired&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expired&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeout&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="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;running&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timer is already running&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;setTimeout&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Tidying up the API
&lt;/h4&gt;

&lt;p&gt;There is one last thing. We can create a default timer instead of requiring the caller to pass it as a parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;throttleWithTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;throttleWithTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttleTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// ... same as before&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we can use our throttle function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onClickSendEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Practice TDD
&lt;/h2&gt;

&lt;p&gt;If you like the idea of writing your test first, then give TDD a try. In this article I showed the &lt;em&gt;throttle&lt;/em&gt; function, maybe you can try the &lt;em&gt;debounce&lt;/em&gt; by yourself. When I was thinking about the article I almost settled on using &lt;a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life" rel="noopener noreferrer"&gt;Conway's Game of Life&lt;/a&gt; as example, but it did not take me long to realize that the article would be too long. If you are up for it, it's a fun exercise to build with TDD.&lt;/p&gt;

&lt;p&gt;You could also try some of the programming &lt;a href="https://en.wikipedia.org/wiki/Kata_(programming)" rel="noopener noreferrer"&gt;Katas&lt;/a&gt; available online, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.codewars.com/" rel="noopener noreferrer"&gt;code wars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://codekata.com/" rel="noopener noreferrer"&gt;code kata&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://codingdojo.org/KataCatalogue/" rel="noopener noreferrer"&gt;coding dojo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Whatever you pick to flex your TDD muscle, my suggestion is to give it sometime. At least for me, TDD did not click right away. The first times I tried it, I got stuck - I could not figure how to write the test before the code. But I kept practicing on my own and ultimately it became natural to think about the test before thinking about the code.  &lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/napicellatwit" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to get new posts in your feed.&lt;br&gt;
Credit for the cover image to &lt;a href="https://pixabay.com/users/graphicmama-team-2641041/" rel="noopener noreferrer"&gt;GraphicMama-team&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How are docker images built? A look into the Linux overlay file-systems and the OCI specification</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Tue, 21 Apr 2020 06:40:10 +0000</pubDate>
      <link>https://dev.to/napicella/how-are-docker-images-built-a-look-into-the-linux-overlay-file-systems-and-the-oci-specification-175n</link>
      <guid>https://dev.to/napicella/how-are-docker-images-built-a-look-into-the-linux-overlay-file-systems-and-the-oci-specification-175n</guid>
      <description>&lt;p&gt;It's impossible to work with docker containers without  docker images. In this post I want to talk about what makes docker images possible: the &lt;strong&gt;overlay filesystems.&lt;/strong&gt; &lt;br&gt;
I'll start with a brief description of overlay filesystems. Then we will see how it applies to docker images and how docker builds an image from a dockerfile. I'll conclude with layers cache and OCI format for container images.&lt;/p&gt;

&lt;p&gt;As usual I'll try to make the blog post as practical as possible.&lt;/p&gt;
&lt;h3&gt;
  
  
  What's an overlay filesystems
&lt;/h3&gt;

&lt;p&gt;Overlay filesystems (also called union filesystems) allow creating a union of two or more directories: a list of &lt;em&gt;lower&lt;/em&gt; directories and an &lt;em&gt;upper&lt;/em&gt; directory. The lower directories of the filesystem are read only, whereas the upper directory can be used for both reads and writes. &lt;/p&gt;

&lt;p&gt;Let's see what that means in practice by mounting one.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create an overlay fs
&lt;/h3&gt;

&lt;p&gt;Let's create a few folders and combine them. &lt;br&gt;
First I'll create a folder called "mount" which will contain the union of all the other folders. Then I'll create a bunch of folders called "layer-1", "layer-2", "layer-3", "layer-4". Finally a folder called "workdir" which is needed by the overlay filesystem, well, to work properly.&lt;/p&gt;

&lt;p&gt;You can call any of the folders as you wish, but calling them "layer-1", "layer-2", etc. will make easier to understand the parallel with docker images as we shall see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;overlay-example &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;overlay-example

&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:02:35] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;mount layer-1 layer-2 layer-3 layer-4 workdir

&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:02:38] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;ls
&lt;/span&gt;layer-1  layer-2  layer-3  layer-4 mount  workdir

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also create some files into layer-1, layer-2 and layer-3 folders.&lt;br&gt;
We will leave the layer-4 (our upper folder) empty. Again, that's not necessary, it will just make easier our parallel with docker images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:02:40] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Layer-1 file"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./layer-1/some-file-in-layer-1

&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:03:36] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Layer-2 file"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./layer-2/some-file-in-layer-2

&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:03:53] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Layer-3 file"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./layer-3/some-file-in-layer-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's mount the filesystem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; overlay overlay-example &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;lowerdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/overlay-example/layer-1:/tmp/overlay-example/layer-2:/tmp/overlay-example/layer-3,upperdir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/overlay-example/layer-4,workdir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/overlay-example/workdir &lt;span class="se"&gt;\&lt;/span&gt;
/tmp/overlay-example/mount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's look inside the mount folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:13:28] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;mount/

&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:13:31] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example/mount]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
total 20
drwxr-xr-x 1 napicell domain^users 4096 Apr 19 16:07 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-xr-x 8 napicell domain^users 4096 Apr 19 16:07 ..
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 napicell domain^users   13 Apr 19 16:03 some-file-in-layer-1
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 napicell domain^users   13 Apr 19 16:03 some-file-in-layer-2
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 napicell domain^users   13 Apr 19 16:03 some-file-in-layer-3

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As expected the content of the folders layer-1, layer-2 and layer-3 have been mounted/combined in the mount folder.&lt;br&gt;
Sure enough if we look at the content of the files, we'll find what we have written in the previous step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:13:33] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example/mount]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cat &lt;/span&gt;some-file-in-layer-3
Layer-3 file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let' try to create a file in the mount folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:23:31] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example/mount]  
 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"new content"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; new-file

&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:27:33] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example/mount]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;ls
&lt;/span&gt;new-file  some-file-in-layer-1  some-file-in-layer-2  some-file-in-layer-3

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where should the new file be? In the upper layer, which in our case is the folder called "layer-4":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:23:49] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tree
&lt;span class="nb"&gt;.&lt;/span&gt;
├── layer-1
│   └── some-file-in-layer-1
├── layer-2
│   └── some-file-in-layer-2
├── layer-3
│   └── some-file-in-layer-3
├── layer-4
│   └── new-file
├── mount
│   ├── new-file
│   ├── some-file-in-layer-1
│   ├── some-file-in-layer-2
│   └── some-file-in-layer-3
└── workdir
    └── work &lt;span class="o"&gt;[&lt;/span&gt;error opening &lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

7 directories, 8 files

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try to delete a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:27:33] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example/mount]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;some-file-in-layer-2

&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:28:58] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example/mount]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;ls
&lt;/span&gt;new-file  some-file-in-layer-1  some-file-in-layer-3

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What do you think happened to the original file in the "layer-2" folder?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:29:57] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tree
&lt;span class="nb"&gt;.&lt;/span&gt;
├── layer-1
│   └── some-file-in-layer-1
├── layer-2
│   └── some-file-in-layer-2
├── layer-3
│   └── some-file-in-layer-3
├── layer-4
│   ├── new-file
│   └── some-file-in-layer-2
├── mount
│   ├── new-file
│   ├── some-file-in-layer-1
│   └── some-file-in-layer-3
└── workdir
    └── work &lt;span class="o"&gt;[&lt;/span&gt;error opening &lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

7 directories, 8 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new file called "some-file-in-layer-2" was created in "the layer-4". The weird thing is that the file is a character file. These kinds of files are called "whiteout" files and are how the overlay filesystem represents a file being deleted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:31:09] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example/layer-4]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
total 12
drwxr-xr-x 2 napicell domain^users 4096 Apr 19 16:28 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-xr-x 8 napicell domain^users 4096 Apr 19 16:07 ..
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 napicell domain^users   12 Apr 19 16:23 new-file
c--------- 1 root     root         0, 0 Apr 19 16:28 some-file-in-layer-2

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have finished with it, let's unmount the filesystem and remove the folder we created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;2020-04-19 16:37:11] &lt;span class="o"&gt;[&lt;/span&gt;ubuntu] &lt;span class="o"&gt;[&lt;/span&gt;/tmp/overlay-example]  
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;umount /tmp/overlay-example/mount &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/overlay-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wrapping up the overlay filesystems
&lt;/h3&gt;

&lt;p&gt;As we said at the beginning, the overlay filesystem allows to create a union of directories. In our case the union was created in the "mount" folder and it was the result of combining the "layer-{1, 2, 3, 4}" folders. Changes to files, deletion or creation will be stored in the upper dir, which in our case is "layer-4". This is why this layer is also called "diff" layer.&lt;br&gt;
Files from upper layer shadow the ones in lower layers, i.e. if you have a file with the same name and relative path in layer-1 and layer-2, the layer-2 file is going to end up in the "mount" folder.&lt;/p&gt;

&lt;p&gt;In the next section we will see how this is used with docker images.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's a docker Image?
&lt;/h2&gt;

&lt;p&gt;A docker image is essentially a tar file with a root file system and some metadata. You might have heard of the expression image layer and that every line in a docker file creates a new layer. For example in the following snippet we will end up with an image with three layers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; my-files /doc&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; hello /&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/hello"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what happens when you type "docker run". A lot of things really, but for the purpose of this article we are only interested in the bits concerning the image. &lt;br&gt;
At high level, docker downloads the tarballs for the image, it unpacks each layer into a separate directory and then tells the overlay filesystem to combine them all together together with an empty upper directory that the container will write its changes to it. &lt;br&gt;
When you change, create or delete files in the container, the changes are going to be stored in this empty directory. When the container exits, docker cleans up the folder - that is why the changes you make in the container do not persist.&lt;/p&gt;
&lt;h3&gt;
  
  
  Layers cache
&lt;/h3&gt;

&lt;p&gt;This way to use the overlay filesystem allows hosts to cache docker images effectively.  For example, if you define two images, they can both use the same layers. No need to download multiple times or to have many copies on the disk!&lt;/p&gt;
&lt;h3&gt;
  
  
  OCI-format &lt;em&gt;container images&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Running a container at high level can be seen as a two steps process: building the image and running a container from the image. The popularity of docker has convinced people to standardize both steps -  allowing the two pieces to evolve separately. The Open Container Initiative (OCI) is the governance which has been working with the industry to these standards. &lt;/p&gt;

&lt;p&gt;The OCI currently contains two specifications: the Runtime Specification (runtime-spec) and the Image Specification (image-spec). The Runtime Specification outlines how to run a “filesystem bundle” that is unpacked on disk. At a high-level an OCI implementation would download an OCI Image then unpack that image into an OCI Runtime filesystem bundle. At this point the OCI Runtime Bundle would be run by an OCI Runtime.&lt;/p&gt;

&lt;p&gt;The standardization allows other people to develop custom container builders and runtimes. For example, jessfraz/img, buildah and Skopeo are all tools that allow you to build container images without using docker. Similarly, many tools to run containers (so called container runtimes) have emerged, for example runc (used by docker) and rkt.&lt;/p&gt;
&lt;h3&gt;
  
  
  Other overlay filesystems
&lt;/h3&gt;

&lt;p&gt;Overlay is not the only union file system that docker can use. Any file system that allows union like features and diff layer could potentially be used. For example docker can use overlay as we have seen, but also aufs, btrfs, zfs and devicemapper. &lt;/p&gt;
&lt;h3&gt;
  
  
  What happens when you build an image?
&lt;/h3&gt;

&lt;p&gt;Let's assume we have the following dockerfile we want to use to build an image from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At high level, this is how docker builds an image out of it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Docker downloads the tarball for the image specified in the "FROM" and unpacks it. This is the first layer of the image.&lt;/li&gt;
&lt;li&gt;Mounts a union file system, with the lower dir being the one just downloaded. The upper dir is an empty folder&lt;/li&gt;
&lt;li&gt;Starts bash in a chroot and runs the command specified in RUN: chroot . /bin/bash -c "apt get update"&lt;/li&gt;
&lt;li&gt;When the command is over, it zips the upper layer. This is the new layer of the image we are building&lt;/li&gt;
&lt;li&gt;If the dockerfile contains other commands, repeat the process from the second step using as lower dir all the layers we have got so far. Otherwise exit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of course this is a simplified workflow which does not take into account different type of commands like "ENV", "ENTRYPOINT", etc. Those things are stored in the metadatafile which is going to be bundled together with the layers.&lt;/p&gt;

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

&lt;p&gt;The idea of zipping a whole root file system in a tar and keeping a tar for each diff-layer turned out to be very powerful. It did not just enabled docker, but turns out to be a concept that can be used in other context as well. I guess, we will see more tools taking advantage of that in the future.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/napicellatwit"&gt;Twitter&lt;/a&gt; to get new posts in your feed.&lt;br&gt;
Credit for the cover image to &lt;a href="https://unsplash.com/@frankiefoto?utm_medium=referral&amp;amp;utm_campaign=photographer-credit&amp;amp;utm_content=creditBadge" rel="noopener noreferrer" title="Download free do whatever you want high-resolution photos from frank mckenna"&gt;&lt;span&gt;unsplash-logo&lt;/span&gt;&lt;span&gt;frank mckenna&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>linux</category>
      <category>computerscience</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AWS Networking cheat-sheet - EIP, ENI, VPC, etc</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Fri, 17 Apr 2020 12:52:39 +0000</pubDate>
      <link>https://dev.to/napicella/aws-networking-cheat-sheet-eip-eni-vpc-etc-139m</link>
      <guid>https://dev.to/napicella/aws-networking-cheat-sheet-eip-eni-vpc-etc-139m</guid>
      <description>&lt;p&gt;AWS network infrastructure can become complex. &lt;br&gt;
There is also some overlaps between different technologies which makes it harder to understand what does what.&lt;/p&gt;

&lt;p&gt;Some of the questions I hear the most are about the intersection between AWS VPC, Elastic Network interfaces, Elastic IP, and the closely related Security Group and ACL. This is my attempt to clarify in practical terms the difference between these technologies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are eager to look at the cheat-sheet, scroll down to the bottom.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS VPC
&lt;/h2&gt;

&lt;h3&gt;
  
  
  History - VPC was an afterthought
&lt;/h3&gt;

&lt;p&gt;In the early days of AWS, when EC2 lunched all the instances from all the customers could communicate with each other.  If Alice launched an instance in her account and Bob launched another instance is his account, the two instances were able to communicate with each other out of the box. &lt;br&gt;
To prevent that, Alice (or Bob) were required to set a security group, allowing only traffic from selected ips or from instances launched with the same security group. &lt;/p&gt;

&lt;p&gt;The problem with security group was that was very easy to mess it up, thus allowing other AWS accounts to breach the instances. This mode of operations is called EC2-classic to distinguish it from the new way  in which EC2 instances are provisioned. &lt;/p&gt;

&lt;p&gt;In this new mode, EC2 instances are always launched in a VPC (called default VPC) which has a default security group. Instances in different VPCs do not communicate with each other, unless they are peered. &lt;br&gt;
Of course you can also create your own VPC and launch instances in there.&lt;/p&gt;

&lt;p&gt;Today EC2-classic only exist for legacy reasons which tie into the challenge of taking back a service once that has been launched.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's a VPC?
&lt;/h3&gt;

&lt;p&gt;Having covered the history, what's a VPC? &lt;strong&gt;It's an isolated portion of the AWS cloud&lt;/strong&gt;. Instances launched in a VPC cannot be accessed from other VPCs in the same account or other AWS accounts.&lt;br&gt;
It's main property is the CIDR block, which is the range of IPv4 addresses assigned to instances launched in the VPC.&lt;br&gt;
Leaving aside Security group and ACL (which are discussed later), &lt;strong&gt;all the instances created in the same VPC can communicate with each other, like in  a private LAN&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After creating a VPC, you can add additional subnets by dividing the range of IP addresses assigned to the VPC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security group
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's a Security Group?
&lt;/h3&gt;

&lt;p&gt;A &lt;em&gt;security group&lt;/em&gt; acts as a virtual firewall that controls the traffic from/to a network interface. &lt;br&gt;
When you launch an instance, you get a default network interface (eth0).&lt;br&gt;
An EC2 instance can have multiple network interfaces and &lt;strong&gt;each network interface can have a different security group&lt;/strong&gt;.&lt;br&gt;
Later I'll discuss how to attach additional network interfaces to an instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb888o6cvdolifqjncpjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb888o6cvdolifqjncpjq.png" alt="multiple network interfaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Confusing terminology
&lt;/h3&gt;

&lt;p&gt;Security Groups are attached to a network interface, not an instance. When you start an instance, it receives a default network interface (eth0). The security group will be attached to that default network interface.&lt;br&gt;
Don't get fooled, every time you specify a security group for an AWS service, behind there is a network interface.&lt;br&gt;
Let's take for example AWS Elastic File System. When you create a File System, you are going to specify security groups. Does that mean that the security group control the access to the File System? Well, in some sense yes.  But technically the security group are going to be attached to a Elastic Network Interface (ENI) that EFS creates on your behalf.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I am going to discuss about ENIs later, the takeaway here is:  Security groups are associated to Network Interfaces (Elastic or not) and they act as a virtual firewall that controls the traffic from/to a network interfaces.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Even more confusing
&lt;/h3&gt;

&lt;p&gt;Subnets in the VPC can have a default security group. Does this mean that security groups can be attached to network interfaces as well as subnets? Mmm, not really. By defining a default security group for a subnet, you have informed EC2 that the next time you launch an instance in that subnet, it needs to attach that security group to the network interface of the instance. That is, unless you override the security group in the launch instance api call.&lt;/p&gt;

&lt;p&gt;Subnets also feature a property called: "auto-assign public IPv4 address" which means that new network interfaces in that subnet automatically receives also a public IPv4 or IPv6 address (in addition to the private IP address). As for security groups, this setting defines the default value that EC2 is going to use when launching an instance in that subnet. For example, let's assume we start an instance with the following command:&lt;/p&gt;

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

aws ec2 run-instances \
  --subnet-id subnet-10000000 \
  --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The instance launched will have an auto assigned ip address if &lt;code&gt;subnet-10000000&lt;/code&gt; has the "auto-assign public IPv4 address" setting set to true.  But, like I mentioned this is just a default and we can override this behavior by using the &lt;code&gt;--associate-public-ip-address&lt;/code&gt; or the &lt;code&gt;--no-associate-public-ip-address&lt;/code&gt; flags. For example if we run the following command:&lt;/p&gt;

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

aws ec2 run-instances \
  --subnet-id subnet-10000000 \
  --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 \
  --associate-public-ip-address 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;the instance will get a public IP regardless of whether we have set the "auto-assign public IPv4 address" on the subnet &lt;code&gt;subnet-10000000&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subnets Isolation
&lt;/h2&gt;

&lt;p&gt;After creating your VPC, you divide it into subnets. In an AWS VPC, subnets are not isolation boundaries. Rather, they are containers for routing policies. &lt;strong&gt;Isolation between subnets is achieved by attaching a Security Group to the EC2 instances.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example let's assume we have two subnets in the VPC: SubnetA is 10.0.0.0/25 and SubnetB is 10.0.0.128/25. To isolate them, create a security group for SGSubnetA which allows all traffic for the CIDR 10.0.0.0/25 and another SGSubnetB that allows traffic for the CIDR 10.0.0.128/25.&lt;br&gt;
Attach the SGSubnetA to the instances launched in SubnetA and SGSubnetB for the one in SubnetB.&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: Only the instances in the subnet can talk to each other, but instances from SubnetA cannot communicate with instances from SubnetB and vice versa.&lt;/p&gt;

&lt;h2&gt;
  
  
  VPC ACL
&lt;/h2&gt;

&lt;p&gt;A network access control list (ACL) is a network firewall. &lt;strong&gt;With security groups you can control what goes in and out your instances, and with VPC ACL you can control what goes in and out of your VPC.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  EIP
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;An Elastic IP address (EIP) is a static IPv4 address provided by AWS.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You associate an EIP to a network interface. The documentation is a bit confusing on this point because it says you can attach an EIP to a running instance or to ENI. What it does not say is that by attaching the EIP to the instance, you are actually attaching it to the default network interface of the instance (eth0). &lt;/p&gt;

&lt;p&gt;You can attach additional network interfaces to an instance, by attaching to it additional ENIs. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why do you even need an EIP? Are they different from public IPs?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Public IP addresses are dynamic, their value is lost once the EC2 instance using it is stopped or terminated.&lt;br&gt;&lt;br&gt;
Elastic IPs are static, they survive instance stop/start cycles.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ENI
&lt;/h2&gt;

&lt;p&gt;An Elastic Network interface (ENI) is the network stack of the instance.&lt;br&gt;
Each ENI lives within a particular subnet of the VPC and has the following attributes (not exhaustive list):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private IP Address&lt;/li&gt;
&lt;li&gt;Public IP Address&lt;/li&gt;
&lt;li&gt;Elastic IP Address&lt;/li&gt;
&lt;li&gt;Subnet&lt;/li&gt;
&lt;li&gt;MAC address&lt;/li&gt;
&lt;li&gt;Security Group(s)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A very important consequence of this new model is that the idea of launching an EC2 instance in a particular VPC subnet is effectively obsolete. A single EC2 instance can be attached to two ENIs, each one on a distinct subnet. The ENI (not the instance) is now associated with a subnet. &lt;/p&gt;

&lt;p&gt;This enable two powerful use cases:&lt;br&gt;
Assuming we have created two EC2 instances (Instance-A and Instance-B), an ENI and a public EIP. The EIP is attached to the ENI and the ENI is attached to instance-A. Both EC2 instances run a Web Server.&lt;/p&gt;

&lt;p&gt;Let's visualize that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frgduorqe5dgzr9tztzjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frgduorqe5dgzr9tztzjq.png" alt="EC2 Instance A"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the image, the Client sends an http request to the EIP. EC2-A will respond with an html page. Now if we move the ENI from EC2-A to EC2-B (detach from EC2-A and attach to EC2-B), the next request coming from the client will be server by EC2-B.&lt;br&gt;
This is a fundamental building block which allows to build Primary-Failover strategy.&lt;br&gt;
Note: I do not want to muddy the water, so I won’t go into other Primary-Failover solutions. Just for you to know, ENI switching is just one of the possible solution to build Primary-Failover mechanic in a system - Load Balancers or consensus groups are other options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsmeb70759zer94jigep5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsmeb70759zer94jigep5.png" alt="EC2 Instance B"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use it to build dual-homed environment for your web, application, and database servers. The instance’s first ENI would be attached to a public subnet, routing 0.0.0.0/0 (all traffic) to the VPC’s Internet Gateway. The instance’s second ENI would be attached to a private subnet, with 0.0.0.0 routed to the VPN Gateway connected to your corporate network. You would use the private network for SSH access, management, logging, and so forth. You can apply different security groups to each ENI so that traffic port 80 is allowed through the first ENI, and traffic from the private subnet on port 22 is allowed through the second ENI. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: When creating a subnet, you can specify some default options that are going to be applied to all the ENIs that are associated to that subnet. Most notably, you can choose to give a public ip to all the ENIs associated to that subnet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Internet Gateway
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;VPC component that allows communication between instances in your VPC and the Internet.&lt;/strong&gt;&lt;br&gt;
For your instances to connect to the Internet or be reached from the Internet, the VPC needs to have a Internet Gateway. Bear in mind that is a necessary component, but it's not sufficient, which means you also need something else to enable internet connectivity. This is discussed in the sections &lt;em&gt;"Connect from EC2 to the Internet"&lt;/em&gt; and  &lt;em&gt;"Connect from Internet to EC2"&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route Table
&lt;/h2&gt;

&lt;p&gt;A route table defines how the traffic flow from one subnet to another.&lt;br&gt;
A Route table is created automatically when you create a VPC and contains only one rule which allows all the instances in the VPC to communicate between each other (the rule is called "from subnet CIDR to local").&lt;br&gt;
The rule cannot be modified or deleted, which means that instances in the same VPC can always communicate with each other and you can't restrict that using a Route Table. You need to use security groups for that: see the "&lt;em&gt;Subnets Isolation&lt;/em&gt;" section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Private VS Public subnet
&lt;/h2&gt;

&lt;p&gt;Private and public are an emergent property of a subnet, which means a subnet becomes public or private depending on how the subnet is setup. They identify a concept, not actually entities in the AWS console.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Public Subnet&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;If a subnet's traffic is routed to an Internet gateway, the subnet is known as a public subnet.&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Private Subnet&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;If a subnet doesn't have a route to the Internet gateway, the subnet is known as a private subnet.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect from EC2 to the Internet
&lt;/h2&gt;

&lt;p&gt;To connect to the Internet, you have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public Subnet&lt;/strong&gt;. Create an Internet Gateway in the VPC AND assign a public address (or public EIP) to the EC2 machine network interface. This same configuration allows also traffic from the Internet to EC2. The Route table assigned to the network needs to be set to forward internet traffic (0.0.0.0/0) to the Internet Gateway&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Private Subnet&lt;/strong&gt;. Create two subnets (SubPublic and SubPrivate) and an Internet Gateway. Create a NAT Gateway in SubPublic. Create two Route Tables, one for SubPublic and one for SubPrivate. In the RouteTable for SubPublic, define a rule that forwards all the Internet traffic (0.0.0.0/0) to the Internet Gateway.  In the SubPrivate define a rule which forwards Internet traffic (0.0.0.0/0) to the NAT Gateway&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before I tell you which one I would recommend, let me digress a bit. &lt;br&gt;
If you have followed along you might be asking yourself some common questions.&lt;br&gt;
Regarding the first option, a common question is: why isn't  the Internet Gateway enough to connect to the Internet? Why does the EC2 instance need a public address?&lt;br&gt;
Well the answer is that the Internet Gateway does not perform a full NAT (like the router we have home), it only performs 1 to 1 NAT to public IPs that are mapped to instances. This means the calls cannot exit the Internet gateway with a private address because the gateway would not know how to map the answer to the current instance on the way back.&lt;br&gt;
&lt;strong&gt;Practically speaking, it does not do NAT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Regarding the second option, one question is why we  need two Subnets, two Route Tables, etc.&lt;br&gt;
One could imagine, having one subnet and create a NAT into it. Create an Internet Gateway for the VPC and that would be all. And the answer is that you can't do that and I am not really sure why. You need to have two subnets.&lt;br&gt;
Looks like a design decision AWS made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which approach should we choose?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the first approach (public subnet) the downside is that not only we have created a channel from EC2 instances to the Internet, but also the other way around. This means anyone on the Internet could try to access your EC2 instances.&lt;br&gt;
Technically there is nothing stopping you using this approach, after all you can use a security group that denies inbound connection. However by using a private subnet, even if you accidentally opened up the security group, there is no way for external access into that subnet from outside AWS directly, so its an extra layer of protection. &lt;br&gt;
&lt;strong&gt;This last point makes Private Subnet my default approach&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect from Internet to EC2
&lt;/h2&gt;

&lt;p&gt;To reach a machine from the Internet, you need an Internet Gateway and a Public IP (or a public EIP attached to the instance). This  is the Public Subnet approach we have seen in the previous section.&lt;/p&gt;

&lt;h2&gt;
  
  
  VPC Endpoints
&lt;/h2&gt;

&lt;p&gt;A VPC endpoint allows instances in a VPC to communicate to supported AWS services (S3, Dynamo, etc.)  without an Internet gateway or NAT gateway. Use case: The EC2 instances need to have access to other AWS services, but you do not want to allow any other outbound traffic to the Internet.&lt;br&gt;
You could obtain the same result by attaching a security group that only allows outbound connections to the AWS services, but that comes with the risk of  accidentally changing the security group in a way that allows access to the Internet. VPC endpoint solve this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get a Shell in EC2
&lt;/h2&gt;

&lt;p&gt;Forget SSH, use AWS SSM to connect to the instance. Or if you want to use SSH, use SSM over SSH.&lt;br&gt;
Managing loads of SSH keys or bastion host is something that will soon be forgotten.&lt;/p&gt;

&lt;p&gt;If the EC2 instances do not need to be customer facing (private subnet), consider using a VPC endpoint for SSM, which  &lt;strong&gt;allows to connect to instances while remain completely inaccessible from anyone on the Internet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS SSM comes preinstalled on Amazon Linux 2 instances - that is you can start using it out of the box to connect to your EC2 instances. Anyway, it's very easy to install SSM on the instance in the user data as part of the instance bootstrap.&lt;/p&gt;

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

&lt;p&gt;I have covered a big chuck of AWS Networking, but not all of it. Some things you might want to read next are: VPN, Transit gateways, VPC peering, Private Links, Route53 Private Hosted Zones, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cheat-sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AWS resource&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VPC&lt;/td&gt;
&lt;td&gt;An isolated portion of the AWS cloud network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security Group&lt;/td&gt;
&lt;td&gt;Security group are associated to Network Interfaces (Elastic or not) and they act as a virtual firewall that controls the traffic from/to a network interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subnets Isolation&lt;/td&gt;
&lt;td&gt;Subnets are not isolation boundaries. Rather, they are containers for routing policies. To isolate one subnet from another, attach a security group to the instances launched in each subnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACL&lt;/td&gt;
&lt;td&gt;ACL is a network firewall. With security groups you can control what goes in and out your instances, and with VPC ACL you can control what goes in and out of your VPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EIP&lt;/td&gt;
&lt;td&gt;A static IPv4 address (it can private or public)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ENI&lt;/td&gt;
&lt;td&gt;A network interface that can be attached to an EC2 instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internet Gateway&lt;/td&gt;
&lt;td&gt;A VPC component which allows instances in the VPC to connect to the Internet. It's necessary but not sufficient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public Subnet&lt;/td&gt;
&lt;td&gt;A subnet which traffic is routed to an Internet Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private Subnet&lt;/td&gt;
&lt;td&gt;A subnet that doesn't have a route to the Internet Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect from EC2 to the Internet&lt;/td&gt;
&lt;td&gt;To connect to the Internet, the EC2 instance needs to be either:&lt;br&gt;-  In a VPC with an Internet Gateway and have a Public address (or public EIP) attached to its network interface&lt;br&gt;-  In a VPC with an Internet Gateway and a NAT gateway. The Route Table needs to have a rule that forward traffic (from the EC2 instances and directed to Internet traffic) to the NAT gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect from Internet to EC2&lt;/td&gt;
&lt;td&gt;To connect from the Internet to an EC2 instance, you need to have a VPC with an Internet Gateway and have a Public address (or public EIP) attached to its network interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VPC Endpoints&lt;/td&gt;
&lt;td&gt;A VPC endpoint allows instances in a VPC to communicate to supported AWS services (S3, Dynamo, etc.)  without an Internet gateway or NAT gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get a Shell in EC2&lt;/td&gt;
&lt;td&gt;SSM or SSM + VPC Endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/napicellatwit" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to get new posts in your feed.&lt;br&gt;
Credit for the cover image to &lt;a href="https://pixabay.com/users/graphicmama-team-2641041/" rel="noopener noreferrer"&gt;GraphicMama-team&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>showdev</category>
      <category>development</category>
    </item>
    <item>
      <title>Interesting bits - 2</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Sun, 29 Mar 2020 18:38:37 +0000</pubDate>
      <link>https://dev.to/napicella/interesting-bits-2-45li</link>
      <guid>https://dev.to/napicella/interesting-bits-2-45li</guid>
      <description>&lt;p&gt;Hi everybody! &lt;br&gt;
Welcome to the second issue of &lt;em&gt;Interesting Bits&lt;/em&gt;, in which I filter through the Software Engineering related content to bring you news, tips, articles and papers  worth reading.&lt;/p&gt;

&lt;p&gt;In this issue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Awk basics&lt;/li&gt;
&lt;li&gt;a guide on how to improve technical writing&lt;/li&gt;
&lt;li&gt;consistency explained through a baseball game&lt;/li&gt;
&lt;li&gt;Docker insecure flag&lt;/li&gt;
&lt;li&gt;Prof. Feynman’s Messenger Series&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Awk basics
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://www.youtube.com/watch?v=jJ02kEETw70"&gt;garyexplains YouTube channel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The video covers the basics of Awk.&lt;br&gt;
Awk is a Linux tool and scripting language used for manipulating data.&lt;br&gt;
It's name comes from  Aho, Weinberger and Kernighan (yes, Brian Kernighan, the same of the C programming language), the authors of the language, which was started in 1977 while their working at the Bell Labs.&lt;/p&gt;

&lt;p&gt;In Awk a record is a space separated word in a line. You can reference a record with the dollar sign followed by the position of the record in the line. For example, $1 is the first record, $2 is the second, etc.&lt;/p&gt;

&lt;p&gt;For example, the following line prints the first and the second record from 'my-file' which match 'string-to-search' and in which the second record is greater than 1500.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/string-to-search/ &amp;amp;&amp;amp; $2&amp;gt; 1500 {print $1,$2/1024"K"}'&lt;/span&gt; my-file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at the video for a step to step walkthrough of the command.&lt;br&gt;
The host is very clear and the presentation makes the syntax of the command easy to remember.&lt;/p&gt;
&lt;h2&gt;
  
  
  Technical Writing
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://developers.google.com/tech-writing/one"&gt;Google developers blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you were searching for a guide on how to improve your tech writing skills, look no further. Some of the takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start the doc with the reason why you are writing&lt;/li&gt;
&lt;li&gt;describe the audience for which the document is written for&lt;/li&gt;
&lt;li&gt;write the outline of the doc at the top of the document&lt;/li&gt;
&lt;li&gt;do not use idioms specific for a certain country&lt;/li&gt;
&lt;li&gt;use sentences in the active form&lt;/li&gt;
&lt;li&gt;prefer point lists to comma separated list&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Consistency explained through a baseball game
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2011/10/ConsistencyAndBaseballReport.pdf"&gt;Microsoft research&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In distributed systems, a consistency model is a contract between the system and the developer who uses it. They are a powerful abstraction which helps to describe a system in terms of its observable properties.&lt;/p&gt;

&lt;p&gt;The paper takes a practical approach; it describes consistency models through a game of baseball! &lt;/p&gt;
&lt;h2&gt;
  
  
  Docker insecure flag
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://twitter.com/napicellatwit/status/1236329365824311297"&gt;twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One liner to remember that docker &lt;em&gt;unprivileged&lt;/em&gt; flag might be harmful.&lt;br&gt;
What about calling it docker &lt;em&gt;insecure&lt;/em&gt; flag instead?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; bash bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"cat /proc/partitions | xargs -I {} dd if=/dev/zero of={}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prof. Feynman’s Messenger Series
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://madhavanmalolan.github.io/feynman-messenger/ch01.html"&gt;madhavanmalolan.github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A piece of history: Prof. Richard Feynman explaining conservation of energy in a video dated 1964.&lt;br&gt;
In minute 23, the analogy he uses to explain energy is pure gold...I mean mind blowing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;...and this is all for this issue. Thanks for reading.&lt;/p&gt;

&lt;p&gt;Some of the content I write is too short for a post, but still interesting enough to share it as a tweet. Follow me on &lt;a href="https://twitter.com/napicellatwit"&gt;Twitter&lt;/a&gt; to get them in your Twitter feed!&lt;/p&gt;




&lt;p&gt;Credit for the cover image to &lt;a href="https://pixabay.com/users/graphicmama-team-2641041/"&gt;GraphicMama-team&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>linux</category>
      <category>showdev</category>
      <category>writing</category>
    </item>
    <item>
      <title>Interesting bits - 1</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Sun, 08 Mar 2020 13:08:20 +0000</pubDate>
      <link>https://dev.to/napicella/interesting-bits-5bia</link>
      <guid>https://dev.to/napicella/interesting-bits-5bia</guid>
      <description>&lt;p&gt;Hi everybody! &lt;br&gt;
Welcome to the first issue of &lt;em&gt;Interesting Bits&lt;/em&gt;, in which I filter through the Software Engineering related content to bring you news, tips, articles and papers  worth reading.&lt;/p&gt;

&lt;p&gt;In this issue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reducing Docker image size, scratch images and statically compiled binaries&lt;/li&gt;
&lt;li&gt;how to measure system availability&lt;/li&gt;
&lt;li&gt;first impressions on AWS XRay and gotchas for Golang users&lt;/li&gt;
&lt;li&gt;understanding the business value is the key aspect in a software architecture&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Reducing Docker images size
&lt;/h3&gt;

&lt;p&gt;Source: &lt;a href="https://www.ardanlabs.com/blog/2020/02/docker-images-part1-reducing-image-size.html"&gt;Jérôme Petazzoni&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Among all the articles on the subject, this is the best I have read.&lt;br&gt;
Great tips on how to reduce Docker images size, and it also features insights on statically vs dynamically linked binaries.&lt;/p&gt;

&lt;p&gt;A statically compiled binary can run even in a Scratch image, because the binary will include all the libraries it needs, even libc. In golang to get a statically compiled binary use the GCO flag:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CGO_ENABLED=0 go build&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure availability for systems
&lt;/h3&gt;

&lt;p&gt;Source: &lt;a href="https://www.usenix.org/system/files/nsdi20spring_hauer_prepub.pdf"&gt;Google Research Paper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A paper from Google engineers about measuring availability.&lt;br&gt;
It's well written and very light on math; it's easy to picture how to implement the suggested approach to measure availability for our systems.&lt;/p&gt;

&lt;p&gt;Some takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer time based (availability = uptime / (uptime + downtime)) metrics to count based metrics (availability = successful requests / total requests)&lt;/li&gt;
&lt;li&gt;After a successful (or failing) operation, assume that the system is up (or down) until the user sees evidence to the contrary.&lt;/li&gt;
&lt;li&gt;If no request arrives within a cutoff duration the segment is marked as inactive and does not count towards user downtime or uptime. &lt;/li&gt;
&lt;li&gt;use different time windows, i.e. 1 minute, 5 minutes, 1 hour and graph them at different percentiles&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  AWS XRay first impressions and gotchas
&lt;/h3&gt;

&lt;p&gt;Source: &lt;a href="https://twitter.com/napicellatwit"&gt;myself&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 1&lt;/strong&gt;&lt;br&gt;
XRay allows to build a latency map for a request.&lt;br&gt;
The number in the circle is the time between the moment the request entered the node and the one in which it exited.&lt;br&gt;
By clicking on the edge, you get the time for the request to reach the next node. Notice that this measure is only accurate if the next node is instrumented with XRay, otherwise it's an estimate computed on the caller side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s_itwUcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8oe7d3lkju833shzuoqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s_itwUcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8oe7d3lkju833shzuoqt.png" alt="AWS XRay service map" width="880" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 2&lt;/strong&gt;&lt;br&gt;
Dropping in XRay in an existing application is easy, just wrap the client.&lt;/p&gt;

&lt;p&gt;Instrumenting http calls made via an http client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;xray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodGet&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="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instrumenting calls to AWS Services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// DynamoDB example&lt;/span&gt;
&lt;span class="n"&gt;dynamoDbSvc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;xray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamoDbSvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Takeaway 3&lt;/strong&gt;&lt;br&gt;
Using XRay in a Lambda with Golang requires a XRay libray version greater or equal to v1. This is important, otherwise you will be presented with a cryptic error message at runtime!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get –u github.com/aws/aws-xray-sdk-go@v1.0.0-rc.14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Takeaway 4&lt;/strong&gt;&lt;br&gt;
You need to pass the context to each call you make. In the following snippet, notice the &lt;em&gt;PutItemWithContext&lt;/em&gt; call instead of &lt;em&gt;PutItem&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALBTargetGroupRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALBTargetGroupResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamoDbSvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutItemWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutItemInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
   &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;av&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
   &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="n"&gt;ConditionExpression&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"attribute_not_exists(wid)"&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;If you are running in a Lambda, the context is what you receive from the Lambda invocation. Otherwise you need to create a new Context.&lt;/p&gt;

&lt;p&gt;If you get an error like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;failed to begin subsegment
named 'dynamo': segment cannot be found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you likely forgot to pass a context to the request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 5&lt;/strong&gt;&lt;br&gt;
Many services integrate with XRay, for example API Gateway.&lt;br&gt;
If you enable XRay for API Gateway via console or CloudFormation you need to manually trigger a deployment. Otherwise you'll get UNAUTHORIZED from API Gateway!&lt;/p&gt;

&lt;h3&gt;
  
  
  Business value is the most important aspect when designing architectures
&lt;/h3&gt;

&lt;p&gt;Source: &lt;a href="https://martinfowler.com/articles/value-architectural-attribute.html"&gt;Martin Fowler's blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Software architects tend to describe the performance of these systems, how resilient they are to faults, and how they are designed to evolve to easily support new capabilities. The elephant that rarely comes up, however, is how different systems contribute to business value.&lt;/p&gt;

&lt;p&gt;Business value is the most important aspect to consider when designing a system architecture. Both functional and non functional requirements depend on business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 1&lt;/strong&gt;&lt;br&gt;
Consider the business value impact of failures. If we want to take measures to improve the resilience of a system, it's good to express it in terms of the value at risk should it fail. An important part of analyzing how much value is at risk is recognizing that, since different failures have differently severe consequences, all software components do not need the same levels of resilience&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 2&lt;/strong&gt;&lt;br&gt;
What's the cost for the business of less resiliency, scalability or speed?&lt;br&gt;
Modulate the effort to achieve these system attributes based on the actual value provided to the business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 3&lt;/strong&gt;&lt;br&gt;
Consider the cost of configurability or other premature optimizations&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway 4&lt;/strong&gt;&lt;br&gt;
When doing an assessment of value that would be at risk, it's worth approaching in two complimentary ways. One route is to go top-down, looking at a business function and identifying which software systems support that function. The other is the reverse, starting with a software system, and considering what ramifications a failure here would have.&lt;br&gt;
&lt;em&gt;Top-down&lt;/em&gt;&lt;br&gt;
I.e. Which features make money? What are the components of the system which implement the feature? This would be the most valuable pieces of the architecture.&lt;br&gt;
&lt;em&gt;Bottom-up&lt;/em&gt;&lt;br&gt;
If this component would go down, which features would be impacted? What would be the lost value?&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;...and this is all for this issue.&lt;/p&gt;

&lt;p&gt;Some of the content I write is too short for a post, but still interesting enough to share it as a tweet. Follow me on &lt;a href="https://twitter.com/napicellatwit"&gt;Twitter&lt;/a&gt; to get them in your Twitter feed!&lt;/p&gt;




&lt;p&gt;Credit for the cover image to &lt;a href="https://pixabay.com/users/graphicmama-team-2641041/"&gt;GraphicMama-team&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>go</category>
      <category>showdev</category>
      <category>aws</category>
    </item>
    <item>
      <title>Linux terminals, tty, pty and shell - part 2</title>
      <dc:creator>Nicola Apicella</dc:creator>
      <pubDate>Mon, 02 Mar 2020 17:21:18 +0000</pubDate>
      <link>https://dev.to/napicella/linux-terminals-tty-pty-and-shell-part-2-2cb2</link>
      <guid>https://dev.to/napicella/linux-terminals-tty-pty-and-shell-part-2-2cb2</guid>
      <description>&lt;p&gt;This is the second post of the series on Linux terminals, tty, pty and shell.&lt;/p&gt;

&lt;p&gt;In the first post we have talked about the difference between tty, pty and Shell and what happens when we press a key in a Terminal (like Xterm). If you haven't had the chance to read it yet, this is the link to the first part of the &lt;a href="https://dev.to/napicella/linux-terminals-tty-pty-and-shell-192e"&gt;article&lt;/a&gt;. Without reading the first part, some of the things discussed here might be harder to understand.&lt;/p&gt;

&lt;p&gt;In this article we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define what's a line discipline and see how programs can control it&lt;/li&gt;
&lt;li&gt;build a simple remote terminal application in golang&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Line discipline
&lt;/h3&gt;

&lt;p&gt;In the previous article we introduced the line discipline as an essential part of the terminal device.&lt;/p&gt;

&lt;h4&gt;
  
  
  But what is it?
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;From Wiki:&lt;br&gt;
In the Linux terminal subsystem, the line discipline is a kernel module which serves as a protocol handler between the level device driver and the generic program interface routines (such as read(2), write(2) and ioctl(2)) offered to the programs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This definition is a bit dry, fortunately it contains a few keywords we can use to dive deeper.&lt;/p&gt;

&lt;p&gt;In a Unix-like system &lt;strong&gt;everything is a file&lt;/strong&gt;, we all have heard this before. A program managing a pty will essentially perform &lt;strong&gt;read&lt;/strong&gt; and &lt;strong&gt;write&lt;/strong&gt; operations on a pair of files, pty master and pty slave.&lt;br&gt;&lt;br&gt;
A program writing data to disk, sending a document to the printer or getting data from an usb stick will use the same &lt;strong&gt;read&lt;/strong&gt; and &lt;strong&gt;write&lt;/strong&gt; operations, although the work required to perform the tasks depends on the type of the device and the characteristics of the device itself. &lt;br&gt;
Our program is completely unaware of those details - the kernel provides a programming interface and takes care of all these differences for us.&lt;/p&gt;

&lt;p&gt;When a program calls the &lt;strong&gt;read&lt;/strong&gt; or &lt;strong&gt;write&lt;/strong&gt; operations, behind the scene, the kernel will use the right implementation for us.&lt;br&gt;
In the case of the pty, the kernel will use the tty driver to &lt;strong&gt;handle the communication&lt;/strong&gt; between the terminal and the program. The line discipline is a logical component of the tty driver. &lt;/p&gt;

&lt;h4&gt;
  
  
  What does it do?
&lt;/h4&gt;

&lt;p&gt;The following is a (non comprehensive) list of the line discipline functionalities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when we type, characters are echoed back to the pty master (&lt;a href="https://dev.to/napicella/linux-terminals-tty-pty-and-shell-192e"&gt;terminals are dumb&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;it buffers the characters in memory. It sends them to the pty slave when we press enter&lt;/li&gt;
&lt;li&gt;when we type &lt;code&gt;CTRL + W&lt;/code&gt;, it deletes the last word we typed&lt;/li&gt;
&lt;li&gt;when we type &lt;code&gt;CTR + C&lt;/code&gt;, it sends the &lt;code&gt;kill -2 (SIGINT)&lt;/code&gt; command to the program attached to the pty slave&lt;/li&gt;
&lt;li&gt;when we press &lt;code&gt;CRL + Z&lt;/code&gt;, it sends the &lt;code&gt;kill -STOP&lt;/code&gt; command &lt;/li&gt;
&lt;li&gt;when pressing &lt;code&gt;CTRL + S&lt;/code&gt;, it sends XOFF to the tty driver to put the process that is sending data into a sleep state&lt;/li&gt;
&lt;li&gt;it replaces all the New Line (Enter key) characters with a Carriage return and New Line sequence.&lt;/li&gt;
&lt;li&gt;when we press backspace, it deletes the character from the buffer. It then sends to the pty master the instructions to delete the last character&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Historical note&lt;/strong&gt;: XON/XOFF is a flow control feature that traces back to the time of hardware teletypes connected to the computer via a UART line. When one end of the data link  could not receive any more data (because the buffer was full) it would send an "XOFF" signal to the sending end of the data link to pause until the "XON" signal was received.&lt;br&gt;&lt;br&gt;
Today, with computers featuring Giga bytes of RAM, "XOFF" is used just as a mean to suspend processes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Linux system is made of tons of abstractions which makes our life easier when we need to program. As with all the abstractions, they also make it hard to understand what's going on.&lt;/p&gt;

&lt;p&gt;Fortunately, we have an ace in the hole: the power of trying out stuff. This is what we are going to next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing the line discipline with stty
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;stty&lt;/strong&gt; is an utility to query and change the line discipline rules for the device connected to its standard input. &lt;/p&gt;

&lt;p&gt;Run in a terminal &lt;strong&gt;stty -a&lt;/strong&gt;  &lt;/p&gt;

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

$ stty -a
speed 38400 baud; rows 40; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = &amp;lt;undef&amp;gt;;
eol2 = &amp;lt;undef&amp;gt;; swtch = &amp;lt;undef&amp;gt;; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The output of the command returns the terminal characteristics and the line discipline rules.&lt;br&gt;&lt;br&gt;
The first line contains the baud rate, the number of rows and columns of the terminal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Historical note&lt;/strong&gt;: When the terminal and the computer where connected via a line, baud rate provided the symbol rate of the channel. The baud rate is meaningless for a pty. You can read more about it in its &lt;a href="https://en.wikipedia.org/wiki/Baud" rel="noopener noreferrer"&gt;wiki page&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next line contains key bindings: For example the &lt;code&gt;initr = ^C&lt;/code&gt; maps &lt;code&gt;CTRL + C&lt;/code&gt; to &lt;code&gt;kill -2 (SIGINT)&lt;/code&gt;.&lt;br&gt;
Scrolling through the end of the output we find the line discipline rules that do not require a key binding. &lt;br&gt;
Do you see the &lt;strong&gt;echo&lt;/strong&gt; ?&lt;br&gt;&lt;br&gt;
&lt;strong&gt;echo&lt;/strong&gt; is the rule which instructs the line discipline to echo characters back.&lt;/p&gt;

&lt;p&gt;You can imagine what happens if we disable it.&lt;br&gt;
Open a terminal and run:&lt;/p&gt;

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

$ stty -echo


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then type something...nothing will appear on the screen.&lt;br&gt;
The line discipline does not echo the characters back to the pty master, thus the terminal does not show what we type anymore!&lt;/p&gt;

&lt;p&gt;Everything else works as usual. For example, type &lt;code&gt;ls&lt;/code&gt; followed by enter. You will see the output of &lt;code&gt;ls&lt;/code&gt;, although you haven't seen the characters &lt;code&gt;ls&lt;/code&gt; when you typed them.&lt;/p&gt;

&lt;p&gt;We can restore it by typing:&lt;/p&gt;

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

stty echo


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can disable all the rules of the line discipline by typing &lt;strong&gt;stty raw&lt;/strong&gt;. Such terminal is called &lt;strong&gt;raw terminal&lt;/strong&gt;.&lt;br&gt;
A &lt;strong&gt;cooked terminal&lt;/strong&gt; is the opposite of a raw terminal - it's a terminal connected to a line discipline with all the rules enabled.&lt;/p&gt;

&lt;p&gt;Why would someone want a raw terminal? No echo, line editing, suspend or killing, etc. It looks like a nightmare! &lt;br&gt;
Well, it depends on what's the program receiving the input of the terminal. For example, programs like VIM set the terminal to raw because they need to process the characters themselves. Any external intervention that transforms or eats up characters would be a problem for an editor.&lt;/p&gt;

&lt;p&gt;As we will see, our remote terminal would need a raw terminal as well. &lt;/p&gt;

&lt;h3&gt;
  
  
  Build a remote terminal in golang
&lt;/h3&gt;

&lt;p&gt;A remote terminal program allows to access a terminal on a remote host.&lt;br&gt;
Connecting through SSH to a remote machine does just that. What we want to do is similar to the result of running &lt;code&gt;ssh&lt;/code&gt;, minus the encryption bits.&lt;/p&gt;

&lt;p&gt;I think we know enough to start hacking on some code.&lt;br&gt;
We would need a client-server application. The client runs on our machine and the server sits on some remote host.&lt;br&gt;&lt;br&gt;
The client and the server will communicate via tcp.&lt;/p&gt;

&lt;p&gt;I have simplified the code to highlight the interesting bit for this article. You can find the code for the example and how to build it on &lt;a href="https://gist.github.com/napicella/777e83c0ef5b77bf72c0a5d5da9a4b4e" rel="noopener noreferrer"&gt;git&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's start from the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remote terminal server
&lt;/h3&gt;

&lt;p&gt;The server performs the following operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open a tcp connection and listen for incoming requests&lt;/li&gt;
&lt;li&gt;it creates a pty when it receives a request&lt;/li&gt;
&lt;li&gt;run the bash process&lt;/li&gt;
&lt;li&gt;assign the standard input, output and error of bash to the pty slave&lt;/li&gt;
&lt;li&gt;send data received from the connection down to the pty master&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our server does exactly what the &lt;a href="https://dev.to/napicella/linux-terminals-tty-pty-and-shell-192e"&gt;terminal emulator does&lt;/a&gt;, but in this case instead of drawing stuff to the screen, it performs the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read from the master and send the content down to the tcp connection&lt;/li&gt;
&lt;li&gt;read from the tcp connection and write the content to the master&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follows the code for the server:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="c"&gt;// Create command&lt;/span&gt;&lt;br&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bash"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="c"&amp;gt;// Start the command with a pty.&amp;lt;/span&amp;gt;
&amp;lt;span class="c"&amp;gt;// It also assign the standard input, output and error of bash to the pty slave&amp;lt;/span&amp;gt;
&amp;lt;span class="n"&amp;gt;ptmx&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;:=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;pty&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Start&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;c&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;!=&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span class="c"&amp;gt;// Make sure to close the pty at the end.&amp;lt;/span&amp;gt;
&amp;lt;span class="k"&amp;gt;defer&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;_&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;ptmx&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Close&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;}()&amp;lt;/span&amp;gt; &amp;lt;span class="c"&amp;gt;// Best effort.&amp;lt;/span&amp;gt;

&amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;listen&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;ptmx&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptmx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Launching server..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="c"&amp;gt;// listen on all interfaces&amp;lt;/span&amp;gt;
&amp;lt;span class="n"&amp;gt;ln&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;:=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;net&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Listen&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"tcp"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;":8081"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;!=&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;
&amp;lt;span class="c"&amp;gt;// accept connection on port&amp;lt;/span&amp;gt;
&amp;lt;span class="n"&amp;gt;conn&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;:=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;ln&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Accept&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt;
&amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;!=&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;

&amp;lt;span class="k"&amp;gt;go&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;_&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;_&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;io&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Copy&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;ptmx&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;conn&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;}()&amp;lt;/span&amp;gt;
&amp;lt;span class="n"&amp;gt;_&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;io&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Copy&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;conn&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;ptmx&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Remote terminal client&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;It would appear that our client would just need to open a tcp connection with the server, send the standard input to the tcp connection and write the data from the connection to the standard standard output.&lt;br&gt;&lt;br&gt;
And indeed, there isn't much more to it. &lt;/p&gt;

&lt;p&gt;There is only a caveat, the client should send all the characters to the server. We do not want the line discipline on the client to interfere with the characters we type. &lt;strong&gt;Setting the terminal to raw mode does just that.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The client performs the following operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set the terminal to raw mode&lt;/li&gt;
&lt;li&gt;open a tcp connection with the remote host&lt;/li&gt;
&lt;li&gt;send the standard input to the tcp connection&lt;/li&gt;
&lt;li&gt;send the data from the tcp connection to the standard output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally let's see the client code:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="c"&gt;// MakeRaw put the terminal connected to the given file&lt;/span&gt;&lt;br&gt;
    &lt;span class="c"&gt;// descriptor into raw mode and returns the previous state&lt;/span&gt;&lt;br&gt;
    &lt;span class="c"&gt;// of the terminal so that it can be restored.&lt;/span&gt;&lt;br&gt;
    &lt;span class="n"&gt;oldState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;terminal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MakeRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fd&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;br&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;terminal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Restore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fd&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;oldState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}()&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="c"&amp;gt;// Connect to this socket.&amp;lt;/span&amp;gt;
&amp;lt;span class="c"&amp;gt;// If client and server runs on different machines,&amp;lt;/span&amp;gt;
&amp;lt;span class="c"&amp;gt;// replace the loopback address with the address of&amp;lt;/span&amp;gt;
&amp;lt;span class="c"&amp;gt;// remote host&amp;lt;/span&amp;gt;
&amp;lt;span class="n"&amp;gt;conn&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;:=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;net&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Dial&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"tcp"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;"127.0.0.1:8081"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;!=&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;

&amp;lt;span class="k"&amp;gt;go&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;_&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;_&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;io&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Copy&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;os&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Stdout&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;conn&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;}()&amp;lt;/span&amp;gt;
&amp;lt;span class="n"&amp;gt;_&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;io&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Copy&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;conn&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;os&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Stdin&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span class="n"&amp;gt;fmt&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Println&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"Bye!"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;

&amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;e&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  What happens when we run the program?&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Now that we have the client and the server, we can see the whole workflow from client to server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fca19ny7m3an73am93u55.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fca19ny7m3an73am93u55.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the following we assume the golang program has been compiled in a binary called &lt;code&gt;remote&lt;/code&gt;. We will also assume the program has already been started on the server machine.&lt;/p&gt;


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

&lt;p&gt;go build -o remote main.go&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Initialization&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The client&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the user (the stick-man in the picture[&lt;strong&gt;*&lt;/strong&gt;]) opens a terminal emulator, like XTERM.
The terminal emulator will:

&lt;ol&gt;
&lt;li&gt;draw the UI to the video and requests a pty from the OS&lt;/li&gt;
&lt;li&gt;launch bash as subprocess&lt;/li&gt;
&lt;li&gt;set the std input, output and error of bash to be the pty slave&lt;/li&gt;
&lt;li&gt;listen for keyboard events&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;the user types &lt;code&gt;./remote -client&lt;/code&gt;

&lt;ol&gt;
&lt;li&gt;the terminal emulator receives the keyboard events &lt;/li&gt;
&lt;li&gt;sends the character to the pty master&lt;/li&gt;
&lt;li&gt;the line discipline gets the character and buffers them. It copies them to the slave only when &lt;code&gt;Enter&lt;/code&gt; is pressed. It also writes back its input to the master (echoing back). &lt;/li&gt;
&lt;li&gt;when the user presses enter, the tty driver takes care of copying the buffered data to the pty slave&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;the user presses &lt;code&gt;Enter&lt;/code&gt;:

&lt;ol&gt;
&lt;li&gt;bash (which was waiting for input on standard input) finally reads the characters &lt;/li&gt;
&lt;li&gt;bash interprets the characters and figures it needs to run a program called &lt;code&gt;remote&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;bash forks itself and runs the &lt;code&gt;remote&lt;/code&gt; program in the fork. The forked process will have the same stdin, stdout and stderr used by bash, which is the pty slave.
The remote client starts

&lt;ol&gt;
&lt;li&gt;set the terminal in raw mode, disabling the line discipline&lt;/li&gt;
&lt;li&gt;open a tcp connection with the server&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The server&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;accept the tcp connection&lt;/li&gt;
&lt;li&gt;request a pty from the OS&lt;/li&gt;
&lt;li&gt;launch bash and set the std input, output and error of bash to be the pty slave 

&lt;ol&gt;
&lt;li&gt;bash starts&lt;/li&gt;
&lt;li&gt;bash writes to standard output (pty slave) the bash line &lt;code&gt;~ &amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the tty driver copies the characters from the pty slave to pty master&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;the remote server copies the data from the pty master to the tcp connection&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The client&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the client receives data from the tcp connection and sends it to the standard output&lt;/li&gt;
&lt;li&gt;the tty driver copies the characters from the pty slave to pty master&lt;/li&gt;
&lt;li&gt;the terminal emulator gets the characters from the pty master and draws them on the screen&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of this is just to display on the client the bash line &lt;code&gt;~ &amp;gt;&lt;/code&gt; coming from the bash process which runs on the remote server!&lt;br&gt;
Now, what happens when the user types a command?    &lt;/p&gt;

&lt;h3&gt;
  
  
  Typing a command
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The client&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the user types &lt;code&gt;ls -la&lt;/code&gt; followed by &lt;code&gt;Enter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the terminal emulator sends the characters to the pty master&lt;/li&gt;
&lt;li&gt;the tty driver copies them as they come to the pty slave (remember the remote client has disabled the line discipline)&lt;/li&gt;
&lt;li&gt;the remote client reads the data from the pty slave and sends them through the tcp connection&lt;/li&gt;
&lt;li&gt;the remote client waits to read the characters from the tcp connection &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The server&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the remote server writes the bytes received from the tcp connection to the pty master&lt;/li&gt;
&lt;li&gt;tty driver buffers them until the character &lt;code&gt;Enter&lt;/code&gt; has been received. It also writes back its input to the master (echoing back). &lt;/li&gt;
&lt;li&gt;the remote server reads the characters from the master and sends them back to the tcp connection (these are the characters typed by the client!)&lt;/li&gt;
&lt;li&gt;the tty driver writes the data to the pty slave&lt;/li&gt;
&lt;li&gt;bash interprets the character and figures it needs to run a program called &lt;code&gt;ls -la&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;bash forks the process. The forked process will have the same stdin, stdout and stderr used by bash, which is the pty slave.&lt;/li&gt;
&lt;li&gt;the output of the command is copied to the pty slave&lt;/li&gt;
&lt;li&gt;the tty driver copies the output to the pty master&lt;/li&gt;
&lt;li&gt;the remote server copies the data from the pty master to the tcp connection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An interesting thing to notice. On the client machine, all the characters we see on the screen come from the remote server. Including what we type!&lt;br&gt;
It's the line discipline on the remote server which echoes back the characters and from there find their way back to the client!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Look back at our little golang program and compare it with the number of steps in the workflow. &lt;br&gt;
With a little over 50 lines of code we were able to implement the whole workflow. Our program is small thanks to the kernel, which performs the heavy lifting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's the power of abstraction&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;We have reached the end of the series. The research work to write it was a lot fun and I hope it was also an interesting read.&lt;/p&gt;

&lt;p&gt;Some of the content I write is too short for a post, but still interesting enough to share it as a tweet. Follow me on &lt;a href="https://twitter.com/napicellatwit" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to get them in your Twitter feed!&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;br&gt;
-Nicola&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;*&lt;/strong&gt; I know, it looks more like the game of hangman than a system diagram&lt;/p&gt;

</description>
      <category>linux</category>
      <category>beginners</category>
      <category>go</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
