<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Tdd Apps</title>
    <description>Ideas about sofware craftmanship and automation. Things that were hard to discover. Useful tips about some tools. And book reviews.
</description>
    <link>https://www.tddapps.com/</link>

    <image>
      <url>https://www.tddapps.com/apple-icon.png</url>
      <title>Tdd Apps</title>
      <link>https://www.tddapps.com/</link>
      <description>Ideas about sofware craftmanship and automation. Things that were hard to discover. Useful tips about some tools. And book reviews.
</description>
    </image>

    <atom:link href="https://www.tddapps.com/feed.xml" rel="self" type="application/rss+xml" />
    
      
        <item>
          <title>Book Review: Cracking the Coding Interview</title>
          <description>&lt;p&gt;Interviews are stressful, they cover rarely used topics. Industry leading companies expect candidates to prepare in many areas. It is impossible to answer every question correctly. Reading a book won’t get you hired but this book is as good of a practice guide as it gets.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://amzn.to/3naRTk0&quot;&gt;&lt;img src=&quot;/images/books/cracking-the-coding-interview.png&quot; alt=&quot;Cracking the Coding Interview&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;exercises&quot;&gt;Exercises&lt;/h2&gt;
&lt;p&gt;Cracking the Coding Interview is a good refresher of many Computer Science topics. The book has hundreds of exercises of data structures and algorithms. Every solution is explained in detail.&lt;/p&gt;

&lt;h2 id=&quot;big-o-notation&quot;&gt;Big-O notation&lt;/h2&gt;
&lt;p&gt;The book does a superb job explaining time and space complexity. This topic comes up in every interview. Candidates need to understand it well.&lt;/p&gt;

&lt;h2 id=&quot;be-prepared&quot;&gt;Be prepared&lt;/h2&gt;
&lt;p&gt;Reading a book will not get you hired. Practice is very important. Do the exercises, practice coding on a whiteboard. Be aware different companies value different skills and you may even get rejected after intensive practice.&lt;/p&gt;
</description>
          <pubDate>Sat, 11 Jul 2020 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2020/07/11/cracking-the-coding-interview/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2020/07/11/cracking-the-coding-interview/</guid>
        </item>
      
    
      
        <item>
          <title>Lessons from building a Raspberry Pi 4 NAS</title>
          <description>&lt;p&gt;I built a home NAS with a Raspberry Pi 4. It took longer than expected. The end result is not as great as I originally envisioned. These are some bits of hard-earned knowledge to save you time and pain.&lt;/p&gt;

&lt;h2 id=&quot;usb&quot;&gt;USB&lt;/h2&gt;

&lt;p&gt;USB 3 support is not great. I tested at least four different drives and as many drive enclosures. Powered USB hubs. Even powered hard drive enclosures. Drives just disconnect regularly.&lt;/p&gt;

&lt;p&gt;This &lt;a href=&quot;https://www.raspberrypi.org/forums/viewtopic.php?f=28&amp;amp;t=245931&quot;&gt;forum post&lt;/a&gt; helped mitigate some USB problems. However, I found no reliable way to copy hundreds of gigabytes over USB. I had to copy the bulk of the data on my laptop.&lt;/p&gt;

&lt;h2 id=&quot;file-system&quot;&gt;File System&lt;/h2&gt;

&lt;p&gt;Since I had to plug the NAS disk to my laptop, file system selection was harder.&lt;/p&gt;

&lt;p&gt;I chose &lt;a href=&quot;https://en.wikipedia.org/wiki/ExFAT&quot;&gt;Ex-FAT&lt;/a&gt;. It comes native on Mac OS and &lt;a href=&quot;https://pimylifeup.com/raspberry-pi-exfat/&quot;&gt;can be installed&lt;/a&gt; on Linux. However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mount.exfat&lt;/code&gt; CPU usage becomes high at times.&lt;/p&gt;

&lt;h2 id=&quot;operating-system&quot;&gt;Operating System&lt;/h2&gt;

&lt;p&gt;Use Raspbian to minimize friction. Most documentation and examples are based on it.&lt;/p&gt;

&lt;p&gt;I tried Ubuntu first. But some things work differently (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot/cmdline.txt&lt;/code&gt; is in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot/firmware/nobtcmd.txt&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id=&quot;power-supply&quot;&gt;Power Supply&lt;/h2&gt;

&lt;p&gt;Get the &lt;a href=&quot;https://amzn.to/3b2OsVn&quot;&gt;Official Power Supply&lt;/a&gt;. The Raspberry Pi 4 comes with a USB-C charging port. But this port &lt;a href=&quot;https://arstechnica.com/gadgets/2019/07/raspberry-pi-4-uses-incorrect-usb-c-design-wont-work-with-some-chargers/&quot;&gt;is not entirely compliant with the USB-C standard&lt;/a&gt;. Incompatible cables and power supplies cause throttling.&lt;/p&gt;

&lt;p&gt;Monitor your computer physical parameters. Voltage, temperature, etc. If the board gets too hot it will get throttled. The &lt;a href=&quot;https://www.raspberrypi.org/documentation/raspbian/applications/vcgencmd.md&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vcgencmd&lt;/code&gt;&lt;/a&gt; is very useful to troubleshoot these issues.&lt;/p&gt;

&lt;h2 id=&quot;automation&quot;&gt;Automation&lt;/h2&gt;

&lt;p&gt;Automate your setup. I created a few ansible playbooks for provisioning. These playbooks are the NAS core.&lt;/p&gt;

&lt;p&gt;I tried many different approaches because I had the automation to reduce variance and manual errors. Ultimately, this NAS will be moved to a different computer and the ansible playbooks will help me do it.&lt;/p&gt;

&lt;h2 id=&quot;learn&quot;&gt;Learn&lt;/h2&gt;

&lt;p&gt;Building a NAS out of a Raspberry Pi is a good learning experience for those who want to learn more about Linux administration.&lt;/p&gt;

&lt;p&gt;Is it worth the time and effort compared to an &lt;a href=&quot;https://amzn.to/2WoHfcQ&quot;&gt;off-the-shelf solution&lt;/a&gt;? It depends.&lt;/p&gt;

&lt;p&gt;This NAS is not suitable for high volume loads. But it is silent, small, and has a close to zero power consumption. It is built on a general purpose computer and operating system. It has no proprietary restrictions. It gets regular security updates. It gives you control to tailor it to your needs.&lt;/p&gt;
</description>
          <pubDate>Tue, 05 May 2020 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2020/05/05/raspberry-pi-4-home-nas/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2020/05/05/raspberry-pi-4-home-nas/</guid>
        </item>
      
    
      
        <item>
          <title>South Florida's Codecamp 2020</title>
          <description>&lt;p&gt;These are the notes on my &lt;strong&gt;Improve your AWS security&lt;/strong&gt; presentation&lt;/p&gt;

&lt;h2 id=&quot;slides&quot;&gt;&lt;a href=&quot;https://docs.google.com/presentation/d/1OZl445nzbtDBh3EtsFWAsJTA22cKxsh8WJHScEwaLQA/edit?usp=sharing&quot;&gt;Slides&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id=&quot;source-code&quot;&gt;&lt;a href=&quot;https://github.com/cshtdd/aws-security-presentation&quot;&gt;Source Code&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id=&quot;presentation-outline&quot;&gt;Presentation Outline&lt;/h2&gt;

&lt;p&gt;Cloud adoption has rapidly increased with the years. The cloud empowers companies to change faster.
However, speed comes with its own set of risks: accidental data leaks, internal services publicly accessible, compromised account credentials.
AWS offers several services to help secure and monitor your account.&lt;/p&gt;

&lt;p&gt;Wouldn’t it be great to automatically validate S3 buckets are not publicly available?
Would you like to know who destroyed the test EC2 instance without your consent?
Would you like to get an alert when your account is under attack?&lt;/p&gt;

&lt;p&gt;This presentation will cover how to use three powerful AWS services to answer these and other questions.
We will walk through AWS Config, AWS Cloudtrail and AWS Guarduty. What do they do. And what problems they solve.&lt;/p&gt;

&lt;p&gt;This is an introductory talk. Participants with any level of AWS knowledge are welcomed.
This is an AWS only talk. The comparable Azure services will be explained in a future presentation.&lt;/p&gt;

</description>
          <pubDate>Sat, 29 Feb 2020 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2020/02/29/codecamp-presentation/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2020/02/29/codecamp-presentation/</guid>
        </item>
      
    
      
        <item>
          <title>Solarize Slack for Real</title>
          <description>&lt;p&gt;These are the instructions I followed to make Slack look like a &lt;a href=&quot;https://ethanschoonover.com/solarized/&quot;&gt;Solarized&lt;/a&gt; app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;The &lt;a href=&quot;https://slack.engineering/rebuilding-slack-on-the-desktop-308d6fe94ae4&quot;&gt;recent Slack client rewrite&lt;/a&gt; renders these instructions unusable.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;before&quot;&gt;Before&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/solarize-slack/before.png&quot; alt=&quot;Before&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;after&quot;&gt;After&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/solarize-slack/after.png&quot; alt=&quot;After&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;step-1-change-the-sidebar-theme&quot;&gt;Step 1: Change the Sidebar theme&lt;/h2&gt;

&lt;p&gt;Use the following sidebar theme. &lt;a href=&quot;https://trevmex.com/post/94769857233/solarized-dark-for-slack&quot;&gt;Detailed instructions&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#073642,#002B36,#B58900,#FDF6E3,#CB4B16,#FDF6E3,#2AA198,#DC322F
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/mgreensmith/098897288f580b964ef8&quot;&gt;Theme Source&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-2-change-the-background-colors&quot;&gt;Step 2: Change the Background Colors&lt;/h2&gt;

&lt;p&gt;Add the following block to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Applications/Slack.app/Contents/Resources/app.asar.unpacked/src/static/ssb-interop.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;DOMContentLoaded&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
   &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://cdn.jsdelivr.net/gh/chattahippie/slack-night-mode@fcafbca8be2a720410c6b3988f280fa09ef8fca0/css/raw/variants/solarized-dark.css&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;na&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;style&amp;gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is a mix from the &lt;a href=&quot;https://github.com/nakedsushi/solarized-slack&quot;&gt;original repo&lt;/a&gt; and &lt;a href=&quot;https://github.com/chattahippie/solarized-slack&quot;&gt;this fork&lt;/a&gt;.&lt;/p&gt;
</description>
          <pubDate>Tue, 19 Mar 2019 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2019/03/19/solarize-slack-for-real/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2019/03/19/solarize-slack-for-real/</guid>
        </item>
      
    
      
        <item>
          <title>Ubuntu Server 18.04 LTS upgrade issues and their solutions</title>
          <description>&lt;p&gt;I run &lt;a href=&quot;https://www.linux-kvm.org/page/Main_Page&quot;&gt;KVM&lt;/a&gt;, &lt;a href=&quot;https://www.postgresql.org/&quot;&gt;PostgreSQL&lt;/a&gt;, &lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt; and the &lt;a href=&quot;https://www.elastic.co/elk-stack&quot;&gt;ELK Stack&lt;/a&gt; on &lt;a href=&quot;https://www.ubuntu.com/&quot;&gt;Ubuntu&lt;/a&gt; at home. These computers were running &lt;a href=&quot;http://releases.ubuntu.com/16.04/&quot;&gt;Ubuntu Server 16.04 LTS&lt;/a&gt;. These are the solutions to the problems encountered during the upgrade to the &lt;a href=&quot;http://releases.ubuntu.com/18.04/&quot;&gt;latest LTS version 18.04&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: These solutions may not be appropriate for you. Follow them at your own risk.&lt;/p&gt;

&lt;h2 id=&quot;upgrade-process&quot;&gt;Upgrade Process&lt;/h2&gt;

&lt;p&gt;The operating system prompts you to upgrade whenever you log into a computer with an outdated distribution.&lt;br /&gt;
After creating the mandatory backups I ran the suggested command.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-release-upgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The upgrade is relatively smooth. It displays several prompts, and everything works fine until it doesn’t.&lt;/p&gt;

&lt;h2 id=&quot;problems-during-the-upgrade&quot;&gt;Problems during the upgrade&lt;/h2&gt;

&lt;h3 id=&quot;boot-partition-is-full&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; partition is full&lt;/h3&gt;

&lt;p&gt;Some computers that run for years, accumulate old kernels in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; partition. The upgrade reaches a point where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; crashes and nothing can be installed.&lt;/p&gt;

&lt;p&gt;This &lt;a href=&quot;https://gist.github.com/ipbastola/2760cfc28be62a5ee10036851c654600&quot;&gt;guide to cleanup the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; partition&lt;/a&gt; proved invaluable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The process to remove old kernels may have to be executed multiple times. Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get -f install&lt;/code&gt; can fill the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot&lt;/code&gt; partition again.&lt;/p&gt;

&lt;h2 id=&quot;problems-after-the-upgrade&quot;&gt;Problems after the upgrade&lt;/h2&gt;

&lt;h3 id=&quot;local-dns-configuration-disappeared&quot;&gt;Local DNS configuration disappeared&lt;/h3&gt;

&lt;p&gt;Every upgraded computer lost their local DNS configuration.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat /etc/resolv.conf&lt;/code&gt; should print something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nameserver 192.168.1.1   # your local DNS IP
search router123456.com  # your local search domain
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If that is not the case, load the DNS configuration from the new location&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo mv&lt;/span&gt; /etc/resolv.conf /etc/resolv.conf.bck
&lt;span class=&quot;nb&quot;&gt;sudo ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; /run/systemd/resolve/resolv.conf /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;kvm-virtual-machines-failed-to-start&quot;&gt;KVM virtual machines failed to start&lt;/h3&gt;

&lt;p&gt;Sample Error:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;virsh start vm1
error: Failed to start domain vm1
error: the CPU is incompatible with host CPU: Host CPU does not provide required features: hle, rtm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: These were virtual machines that were running on that same computer before the upgrade.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://bugzilla.redhat.com/show_bug.cgi?id=1182650&quot;&gt;This Red Hat bug report&lt;/a&gt; has instructions to correct the issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Edit the vm:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;virsh edit vm1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Add the following features to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cpu&lt;/code&gt; xml node&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;feature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;policy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'disable'&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'rtm'&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;feature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;policy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'disable'&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'hle'&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;laptop-screen-does-not-turn-off-after-long-inactivity&quot;&gt;Laptop screen does not turn off after long inactivity&lt;/h3&gt;

&lt;p&gt;This used to work in the old 16.04 version.&lt;/p&gt;

&lt;p&gt;For the new 18.04 version I took a different approach. These commands make sure &lt;a href=&quot;https://itsfoss.com/ubuntu-close-lid-suspend/&quot;&gt;the computer doesn’t suspend when the lid is closed&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/#HandleLidSwitchDocked=ignore/HandleLidSwitchDocked=ignore/g'&lt;/span&gt; /etc/systemd/logind.conf
&lt;span class=&quot;nb&quot;&gt;sudo sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/#HandleLidSwitch=suspend/HandleLidSwitch=ignore/g'&lt;/span&gt; /etc/systemd/logind.conf
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;deprecated-postgresql-package&quot;&gt;Deprecated PostgreSQL package&lt;/h3&gt;

&lt;p&gt;The upgrade process mentioned the installed PostgreSQL version was deprecated. And that I should try running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_upgradecluster&lt;/code&gt; and reading &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/doc/postgresql-common/README.Debian.gz&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since I had a backup of my data I reinstalled PostgreSQL and everything kept working just fine.&lt;/p&gt;

&lt;h2 id=&quot;bottomline&quot;&gt;Bottomline&lt;/h2&gt;

&lt;p&gt;Although the Ubuntu 18.04 upgrade was not free of hiccups, these proved to be relatively easy to solve. Moreover, there was enough troubleshooting documentation readily available.&lt;/p&gt;
</description>
          <pubDate>Mon, 28 Jan 2019 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2019/01/28/ubuntu-server-18.04-lts-upgrade-issues-and-their-solutions/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2019/01/28/ubuntu-server-18.04-lts-upgrade-issues-and-their-solutions/</guid>
        </item>
      
    
      
        <item>
          <title>Migrating large files to GitHub</title>
          <description>&lt;p&gt;GitHub does not track &lt;a href=&quot;https://stackoverflow.com/questions/33330771/git-lfs-this-exceeds-githubs-file-size-limit-of-100-00-mb&quot;&gt;files larger than 100MB&lt;/a&gt;. I hit this limit during a &lt;a href=&quot;https://blog.github.com/changelog/2019-01-08-pricing-changes/&quot;&gt;recent repository migration&lt;/a&gt;. &lt;a href=&quot;https://git-lfs.github.com/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git lsf&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://rtyley.github.io/bfg-repo-cleaner/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bfg&lt;/code&gt;&lt;/a&gt; helped me push everything. These are the steps I took to overcome this limitation.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Create a backup of your repository.&lt;/li&gt;
  &lt;li&gt;Install the &lt;a href=&quot;https://git-lfs.github.com/&quot;&gt;Git Large File Storage Tool&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Get the &lt;a href=&quot;https://rtyley.github.io/bfg-repo-cleaner/&quot;&gt;BFG Repo Cleaner&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Make sure the repository is in a clean state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Try to push.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git push origin master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;gh001-error&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GH001&lt;/code&gt; Error&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Pushing to https://github.com/tddapps/my_secret_app.git
POST git-receive-pack (chunked)
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.        
remote: error: Trace: 354c0f098fda496abeb40f7f6472b655        
remote: error: See http://git.io/iEPt8g for more information.        
remote: error: File Assets/Dependencies/libsmbclient.a is 114.65 MB; this exceeds GitHub's file size limit of 100.00 MB        
To https://github.com/tddapps/my_secret_app.git
 ! [remote rejected] change-photo-source-buttons -&amp;gt; change-photo-source-buttons (pre-receive hook declined)
 ! [remote rejected] critical-section -&amp;gt; critical-section (pre-receive hook declined)
 ! [remote rejected] master -&amp;gt; master (pre-receive hook declined)
 ! [remote rejected] version_1_0 -&amp;gt; version_1_0 (pre-receive hook declined)
 ! [remote rejected] version_1_1 -&amp;gt; version_1_1 (pre-receive hook declined)
error: failed to push some refs to 'https://github.com/tddapps/my_secret_app.git'
Completed with errors, see above
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Write down the problematic filename. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libsmbclient.a&lt;/code&gt; in this case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Remove the file from your history. Don’t worry, it will stay in your working directory.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;java &lt;span class=&quot;nt&quot;&gt;-jar&lt;/span&gt; ~/Downloads/bfg-1.13.0.jar &lt;span class=&quot;nt&quot;&gt;--delete-files&lt;/span&gt; libsmbclient.a &lt;span class=&quot;nt&quot;&gt;--no-blob-protection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Unstage the file.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git reset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Track the file as a Large Object.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git lfs track &lt;span class=&quot;s2&quot;&gt;&quot;libsmbclient.a&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: Stage, commit and push.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git add &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;track large objects properly&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  git push origin master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;success&quot;&gt;Success!&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Pushing to https://github.com/tddapps/my_secret_app.git
POST git-receive-pack (786 bytes)
To https://github.com/tddapps/my_secret_app.git
 = [up to date]      version_1_0 -&amp;gt; version_1_0
 = [up to date]      version_1_1 -&amp;gt; version_1_1
   8ce3374..e7fb4b8  master -&amp;gt; master
updating local tracking ref 'refs/remotes/github/master'
Uploading LFS objects: 100% (1/1), 120 MB | 1.4 MB/s, done
Completed successfully
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;got-other-errors&quot;&gt;Got other errors?&lt;/h2&gt;

&lt;p&gt;Repeat the steps if the error persists with other files.&lt;/p&gt;

&lt;h2 id=&quot;want-to-know-more&quot;&gt;Want to know more?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://rtyley.github.io/bfg-repo-cleaner/&quot;&gt;BFG Repo Cleaner Website&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/33330771/git-lfs-this-exceeds-githubs-file-size-limit-of-100-00-mb&quot;&gt;Stack Overflow question on the topic&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-lfs.github.com/&quot;&gt;Git LFS Website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Tue, 08 Jan 2019 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2019/01/08/migrating-large-files-to-github/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2019/01/08/migrating-large-files-to-github/</guid>
        </item>
      
    
      
    
      
        <item>
          <title>Serverless API Part 1: From idea to Production</title>
          <description>&lt;p&gt;I implemented a Heartbeat API to detect when any of my servers crash. I had two requirements. Cheap to operate. And disaster tolerance. Serverless technologies are designed to solve these kind of problems. Here are some lessons learned on my first AWS Lambda hands-on experiences.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/2018/10/24/the-heartbeat-pattern/&quot;&gt;This other post&lt;/a&gt; explains the Heartbeat Pattern in depth and the need for it. The project sources are &lt;a href=&quot;https://github.com/camilin87/hb-api&quot;&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-ivory-tower-architect&quot;&gt;The Ivory Tower Architect&lt;/h2&gt;

&lt;p&gt;With very little hands-on knowledge I set my product vision. A highly-scalable distributed system running on standard technologies. I decided the API should be serverless. The technology costs start very low and are driven by usage. AWS is the most mature serverless provider. And Java is a widely supported language.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This would be my first serverless project so I would keep it &lt;em&gt;“simple”&lt;/em&gt; [&lt;em&gt;grin&lt;/em&gt;].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After some &lt;em&gt;“architecting”&lt;/em&gt;&lt;sup id=&quot;fnref:architecture&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:architecture&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; I ended up with this design.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Single Region. AWS has very good availability and regional outages are rare. A future release could support multiple regions.&lt;/li&gt;
  &lt;li&gt;Two &lt;a href=&quot;https://aws.amazon.com/kinesis/data-streams/&quot;&gt;Kinesis streams&lt;/a&gt;. One to store messages as they came. Another stream for the notifications.&lt;/li&gt;
  &lt;li&gt;One &lt;a href=&quot;https://aws.amazon.com/dynamodb/&quot;&gt;DynamoDB&lt;/a&gt; table.&lt;/li&gt;
  &lt;li&gt;Multiple lambdas. For authentication, data persistence and analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/serverless-api/hb-api-0.0.0-alpha.png&quot; alt=&quot;Initial design without any hands on data&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;why-so-complicated&quot;&gt;Why so complicated?&lt;/h3&gt;

&lt;p&gt;In a word: &lt;strong&gt;Ignorance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This initial design had too many redundant components. The project did not need to handle huge loads. Moreover, the minimum operational cost was going to be high.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DynamoDB All the Things&lt;/strong&gt;. I researched pricing of the different AWS services. &lt;a href=&quot;https://aws.amazon.com/kinesis/data-streams/&quot;&gt;Kinesis Streams&lt;/a&gt; cost &lt;em&gt;“as little as $0.015 per hour”&lt;/em&gt;. Almost $11/mo per region. So that had to go away. &lt;a href=&quot;https://aws.amazon.com/dynamodb/&quot;&gt;DynamoDB&lt;/a&gt; would have to solve all the storage needs. This was also my first DynamoDB project. I captured my &lt;a href=&quot;/2018/10/06/thoughts-on-dynamodb/&quot;&gt;thoughts on DynamoDB&lt;/a&gt; in a &lt;a href=&quot;/2018/10/06/thoughts-on-dynamodb/&quot;&gt;separate post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Api Gateway to the Rescue&lt;/strong&gt;. I thought I needed a lambda function for authentication and rate limiting. But &lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-to-api.html&quot;&gt;Api Gateway&lt;/a&gt; has all that functionality built-in. One less thing to worry about.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.twilio.com/&quot;&gt;Twilio&lt;/a&gt; has a nice API to send SMS. But &lt;a href=&quot;https://aws.amazon.com/sns/&quot;&gt;SNS&lt;/a&gt; is also very easy to use. Besides, I would not have to manage a separate set of credentials.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://serverless.com/&quot;&gt;Serverless Framework&lt;/a&gt; and the &lt;a href=&quot;https://github.com/99xt/serverless-dynamodb-local&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serverless-dynamodb-local&lt;/code&gt; plugin&lt;/a&gt; proved to be great resources. They allow local testing, automated deployments, and infrastructure provisioning with code.&lt;/p&gt;

&lt;h2 id=&quot;the-mvp-architecture&quot;&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Minimum_viable_product&quot;&gt;MVP&lt;/a&gt; Architecture&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/camilin87/hb-api/releases/tag/0.1.0&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hb-api&lt;/code&gt; release 0.1.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I started building the application I settled on the following design.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;One lambda to save messages.&lt;/li&gt;
  &lt;li&gt;Another lambda to scan the table for expired hosts and send notifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two functions. With built-in rate limiting, authentication, decent availability, and scalability.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/serverless-api/hb-api-0.1.0.png&quot; alt=&quot;MVP&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;bonus-it-is-always-the-database&quot;&gt;BONUS: It is always the Database&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/camilin87/hb-api/releases/tag/0.1.0&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hb-api&lt;/code&gt; release 0.2.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since a table scan on the DynamoDB table &lt;a href=&quot;/2018/10/06/thoughts-on-dynamodb/&quot;&gt;could have disastrous consequences&lt;/a&gt;. I decided to use DynamoDB’s &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html&quot;&gt;TTL&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html&quot;&gt;Streams&lt;/a&gt; to reduce the database load. DynamoDB would expire messages automatically. Any database change would trigger a lambda. Such lambda would send notifications for deletions. This design used even less computing resources. It solved the Heartbeat Pattern problem leveraging serverless technologies to its maximum.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/serverless-api/hb-api-0.2.0.png&quot; alt=&quot;Reduce DynamoDB Load with TTL&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Building serverless applications was easier than I thought. The AWS toolbox is very rich. And the &lt;a href=&quot;https://serverless.com/&quot;&gt;Serverless Framework&lt;/a&gt; a great help. With very little code I was able to build a secure, easy-to-use, fault-tolerant, scalable, and cheap, service. Yet, the serverless model is a paradigm shift. Traditional operations may no longer be possible. Eventual consistency and fault-tolerance become the new standard. Thus, hands-on experience is highly valuable. Stay tuned for &lt;a href=&quot;/2018/11/05/serverless-api-evolution-part-2/&quot;&gt;Part 2&lt;/a&gt; to make the application tolerant to regional outages. And share the operational costs.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:architecture&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;/2018/10/31/clean-architecture/&quot;&gt;The Clean Architecture&lt;/a&gt; book provides a better approach to architecture &lt;a href=&quot;#fnref:architecture&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Mon, 05 Nov 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/11/05/serverless-api-evolution-part-1/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/11/05/serverless-api-evolution-part-1/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: TDD by Example</title>
          <description>&lt;p&gt;Good books are worth re-reading. I have read &lt;a href=&quot;https://amzn.to/2DhvNbI&quot;&gt;TDD by Example&lt;/a&gt; at least three times. The book has many practical examples. It showcases the &lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;Test Driven Development&lt;/a&gt;(&lt;em&gt;TDD&lt;/em&gt;) discipline in an effortless and funny style. &lt;a href=&quot;https://amzn.to/2DhvNbI&quot;&gt;This book&lt;/a&gt; has advanced my career like no other.&lt;/p&gt;

&lt;h2 id=&quot;the-author&quot;&gt;The Author&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Kent_Beck&quot;&gt;Kent Beck&lt;/a&gt; was one of the creators of the &lt;a href=&quot;http://agilemanifesto.org/&quot;&gt;Agile Manifesto&lt;/a&gt;. He is the creator of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;Test Driven Development&lt;/a&gt; discipline. He has also collaborated in other classics such as &lt;em&gt;&lt;a href=&quot;https://amzn.to/2QeVe0y&quot;&gt;Extreme Programming Explained&lt;/a&gt;&lt;/em&gt; and &lt;em&gt;&lt;a href=&quot;https://amzn.to/2qnw4Bu&quot;&gt;Refactoring&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://amzn.to/2DhvNbI&quot;&gt;&lt;img src=&quot;/images/books/tdd-by-example.png&quot; alt=&quot;TDD by Example&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;part-i-and-ii-examples&quot;&gt;Part I and II: Examples&lt;/h2&gt;

&lt;p&gt;The book solves traditional computing problems with a Test Driven approach. These two sections showcase how to practice the discipline to achieve optimal results. Using static or dynamic languages. Code analysis tools reveal the astounding quality of the produced code. The examples show how the tests can drive the design into more expressive and maintainable solutions.&lt;/p&gt;

&lt;h3 id=&quot;stress&quot;&gt;Stress&lt;/h3&gt;

&lt;p&gt;The book mentions stress many times. Do you recognize any of these?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1&lt;/strong&gt;: &lt;em&gt;“We’re in a hurry we don’t have time to write tests”&lt;/em&gt;. Hurried changes without tests produce bad software. Bad software produces more stress.&lt;br /&gt;
&lt;strong&gt;Mitigation&lt;/strong&gt;: Write more tests. More tests increase the quality of the software. Good software is easy to change and has almost no defects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2&lt;/strong&gt;: &lt;em&gt;“This design will cover all the requirements. It is complex. But perfect.”&lt;/em&gt; Three weeks later. &lt;em&gt;“What do you mean requirements changed? How do I fit that into what I have?”&lt;/em&gt;&lt;br /&gt;
&lt;strong&gt;Mitigation&lt;/strong&gt;: Software always changes. Requirements change. Embrace change. Practice Test Driven Development and change will be easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 3&lt;/strong&gt;: &lt;em&gt;“I have to do these 20 things for this feature. Internationalization?! Now I have to do 21”&lt;/em&gt;. Restroom break. &lt;em&gt;“Where was I in the 15 things to do?”&lt;/em&gt;&lt;br /&gt;
&lt;strong&gt;Mitigation&lt;/strong&gt;: Write down all the things to do. Add new tasks to the list as they come. Work through that list one step at a time. Nothing will ever be forgotten.&lt;/p&gt;

&lt;p&gt;The Test Driven Development discipline is about focusing on one task at a time. The book shows how anything else should be written down in a list and dealt with later. This practice reduces stress levels and increases productivity.&lt;/p&gt;

&lt;h2 id=&quot;part-iii-patterns&quot;&gt;Part III: Patterns&lt;/h2&gt;

&lt;p&gt;The third part of the book is dedicated to design and testing patterns. How to write and design tests. The need for isolated, independent and easy to comprehend tests. How to leverage tests to understand the behavior of dependencies.&lt;/p&gt;

&lt;h3 id=&quot;rapid-feedback&quot;&gt;Rapid Feedback&lt;/h3&gt;

&lt;p&gt;An important benefit of the TDD discipline is the shortening of feedback cycles. Rapid feedback lets you know when you’re moving in the right direction. Large changes need decomposition to achieve rapid feedback. This practice enhances the design.&lt;/p&gt;

&lt;h3 id=&quot;refactoring&quot;&gt;Refactoring&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/images/books/refactor-all-the-things.jpeg&quot; alt=&quot;Refactor all the things&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Design is rarely created in a perfect way. It needs to evolve through countless changes. TDD empowers the constant refactoring flow mandated by changing requirements. &lt;a href=&quot;https://amzn.to/2DhvNbI&quot;&gt;The book&lt;/a&gt; showcases several refactoring patterns. It also covers the need to write tests before refactoring untested code. I recommend &lt;a href=&quot;https://amzn.to/2qnw4Bu&quot;&gt;Refactoring&lt;/a&gt; &lt;em&gt;-by Martin Fowler-&lt;/em&gt; for a deep dive on the topic.&lt;/p&gt;

&lt;h3 id=&quot;mastering-tdd&quot;&gt;Mastering TDD&lt;/h3&gt;
&lt;p&gt;The final chapter contains invaluable information for those learning the discipline.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What don’t you have to test?&lt;/li&gt;
  &lt;li&gt;How do you know if you have good tests?&lt;/li&gt;
  &lt;li&gt;How much feedback do you need?&lt;/li&gt;
  &lt;li&gt;When should you delete tests?&lt;/li&gt;
  &lt;li&gt;Can you test drive large systems?&lt;/li&gt;
  &lt;li&gt;Can you drive development with application-level tests?&lt;/li&gt;
  &lt;li&gt;How do you switch to TDD midstream?&lt;/li&gt;
  &lt;li&gt;Who is TDD intended for?&lt;/li&gt;
  &lt;li&gt;Is TDD sensitive to initial conditions?&lt;/li&gt;
  &lt;li&gt;How does TDD relate to patterns?&lt;/li&gt;
  &lt;li&gt;Why does TDD work?&lt;/li&gt;
  &lt;li&gt;How does TDD relate to the practices of Extreme Programming?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;should-you-read-it&quot;&gt;Should you read it?&lt;/h2&gt;
&lt;p&gt;Definitely. &lt;a href=&quot;https://amzn.to/2DhvNbI&quot;&gt;TDD by Example&lt;/a&gt; will not only help you become a better software engineer. The book explains tools to manage any type of work.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://amzn.to/2DhvNbI&quot;&gt;&lt;img src=&quot;/images/books/tdd-by-example.png&quot; alt=&quot;TDD by Example&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
          <pubDate>Thu, 01 Nov 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/11/01/tdd-by-example/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/11/01/tdd-by-example/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: Clean Architecture</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/ref=as_li_ss_tl?s=books&amp;amp;ie=UTF8&amp;amp;qid=1540987718&amp;amp;sr=1-1&amp;amp;keywords=clean+architecture&amp;amp;dpID=41BjtnvIUQL&amp;amp;preST=_SX218_BO1,204,203,200_QL40_&amp;amp;dpSrc=srch&amp;amp;linkCode=sl1&amp;amp;tag=capr04-20&amp;amp;linkId=4db91ebc7378d50015ecea3400e2c985&amp;amp;language=en_US&quot;&gt;Clean Architecture&lt;/a&gt; can be described in a word: &lt;strong&gt;thorough&lt;/strong&gt;. The book explains the &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID&quot;&gt;SOLID&lt;/a&gt; principles in detail. It walks through the evolution of programming paradigms. How they took away features while maintaining productivity. The book revolves around the idea that business rules are the most important application component. And they should not depend on external details. Full of practical examples. The final chapter on itself &lt;em&gt;-written by Simon Brown-&lt;/em&gt; makes the book worth reading. In summary, &lt;a href=&quot;https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/ref=as_li_ss_tl?s=books&amp;amp;ie=UTF8&amp;amp;qid=1540987718&amp;amp;sr=1-1&amp;amp;keywords=clean+architecture&amp;amp;dpID=41BjtnvIUQL&amp;amp;preST=_SX218_BO1,204,203,200_QL40_&amp;amp;dpSrc=srch&amp;amp;linkCode=sl1&amp;amp;tag=capr04-20&amp;amp;linkId=4db91ebc7378d50015ecea3400e2c985&amp;amp;language=en_US&quot;&gt;Clean Architecture&lt;/a&gt; feels like a nice complement to &lt;a href=&quot;https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=as_li_ss_tl?ie=UTF8&amp;amp;qid=1540987558&amp;amp;sr=8-3&amp;amp;keywords=clean+code&amp;amp;dpID=515iEcDr1GL&amp;amp;preST=_SX258_BO1,204,203,200_QL70_&amp;amp;dpSrc=srch&amp;amp;linkCode=sl1&amp;amp;tag=capr04-20&amp;amp;linkId=bd49e88352bf0a04cc67494877a90ed5&amp;amp;language=en_US&quot;&gt;Clean Code&lt;/a&gt; and &lt;a href=&quot;https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software-dp-0321125215/dp/0321125215/ref=as_li_ss_tl?_encoding=UTF8&amp;amp;me=&amp;amp;qid=1540987661&amp;amp;linkCode=sl1&amp;amp;tag=capr04-20&amp;amp;linkId=a115a41549aa74e6ebb55a3043ccaccb&amp;amp;language=en_US&quot;&gt;Domain Driven Design&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-author&quot;&gt;The Author&lt;/h2&gt;

&lt;p&gt;I have a deep admiration for Robert C. Martin (&lt;em&gt;Uncle Bob&lt;/em&gt;). His advice on &lt;em&gt;&lt;a href=&quot;https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=as_li_ss_tl?ie=UTF8&amp;amp;qid=1540987558&amp;amp;sr=8-3&amp;amp;keywords=clean+code&amp;amp;dpID=515iEcDr1GL&amp;amp;preST=_SX258_BO1,204,203,200_QL70_&amp;amp;dpSrc=srch&amp;amp;linkCode=sl1&amp;amp;tag=capr04-20&amp;amp;linkId=bd49e88352bf0a04cc67494877a90ed5&amp;amp;language=en_US&quot;&gt;Clean Code&lt;/a&gt;&lt;/em&gt; and the &lt;em&gt;&lt;a href=&quot;https://cleancoders.com/&quot;&gt;Clean Coders Videos&lt;/a&gt;&lt;/em&gt; has significantly advanced my career. Familiar readers of his work will feel like they have already read the book. I would go even further and say it could fit in two thirds of the pages.&lt;/p&gt;

&lt;h2 id=&quot;the-stories&quot;&gt;The Stories&lt;/h2&gt;

&lt;p&gt;The author wouldn’t be himself without his stories of ancient computers and perforated cards. This book is no exception. It even has a dedicated appendix for them. Although some of the metaphors and examples may no longer apply. They certainly help explain the book lessons. And provide invaluable practical examples of what otherwise be a very abstract topic.&lt;/p&gt;

&lt;h2 id=&quot;architecture&quot;&gt;Architecture&lt;/h2&gt;

&lt;p&gt;This book challenges the foundational beliefs of many software architects.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The goal of software architecture is to minimize the human resources required to build and maintain the required system&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And yet, this definition of architecture is a great tool to gauge the quality of a project. I particularly liked the explanations around the increased costs of lines of code. The book showcases several architecture quality measurement tools. It also describes testing patterns and their effects on an architecture. e.g. the &lt;a href=&quot;https://www.oreilly.com/library/view/clean-architecture-a/9780134494272/ch23.xhtml&quot;&gt;Humble Object pattern&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;details&quot;&gt;Details&lt;/h2&gt;

&lt;p&gt;The book stresses the need to decouple architecture from details. It covers how GUIs, databases, and frameworks are details. And as such, should not be coupled.&lt;/p&gt;

&lt;p&gt;I agree with that guideline. I’ve worked on several projects with high coupling. Where a button change required a database schema modification.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/ref=as_li_ss_tl?s=books&amp;amp;ie=UTF8&amp;amp;qid=1540987718&amp;amp;sr=1-1&amp;amp;keywords=clean+architecture&amp;amp;dpID=41BjtnvIUQL&amp;amp;preST=_SX218_BO1,204,203,200_QL40_&amp;amp;dpSrc=srch&amp;amp;linkCode=sl1&amp;amp;tag=capr04-20&amp;amp;linkId=4db91ebc7378d50015ecea3400e2c985&amp;amp;language=en_US&quot;&gt;&lt;img src=&quot;/images/books/clean-architecture.jpeg&quot; alt=&quot;Clean Architecture&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;should-you-read-it&quot;&gt;Should you read it?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/ref=as_li_ss_tl?s=books&amp;amp;ie=UTF8&amp;amp;qid=1540987718&amp;amp;sr=1-1&amp;amp;keywords=clean+architecture&amp;amp;dpID=41BjtnvIUQL&amp;amp;preST=_SX218_BO1,204,203,200_QL40_&amp;amp;dpSrc=srch&amp;amp;linkCode=sl1&amp;amp;tag=capr04-20&amp;amp;linkId=4db91ebc7378d50015ecea3400e2c985&amp;amp;language=en_US&quot;&gt;Clean Architecture&lt;/a&gt; has good guidelines to design maintainable software. This book can be a good introduction for the &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID&quot;&gt;SOLID&lt;/a&gt; principles. At worst it is a history of computer evolution. Definitely, worth the read.&lt;/p&gt;
</description>
          <pubDate>Wed, 31 Oct 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/10/31/clean-architecture/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/10/31/clean-architecture/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: Rich Dad Poor Dad</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://amzn.to/2D7yegW&quot;&gt;Rich Dad Poor Dad&lt;/a&gt; is a popular personal finance book. I don’t understand why. The entire book can be summarized in one page. It duplicates entire sections. The book provides no real advice on how to become rich. It even gives outright bad financial advice. Don’t waste your time. Read &lt;a href=&quot;https://amzn.to/2ywALxm&quot;&gt;A Random Walk down Wall Street&lt;/a&gt; and &lt;a href=&quot;https://amzn.to/2qc0t5R&quot;&gt;The Little Book of Common Sense Investing&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://amzn.to/2D7yegW&quot;&gt;&lt;img src=&quot;/images/books/rich-dad-poor-dad.png&quot; alt=&quot;Rich Dad Poor Dad&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;good&quot;&gt;Good&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Highlights the need for financial education.&lt;/li&gt;
  &lt;li&gt;Spend money on cash producing assets.&lt;/li&gt;
  &lt;li&gt;Don’t spend money on liabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;bad&quot;&gt;Bad&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Erodes the value of traditional education.&lt;/li&gt;
  &lt;li&gt;Gives very bad advice on paying debts.&lt;/li&gt;
  &lt;li&gt;Does not provide clear tools for financial independence.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Fri, 26 Oct 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/10/26/rich-dad-poor-dad/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/10/26/rich-dad-poor-dad/</guid>
        </item>
      
    
      
        <item>
          <title>The Heartbeat Pattern</title>
          <description>&lt;h2 id=&quot;which-computers-in-a-global-network-are-offline&quot;&gt;Which computers in a global network are offline?&lt;/h2&gt;

&lt;p&gt;Restaurant chains rely on thousands of computers to conduct business. Think of every credit card machine in every McDonald’s in the world. Any of those computers could stop working without warning. Connectivity outages, power losses and hardware failures are common problems. Knowing when a machine disappears is not easy. Headquarters cannot ping machines through thousands of different private networks. In-store personnel may not notice a lack of connectivity for hours. Failures can even happen when the store is empty with no human around.&lt;/p&gt;

&lt;h2 id=&quot;how-do-scientists-know-black-holes-exist&quot;&gt;How do scientists know black holes exist?&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/images/heartbeat/black-hole.jpg&quot; alt=&quot;Black Hole&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Black holes are unobservable &lt;em&gt;-by definition-&lt;/em&gt;. And yet, scientists can know their location. &lt;sup id=&quot;fnref:black_holes&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:black_holes&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;tracking-transactions&quot;&gt;Tracking transactions&lt;/h2&gt;

&lt;p&gt;Computers in a global network are like black holes. They cannot be observed. But their effects can be measured.&lt;/p&gt;

&lt;p&gt;A computer processing sales &lt;strong&gt;is&lt;/strong&gt; online. The system receiving those transactions can know which computers are online.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Are computers without recent transactions offline?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A lack of transactions does not mean there is a problem. Some markets may have low sales periods. Stores in different timezones have different activity hours. Some retailers may process a very small daily volume.&lt;/p&gt;

&lt;h2 id=&quot;heart-beats&quot;&gt;Heart beats&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/images/heartbeat/ekg.png&quot; alt=&quot;HeartBeat&quot; /&gt;&lt;/p&gt;

&lt;p&gt;An average human heart beats approximately 60 times per minute. A lack of heartbeats indicates a serious health problem. That is why &lt;a href=&quot;https://en.wikipedia.org/wiki/Intensive_care_unit&quot;&gt;ICUs&lt;/a&gt; watch them closely.&lt;/p&gt;

&lt;h2 id=&quot;heartbeat-pattern&quot;&gt;Heartbeat Pattern&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What if every computer in the network sends a small test message every minute?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A system receiving these messages can know which computers are online.&lt;br /&gt;
An absence of periodic messages indicates a problem.&lt;br /&gt;
Alerts can be sent. And the appropriate personnel can react. Sometimes before there is any business impact.&lt;/p&gt;

&lt;h2 id=&quot;advantages&quot;&gt;Advantages&lt;/h2&gt;
&lt;p&gt;The Heartbeat Pattern solves a critical business problem. With the simplicity of a recurrent test message. It allows headquarters to assess the status of a global network. It can be the foundation to operational intelligence and proactive problem remediation.&lt;/p&gt;

&lt;p&gt;This pattern can even be adapted to track other indicators.  Complex clients can record internet speed, computer temperature, or last user interaction.&lt;/p&gt;

&lt;h2 id=&quot;reference-architecture&quot;&gt;Reference Architecture&lt;/h2&gt;

&lt;p&gt;The Heartbeat pattern can be implemented with small effort.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/heartbeat/heartbeat-sample-architecture.png&quot; alt=&quot;Heartbeat Sample Architecture&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;client&quot;&gt;Client&lt;/h3&gt;
&lt;p&gt;Runs recurrent &lt;a href=&quot;https://curl.haxx.se/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt;&lt;/a&gt; commands to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; HTTP requests to an API. The message payload will be a unique computer identifier.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; curl &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;x-api-key: SECRET&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{&quot;hostId&quot;: &quot;prod-host-1&quot;}'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST https://api.retailer.com/v1/hearbeat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Sample &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crontab&lt;/code&gt; file for a computer sending heartbeats&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is no need to write any custom code. Major operating systems support recurrent tasks. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; runs on any platform.&lt;/p&gt;

&lt;h3 id=&quot;api&quot;&gt;API&lt;/h3&gt;
&lt;p&gt;Stores the unique identifiers and the current time in a single table.&lt;/p&gt;

&lt;h3 id=&quot;alerts-recurrent-task&quot;&gt;Alerts Recurrent Task&lt;/h3&gt;
&lt;p&gt;Reads all the items in the table.&lt;br /&gt;
Considers any computer that hasn’t sent a recent message to be offline.&lt;br /&gt;
Triggers the appropriate alerts for each offline computer.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:black_holes&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Read &lt;a href=&quot;https://amzn.to/2SfvRx6&quot;&gt;A Brief History of Time by Stephen Hawking&lt;/a&gt; to learn more about black holes &lt;a href=&quot;#fnref:black_holes&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Wed, 24 Oct 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/10/24/the-heartbeat-pattern/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/10/24/the-heartbeat-pattern/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: Creative Selection</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://amzn.to/2pGXKRq&quot;&gt;Creative Selection&lt;/a&gt; describes Apple’s product development process under &lt;a href=&quot;https://en.wikipedia.org/wiki/Scott_Forstall&quot;&gt;Scott Forstall&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Steve_Jobs&quot;&gt;Steve Jobs&lt;/a&gt;. Written from the perspective of a software engineer. &lt;a href=&quot;https://amzn.to/2pGXKRq&quot;&gt;The book&lt;/a&gt; describes frequent product demos as they progress through the company’s ranks. This process is what &lt;a href=&quot;https://amzn.to/2NuMFN4&quot;&gt;Ken Kocienda&lt;/a&gt; calls &lt;em&gt;Creative Selection&lt;/em&gt;. And what makes Apple products not successful accidents but deliberate successes. The demos help discuss ideas in concrete terms. And reduce the procrastination associated with imaginary problems and situations.&lt;/p&gt;

&lt;h2 id=&quot;a-software-engineer&quot;&gt;A software engineer&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://amzn.to/2NuMFN4&quot;&gt;Ken Kocienda&lt;/a&gt; accurately depicts what being a software engineer feels like:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The pressure to meet deadlines.&lt;/li&gt;
  &lt;li&gt;How annoying bugs are.&lt;/li&gt;
  &lt;li&gt;How weeks of efforts can get scrapped.&lt;/li&gt;
  &lt;li&gt;How valuable feedback can be delivered in suboptimal ways.&lt;/li&gt;
  &lt;li&gt;How being a manager is not the logical progression of being an engineer.&lt;/li&gt;
  &lt;li&gt;The dread for meetings.&lt;/li&gt;
  &lt;li&gt;How programming cannot solve a people problem. But people can always solve programming problems.&lt;/li&gt;
  &lt;li&gt;The pride of building a widely used product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;He does a good job explaining hard engineering problems with simple metaphors.&lt;/p&gt;

&lt;h2 id=&quot;beware-of-doing-things-the-apple-way&quot;&gt;Beware of doing things the Apple way&lt;/h2&gt;

&lt;p&gt;While Apple has its reasons to follow certain practices, chances are your company is not Apple.&lt;/p&gt;

&lt;h3 id=&quot;collaboration-and-secrecy&quot;&gt;Collaboration and Secrecy&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://amzn.to/2pGXKRq&quot;&gt;Creative Selection&lt;/a&gt; covers with many examples the strong secrecy ambient around product development. Apple employees had multiple NDA contracts. The author mentions how collaboration is fundamental to create great products. But many examples in the book show otherwise. Product features are generally built by a single person. Individuals can only ask for help to experts in other areas of the company as long as they don’t know what the help is for.&lt;/p&gt;

&lt;h3 id=&quot;team-size&quot;&gt;Team size&lt;/h3&gt;

&lt;p&gt;Small teams with direct access to executives in a fairly flat organizational structure.&lt;/p&gt;

&lt;h3 id=&quot;planning&quot;&gt;Planning&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;Whiteboard discussions feel like work, but often they’re not, since it is too difficult to talk about ideas in the abstract.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;convergence&quot;&gt;Convergence&lt;/h3&gt;

&lt;p&gt;A long QA cycle before product releases. Where the bug count does not decrease before an imminent release date.&lt;/p&gt;

&lt;h3 id=&quot;patents&quot;&gt;Patents&lt;/h3&gt;

&lt;p&gt;The author name figures in several Apple patents.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;The practices described in the book have changed over the last years&lt;sup id=&quot;fnref:change&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:change&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Yet, this book is a firsthand account of the development of products that would end up changing the world. At worst it is an interesting history read.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://amzn.to/2pGXKRq&quot;&gt;&lt;img src=&quot;/images/books/creative-selection.png&quot; alt=&quot;Creative Selection&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:change&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The author mentions this change is one of the reasons he left Apple. &lt;a href=&quot;#fnref:change&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Mon, 08 Oct 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/10/08/creative-selection/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/10/08/creative-selection/</guid>
        </item>
      
    
      
        <item>
          <title>Thoughts on DynamoDB</title>
          <description>&lt;p&gt;I recently built a &lt;a href=&quot;https://github.com/camilin87/hb-api&quot;&gt;Serverless Project&lt;/a&gt; that used &lt;a href=&quot;https://aws.amazon.com/dynamodb/&quot;&gt;DynamoDB&lt;/a&gt;. Learning to use a &lt;a href=&quot;https://en.wikipedia.org/wiki/CAP_theorem&quot;&gt;distributed NoSQL database&lt;/a&gt; is never a simple task. DynamoDB makes a strong emphasis on being intentional with the data read or written. It limits almost every operation. When limits are reached, services shut down or more capacity is added at an increased cost. These are some of the most important takeaways.&lt;/p&gt;

&lt;h2 id=&quot;good&quot;&gt;Good&lt;/h2&gt;
&lt;p&gt;DynamoDB has three features that save a considerable amount of work. Entire companies have been built around tools that [&lt;em&gt;poorly&lt;/em&gt;] solved these problems.&lt;/p&gt;

&lt;h3 id=&quot;global-tables&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/dynamodb/global-tables/&quot;&gt;Global Tables&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Write data in one &lt;a href=&quot;https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html&quot;&gt;AWS Region&lt;/a&gt; and the changes propagate to other regions. No need to write replication code. Consider the alternative of managing your own replication strategy.&lt;/p&gt;

&lt;h3 id=&quot;streams&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html&quot;&gt;Streams&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run custom code whenever any record changes. Such code can be unit tested and can be as decoupled from your product as you want it to be. Streams don’t impact the performance of the live database. &lt;sup id=&quot;fnref:streams&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:streams&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h3 id=&quot;time-to-live-ttl&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html&quot;&gt;Time to Live (TTL)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Expire old data automatically.&lt;/p&gt;

&lt;h3 id=&quot;scalability&quot;&gt;Scalability&lt;/h3&gt;
&lt;p&gt;The capacity to scale DynamoDB is [&lt;em&gt;mostly&lt;/em&gt;] limited by money. Configure &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.html&quot;&gt;Auto Scaling&lt;/a&gt; to handle variable loads and &lt;a href=&quot;https://aws.amazon.com/dynamodb/dax/&quot;&gt;DAX&lt;/a&gt; to speed up reads. No pages, no manual intervention, scalability becomes a non-event.&lt;/p&gt;

&lt;h2 id=&quot;bad&quot;&gt;Bad&lt;/h2&gt;
&lt;p&gt;Performance and scalability come with a cost. Specially when reading data.&lt;/p&gt;

&lt;h3 id=&quot;queries&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html&quot;&gt;Queries&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;DynamoDB has no easy way to answer this question:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Which products are more expensive than $1000?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;: Queries need an equality comparison on the &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey&quot;&gt;Partition key&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;indexes&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html&quot;&gt;Indexes&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Indexes are nothing more than managed table clones with different keys. They cost almost the same as the original table.&lt;/p&gt;

&lt;h3 id=&quot;limits-everywhere&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html&quot;&gt;Limits Everywhere&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Want to know which products are more expensive than $1000? You’re gonna need a &lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html&quot;&gt;Scan&lt;/a&gt; to read every item in the table. This will not only cost you, but it may also take down your service.&lt;/li&gt;
  &lt;li&gt;Want to run a common bulk operation such as “Delete all accounts from the EU”? Be prepared to write code to stagger the deletions or your service may go down.&lt;/li&gt;
  &lt;li&gt;Want to read all the orders for a customer? Consider limiting how many you read.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/database/choosing-the-right-dynamodb-partition-key/&quot;&gt;Proper Key selection&lt;/a&gt; is fundamental. Or your service may go partially down during high traffic periods.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;time-to-live-ttl-1&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html&quot;&gt;Time to Live (TTL)&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The deletion of data is not too responsive [&lt;em&gt;by design&lt;/em&gt;]. In my experience TTL deletes items ten minutes late.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;DynamoDB is a managed globally available service constrained by cost. It has similar problems to other comparable databases. It should be used under the right circumstances [&lt;em&gt;like any other tool&lt;/em&gt;].&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:streams&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Streams are like &lt;a href=&quot;https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-2017&quot;&gt;SQL triggers&lt;/a&gt;. Only better [&lt;em&gt;grin&lt;/em&gt;] &lt;a href=&quot;#fnref:streams&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Sat, 06 Oct 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/10/06/thoughts-on-dynamodb/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/10/06/thoughts-on-dynamodb/</guid>
        </item>
      
    
      
        <item>
          <title>Development Team Evaluation</title>
          <description>&lt;p&gt;Development teams have similarities and differences. These are my questions to gauge the maturity of a team based on certain parameters: Agility, Code Quality, Operational Integrity. Most of the questions don’t have a single correct answer but a spectrum of possible answers. While far from being comprehensive this test can be useful when comparing potential employers and clients.&lt;/p&gt;

&lt;h1 id=&quot;questions&quot;&gt;Questions&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;What is your source control branching strategy?&lt;/li&gt;
  &lt;li&gt;How do you know what is your team working on?
    &lt;ul&gt;
      &lt;li&gt;What needs to be done?&lt;/li&gt;
      &lt;li&gt;What has already been completed?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;How do you assign new work?&lt;/li&gt;
  &lt;li&gt;How long does it take you to go from an idea into something your customers can use?&lt;/li&gt;
  &lt;li&gt;How frequently do you do deployments?&lt;/li&gt;
  &lt;li&gt;How many bugs have you had in the last quarter?
    &lt;ul&gt;
      &lt;li&gt;How many of those were regressions?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;What sort of testing do you do?&lt;/li&gt;
  &lt;li&gt;When was the last time you reviewed the vulnerabilities of your system?&lt;/li&gt;
  &lt;li&gt;Do you know whom to contact in case there’s a problem?&lt;/li&gt;
  &lt;li&gt;When was your last production incident?
    &lt;ul&gt;
      &lt;li&gt;Did you resolve it?&lt;/li&gt;
      &lt;li&gt;How long did it took to get resolved?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Thu, 31 May 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/05/31/development-team-evaluation/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/05/31/development-team-evaluation/</guid>
        </item>
      
    
      
        <item>
          <title>Development Team Evaluation Explained</title>
          <description>&lt;p&gt;Explanation for the &lt;a href=&quot;/2018/05/31/development-team-evaluation/&quot;&gt;Development Team Evaluation&lt;/a&gt; questions.&lt;/p&gt;

&lt;h2 id=&quot;what-is-your-source-control-branching-strategy&quot;&gt;What is your source control branching strategy?&lt;/h2&gt;
&lt;p&gt;This is the first question for a very good reason. If the answer is &lt;em&gt;“we don’t have source control”&lt;/em&gt; you better run for your life.&lt;br /&gt;
Widely adopted patterns such as feature branches or single branch development are generally a sign of maturity.&lt;br /&gt;
A team struggling with their branching strategy is clearly in need of technical expertise.&lt;/p&gt;

&lt;h2 id=&quot;how-do-you-know-what-is-your-team-working-on&quot;&gt;How do you know what is your team working on?&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;What needs to be done?&lt;br /&gt;
What has already been completed?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mature teams actively track their work &lt;em&gt;-generally with an &lt;a href=&quot;https://en.wikipedia.org/wiki/Issue_tracking_system&quot;&gt;Issue Tracking software&lt;/a&gt;-&lt;/em&gt;.&lt;br /&gt;
This practice increases productivity. Problems arise when work is not tracked: features are forgotten, employees burn out, chaos reigns.&lt;/p&gt;

&lt;h2 id=&quot;how-do-you-assign-new-work&quot;&gt;How do you assign new work?&lt;/h2&gt;
&lt;p&gt;Sharing is caring. Agile teams with strong engineering practices share responsibilities across their members. No single engineer is responsible for a single product. Multiple engineers collaborate on the same task. This collaboration reduces silos and increases the &lt;a href=&quot;https://en.wikipedia.org/wiki/Bus_factor&quot;&gt;Bus Factor&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-long-does-it-take-you-to-go-from-an-idea-into-something-your-customers-can-use&quot;&gt;How long does it take you to go from an idea into something your customers can use?&lt;/h2&gt;
&lt;p&gt;There is not a right answer here. Feature sizes can greatly vary &lt;em&gt;even&lt;/em&gt; on the same team.&lt;br /&gt;
Shorter cycle times indicate good organizational health.&lt;/p&gt;

&lt;h2 id=&quot;how-frequently-do-you-do-deployments&quot;&gt;How frequently do you do deployments?&lt;/h2&gt;
&lt;p&gt;Frequent deployments indicate a high level of confidence in the release and monitoring process.&lt;/p&gt;

&lt;h2 id=&quot;how-many-bugs-have-you-had-in-the-last-quarter&quot;&gt;How many bugs have you had in the last quarter?&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;How many of those were regressions?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A mature team will rarely have bugs and &lt;em&gt;NEVER&lt;/em&gt; will have regressions.&lt;br /&gt;
The absence of bugs indicates strong engineering practices.&lt;/p&gt;

&lt;h2 id=&quot;what-sort-of-testing-do-you-do&quot;&gt;What sort of testing do you do?&lt;/h2&gt;
&lt;p&gt;There is no correct answer for this question either. Mature teams will rely heavily on automated unit tests.&lt;br /&gt;
Avoid places that see no value in tests.&lt;/p&gt;

&lt;h2 id=&quot;when-was-the-last-time-you-reviewed-the-vulnerabilities-of-your-system&quot;&gt;When was the last time you reviewed the vulnerabilities of your system?&lt;/h2&gt;
&lt;p&gt;Security is important for any product in any organization. Mature teams understand this and incorporate security oriented practices into their software development lifecycle.&lt;/p&gt;

&lt;h2 id=&quot;do-you-know-whom-to-contact-in-case-theres-a-problem&quot;&gt;Do you know whom to contact in case there’s a problem?&lt;/h2&gt;
&lt;p&gt;A team with strong Operational Integrity will have clearly defined policies and SLAs around incident responses.&lt;/p&gt;

&lt;h2 id=&quot;when-was-your-last-production-incident&quot;&gt;When was your last production incident?&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;Did you resolve it?&lt;br /&gt;
How long did it took to get resolved?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A mature team will rarely have production incidents. If there is any sort of problem it will get resolved immediately.&lt;/p&gt;
</description>
          <pubDate>Thu, 31 May 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/05/31/development-team-evaluation-explained/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/05/31/development-team-evaluation-explained/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: The Non-Designers's Design Book</title>
          <description>&lt;p&gt;Have you ever looked at a flier and felt that it was ugly? Or at an Apple billboard and felt that it was pretty? Do you feel like you can differentiate a good design from a bad one without knowing why? &lt;a href=&quot;http://amzn.to/2tn0ybe&quot;&gt;Then this book is for you&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2tn0ybe&quot;&gt;&lt;img src=&quot;/images/books/non-designers-design-book.jpg&quot; alt=&quot;The Non Designer's Design Book&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2tn0ybe&quot;&gt;The Non Designer’s Design Book&lt;/a&gt; is structured around the premise that once you can identify a handful of design elements you’ll be able to determine what is wrong with a design, and consequently correct it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Robin_Williams_(writer)&quot;&gt;Robin Williams&lt;/a&gt; did a great job conveying design expertise with the printed edition. The paper quality, the typography the colors, are just superb. &lt;strong&gt;How do I know?&lt;/strong&gt; I read the book. &lt;em&gt;[Grin]&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The book teaches through hundreds of good and bad design examples. That being said, the author recognizes it is far from being a substitute to four years of design school.&lt;/p&gt;

&lt;h2 id=&quot;part-1-design-principles&quot;&gt;Part 1: Design principles&lt;/h2&gt;
&lt;p&gt;Truly eye opening. This first part of the book covers in great detail the basic design principles: Contrast, Repetition, Alignment, Proximity, and Color. This section is sprinkled with multiple quizzes.&lt;/p&gt;

&lt;h2 id=&quot;part-2-types&quot;&gt;Part 2: Types&lt;/h2&gt;
&lt;p&gt;The author can’t deny her passion for typefaces. She covers in great detail the Categories and Relationships between typefaces. Practical examples of what to use when and where are abundant. As well as an extensive typeface reference.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;case-study-tddappscom-redesign&quot;&gt;Case study: &lt;a href=&quot;/&quot;&gt;tddapps.com&lt;/a&gt; redesign&lt;/h2&gt;

&lt;p&gt;Being a software engineer by trade, my design expertise is limited to install &lt;a href=&quot;https://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt;. After reading &lt;a href=&quot;http://amzn.to/2tn0ybe&quot;&gt;The Non Designer’s Design Book&lt;/a&gt; I was able to redesign this blog with minimal effort.&lt;/p&gt;

&lt;h3 id=&quot;before&quot;&gt;Before&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog-redesign/before.png&quot; alt=&quot;Website Before&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;after&quot;&gt;After&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog-redesign/after.png&quot; alt=&quot;Website After&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;not-impressed-with-the-changes&quot;&gt;Not impressed with the changes?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Alignment&lt;/strong&gt;: Everything in the updated version is aligned to the left. This makes the design dramatically better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contrast&lt;/strong&gt;: Article titles are clearly distinguishable. They have a different font size and color from the article summaries. The website name can be clearly differentiated as well as the newsletter signup form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proximity&lt;/strong&gt;: Although there is a considerable amount of whitespace between articles, the summaries are close to their respective titles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repetition&lt;/strong&gt;: Every article title has the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; prefix.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Design is everywhere. Being able to identify good and bad designs can be a powerful skill for anyone. &lt;a href=&quot;http://amzn.to/2tn0ybe&quot;&gt;The Non Designer’s Design Book&lt;/a&gt; is a concise and easy-to-read book with many practical lessons.&lt;/p&gt;
</description>
          <pubDate>Tue, 06 Mar 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/03/06/the-non-designers-design-book/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/03/06/the-non-designers-design-book/</guid>
        </item>
      
    
      
        <item>
          <title>Seamlessly debug Entity Framework Core SQL commands</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/ef/core/&quot;&gt;Entity Framework Core&lt;/a&gt; &lt;em&gt;(EF Core)&lt;/em&gt; is a lightweight &lt;a href=&quot;https://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt; for &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/&quot;&gt;.NET Core&lt;/a&gt;. It is a complete rewrite that maintains most of the functionality of &lt;a href=&quot;https://docs.microsoft.com/en-us/ef/ef6/index&quot;&gt;Entity Framework&lt;/a&gt;. EF Core brings some exciting new features such as multi-platform support. And it also misses capabilities such as the ability to seamlessly debug the generated SQL. The EF Core team is tracking &lt;a href=&quot;https://github.com/aspnet/EntityFrameworkCore/issues/6482&quot;&gt;this issue&lt;/a&gt;. In the meantime, here’s my solution to the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;a href=&quot;https://github.com/camilin87/debug-ef-core&quot;&gt;Use my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DebugEFCore&lt;/code&gt; nuget package&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;before-entity-framework&quot;&gt;Before: Entity Framework&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/ef/ef6/index&quot;&gt;Entity Framework&lt;/a&gt; provides a convenient way to &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dn469464%28v=vs.113%29.aspx&quot;&gt;debug the database commands being executed&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SampleContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Log&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;after-ef-core&quot;&gt;After: EF Core&lt;/h2&gt;

&lt;p&gt;I created the &lt;a href=&quot;https://github.com/camilin87/debug-ef-core&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DebugEFCore&lt;/code&gt; nuget package&lt;/a&gt; to achieve the same behavior.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DebugEFCore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SampleContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DbContext&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnConfiguring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DbContextOptionsBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optionsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;debugLoggingEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;optionsBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;EnableLogging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debugLoggingEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;how-does-it-work&quot;&gt;How does it work?&lt;/h2&gt;

&lt;p&gt;EF Core has some built-in capabilities around logging that potentially allow the debugging of the executed SQL commands. This debugging doesn’t come without a fair share of effort and knowledge. The &lt;a href=&quot;https://github.com/camilin87/debug-ef-core&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DebugEFCore&lt;/code&gt; nuget&lt;/a&gt; abstracts away the complexities of dealing with the EF Core internals.&lt;/p&gt;

&lt;h3 id=&quot;existing-ef-core-infrastructure&quot;&gt;Existing EF Core infrastructure&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DbContext&lt;/code&gt; class provides a virtual &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.onconfiguring?view=efcore-2.0&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnConfiguring&lt;/code&gt;&lt;/a&gt; method that gets invoked for every context instance.&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnConfiguring&lt;/code&gt; method receives a &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontextoptionsbuilder?view=efcore-2.0&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DbContextOptionsBuilder&lt;/code&gt;&lt;/a&gt; parameter.&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DbContextOptionsBuilder&lt;/code&gt; class has a &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontextoptionsbuilder.useloggerfactory?view=efcore-2.0#Microsoft_EntityFrameworkCore_DbContextOptionsBuilder_UseLoggerFactory_Microsoft_Extensions_Logging_ILoggerFactory_&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseLoggerFactory(ILoggerFactory)&lt;/code&gt;&lt;/a&gt; method to configure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DbContext&lt;/code&gt; logging.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;debugefcore-internals&quot;&gt;&lt;a href=&quot;https://github.com/camilin87/debug-ef-core&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DebugEFCore&lt;/code&gt;&lt;/a&gt; internals&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Create an implementation of the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/Microsoft.Extensions.Logging.ILoggerFactory?view=aspnetcore-2.0&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ILoggerFactory&lt;/code&gt;&lt;/a&gt; interface. This implementation will proxy every log message it receives to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log4net&lt;/code&gt; logger. &lt;a href=&quot;https://github.com/camilin87/debug-ef-core/blob/master/DataContextLoggerProvider.cs&quot;&gt;Detailed Source Code&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;The nuget adds an extension method &lt;a href=&quot;https://github.com/camilin87/debug-ef-core/blob/master/DbContextOptionsBuilderExtensions.cs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnableLogging&lt;/code&gt;&lt;/a&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DbContextOptionsBuilder&lt;/code&gt; class. This method will call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DbContextOptionsBuilder.UseLoggerFactory&lt;/code&gt; only when logging is necessary.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;faqs&quot;&gt;FAQs&lt;/h2&gt;

&lt;h3 id=&quot;why-debug-the-sql-code-an-orm-produces&quot;&gt;Why debug the SQL code an ORM produces?&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORMs&lt;/a&gt; abstract away many of the complexities of dealing with databases. They streamline the development process and help engineers focus on the task at hand.&lt;/p&gt;

&lt;p&gt;Many software projects work for years without encountering performance problems. Others are not so lucky: processes start to take longer, applications crash randomly, the same code doesn’t work in different environments.&lt;/p&gt;

&lt;p&gt;The database is usually a smoking gun during performance issues. The problem with ORMs is that they abstract away the database interactions. A minor change in the ORM usage could ripple into major database performance issues. In times like these is necessary to debug the generated SQL code in order to effectively tweak the ORM usage.&lt;/p&gt;

&lt;h2 id=&quot;are-orms-bad&quot;&gt;Are ORMs bad?&lt;/h2&gt;

&lt;p&gt;No. They are just tools. &lt;a href=&quot;https://martinfowler.com/bliki/OrmHate.html&quot;&gt;Martin Fowler’s views on the subject&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;should-i-write-my-own-sql-commands&quot;&gt;Should I write my own SQL commands?&lt;/h2&gt;

&lt;p&gt;No. Replicating an ORM feature set is a gargantuan task. &lt;a href=&quot;https://stackoverflow.com/questions/494816/using-an-orm-or-plain-sql&quot;&gt;Similar Stack Overflow question&lt;/a&gt;.&lt;/p&gt;
</description>
          <pubDate>Tue, 06 Mar 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/03/06/seamlessly-debug-entity-framework-core-sql-commands/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/03/06/seamlessly-debug-entity-framework-core-sql-commands/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: Agile Web Development with Rails 5.1</title>
          <description>&lt;p&gt;&lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt; &lt;em&gt;(Rails)&lt;/em&gt; is a web MVC framework used by a diverse audience ranging from students to large companies with hundreds of employees. &lt;a href=&quot;http://amzn.to/2tfg8FN&quot;&gt;Agile Web Development with Rails 5.1&lt;/a&gt; does a great job closing any knowledge gap for those with a &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;Rails is strongly opinionated. It favors a &lt;a href=&quot;http://rubyonrails.org/doctrine/#convention-over-configuration&quot;&gt;Convention over Configuration&lt;/a&gt; approach to most problems. I think that is why it provokes very different emotions on those who use it. Some people love it while others hate it.&lt;/p&gt;

&lt;p&gt;I’ve noticed very few Rails developers take the time to truly understand how a Rails application works. These lack of understanding can be disastrous for web projects: wiped out databases, unmaintainable code, new team members can’t get the app running.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2tfg8FN&quot;&gt;Agile Web Development with Rails 5.1&lt;/a&gt; is a great book for anybody with a Rails app. It showcases the majority of the framework components in a very easy to read style.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2F61g1h&quot;&gt;&lt;img src=&quot;/images/books/agile-web-development-rails.jpg&quot; alt=&quot;Agile Web Development with Rails 5.1&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;part-1-starting-with-rails&quot;&gt;Part 1: Starting with Rails&lt;/h3&gt;
&lt;p&gt;Covers how to create a new application, how to deploy it, as well as some &lt;a href=&quot;https://www.ruby-lang.org/&quot;&gt;Ruby&lt;/a&gt; introduction.&lt;/p&gt;

&lt;h3 id=&quot;part-2-building-a-web-store-from-scratch&quot;&gt;Part 2: Building a web store from scratch&lt;/h3&gt;
&lt;p&gt;My favorite part of the book. With very little code, the authors built a fully functional web store. They even wrote unit tests for every component.&lt;/p&gt;

&lt;p&gt;This part of the book teaches how to name things and where to place them to leverage the full potential of the framework.&lt;/p&gt;

&lt;h3 id=&quot;part-3-deep-dive-into-rails&quot;&gt;Part 3: Deep dive into Rails&lt;/h3&gt;
&lt;p&gt;In depth coverage of topics such as &lt;a href=&quot;http://guides.rubyonrails.org/active_record_basics.html&quot;&gt;Active Record&lt;/a&gt;, migrations, routing, replacing Rails components.&lt;/p&gt;
</description>
          <pubDate>Sun, 04 Mar 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/03/04/agile-web-development-with-rails-5-1/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/03/04/agile-web-development-with-rails-5-1/</guid>
        </item>
      
    
      
        <item>
          <title>Lazy properties without compromises in C#</title>
          <description>&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Lazy_initialization&quot;&gt;Lazy pattern&lt;/a&gt; is very helpful and widely adopted. However, most of the times its usage comes with compromises: the code looks ugly, it is not thread-safe, locks everywhere. The book &lt;a href=&quot;/2018/02/27/functional-programming-in-java/&quot;&gt;Functional Programming in Java&lt;/a&gt; contains a great implementation of the Lazy pattern. This post is my attempt to explain the C# implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;a href=&quot;https://github.com/camilin87/lazy-property-helper&quot;&gt;Use my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LazyPropertyHelper&lt;/code&gt; nuget package&lt;/a&gt; for lazy properties. Read &lt;a href=&quot;http://amzn.to/2F61g1h&quot;&gt;Functional Programming in Java&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These are two common attempts to implement the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lazy_initialization&quot;&gt;Lazy pattern&lt;/a&gt;. Each has its own benefits and drawbacks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All code samples &lt;a href=&quot;https://github.com/camilin87/ThreadSafeEfficientLazyProperty&quot;&gt;can be found here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;attempt-1-being-lazy-and-naive&quot;&gt;Attempt 1: Being Lazy and naive&lt;/h2&gt;

&lt;p&gt;Throughout my career I’ve written code like this. The code performs an expensive computation only when needed and caches the result to prevent unnecessary computations.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyServiceNaive&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveLoad&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExpensiveObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code can be subject to weird race-conditions in a multi-threaded environment.&lt;/p&gt;

&lt;h2 id=&quot;attempt-2-being-lazy-and-inefficient&quot;&gt;Attempt 2: Being Lazy and inefficient&lt;/h2&gt;

&lt;p&gt;The next approach is much better in terms of thread-safety. The expensive computation is executed only once.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyLockedService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_criticalSection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveLoad&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_criticalSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExpensiveObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expensiveLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code has one major drawback: the lock gets acquired on every read. This behavior can greatly impact performance of multi-threaded applications.&lt;/p&gt;

&lt;h2 id=&quot;lazy-properties-without-compromises&quot;&gt;Lazy properties without compromises&lt;/h2&gt;

&lt;p&gt;Wouldn’t it be nice if the two previous solutions could be merged? Implement a thread-safe lazy pattern where the lock is acquired only once, and subsequent reads are fast.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyAwesomeService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_criticalSection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expensiveLoadReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MyAwesomeService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expensiveLoadReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createAndCacheExpensiveLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveLoad&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_expensiveLoadReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExpensiveObjectFactory&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cachedResult&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExpensiveObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_cachedResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createAndCacheExpensiveLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;lock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_criticalSection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_expensiveLoadReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createAndCacheExpensiveLoad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_expensiveLoadReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExpensiveObjectFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_expensiveLoadReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That is certainly a lot of code. Let’s break it down:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExpensiveLoad&lt;/code&gt; is a property that returns whatever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_expensiveLoadReader&lt;/code&gt; returns&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_expensiveLoadReader&lt;/code&gt; will call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createAndCacheExpensiveLoad&lt;/code&gt; by default&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createAndCacheExpensiveLoad&lt;/code&gt; will replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_expensiveLoadReader&lt;/code&gt; the first time it gets called. This replacement will be thread-safe&lt;/li&gt;
  &lt;li&gt;Subsequent calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_expensiveLoadReader&lt;/code&gt; will return whatever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExpensiveObjectFactory.Build&lt;/code&gt; returns. These calls don’t need locks because they’re not mutating anything&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExpensiveObjectFactory.Build&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_cachedResult&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_cachedResult&lt;/code&gt; is only calculated once&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This solution is certainly effective. And very complex too. Who would want to write all that code for a Lazy property?&lt;/p&gt;

&lt;h2 id=&quot;lazypropertyhelper-nuget-to-the-rescue&quot;&gt;&lt;a href=&quot;https://github.com/camilin87/ThreadSafeEfficientLazyProperty&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LazyPropertyHelper&lt;/code&gt;&lt;/a&gt; nuget to the rescue&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;All problems in computer science can be solved by another level of indirection. David Wheeler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I created the &lt;a href=&quot;https://github.com/camilin87/ThreadSafeEfficientLazyProperty&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LazyPropertyHelper&lt;/code&gt;&lt;/a&gt; nuget to encapsulate the solution in a reusable way. It is &lt;a href=&quot;https://github.com/camilin87/ThreadSafeEfficientLazyProperty&quot;&gt;open source&lt;/a&gt;, free, and ready to use.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyServiceThatUsesLazyPropertyHelperNuget&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_expensiveLoadReader&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;LazyProperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ExpensiveObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExpensiveLoad&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_expensiveLoadReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The service code gets considerably simplified. Let’s break it down:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExpensiveLoad&lt;/code&gt; is still a property that returns whatever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_expensiveLoadReader&lt;/code&gt; returns&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_expensiveLoadReader&lt;/code&gt; will create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new ExpensiveObject()&lt;/code&gt; the first time it gets called. It will cache the result in a thread-safe manner&lt;/li&gt;
  &lt;li&gt;Subsequent calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_expensiveLoadReader&lt;/code&gt; will always return the cached object without the need for locks&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;The complexities of implementing the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lazy_initialization&quot;&gt;Lazy pattern&lt;/a&gt; in a thread-safe an efficient manner can be abstracted out into a reusable library.&lt;/p&gt;
</description>
          <pubDate>Thu, 01 Mar 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/03/01/lazy-properties-without-compromises-in-csharp/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/03/01/lazy-properties-without-compromises-in-csharp/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: Functional Programming in Java</title>
          <description>&lt;p&gt;&lt;a href=&quot;http://amzn.to/2F61g1h&quot;&gt;Functional Programming in Java&lt;/a&gt; is an excellent read like almost every other book from the &lt;a href=&quot;https://pragprog.com/&quot;&gt;Pragmatic Bookshelf&lt;/a&gt;. It is written in a simple and concise style that facilitates reading. The book is sprinkled with references to &lt;a href=&quot;http://amzn.to/2F2K0Xr&quot;&gt;other great books&lt;/a&gt; and dozens of code examples.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2F61g1h&quot;&gt;&lt;img src=&quot;/images/books/functional-programming-in-java.jpg&quot; alt=&quot;Functional Programming in Java&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2F61g1h&quot;&gt;Functional Programming in Java&lt;/a&gt; can be a great introduction for those uninitiated with the functional programming paradigm and its benefits. It can also bring great value to more seasoned developers with experience in the platform. It showcases many of the Java 8 additions around lambdas and the Stream API. The book even includes an appendix with the existing &lt;a href=&quot;https://www.geeksforgeeks.org/functional-interfaces-java/&quot;&gt;Functional Interfaces&lt;/a&gt; in the JDK.&lt;/p&gt;

&lt;p&gt;The book also covers platform independent patterns that can help virtually every developer such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Memoization&quot;&gt;Memoization&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Tail_call&quot;&gt;TCO&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Lazy_initialization&quot;&gt;Thread-Safe lazy initialization&lt;/a&gt;, and many more. Moreover, many of the patterns showcased in the book can be seamlessly ported to other platforms.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/2F61g1h&quot;&gt;Go read it now&lt;/a&gt;&lt;/p&gt;
</description>
          <pubDate>Tue, 27 Feb 2018 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2018/02/27/functional-programming-in-java/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2018/02/27/functional-programming-in-java/</guid>
        </item>
      
    
      
        <item>
          <title>Thoughts on side projects</title>
          <description>&lt;p&gt;My first paid job was writing Windows Applications in C#. My work computer was a clunky HP &lt;em&gt;-originally intended to be used as a server-&lt;/em&gt;. During my nights and weekends I started learning &lt;a href=&quot;https://en.wikipedia.org/wiki/Cocoa_(API)&quot;&gt;Cocoa&lt;/a&gt; on a Dell laptop with no battery or display, connected to a 15” CRT monitor, running a &lt;a href=&quot;https://hackintosh.com/&quot;&gt;Hackintosh&lt;/a&gt; version of OS X. A few years passed by… I bought my first Mac, my first iPhone, and my company needed to develop some iOS apps. Guess what iOS developer with several years of experience was readily available?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgflip.com/1xx534.jpg&quot; alt=&quot;I'm always working for you, even when I'm not&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Years later I was working almost exclusively writing &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/wpf/&quot;&gt;WPF&lt;/a&gt; Windows applications. I got interested in rich client-side web apps because &lt;a href=&quot;https://www.tddapps.com/2016/02/02/General-POS-design-considerations/&quot;&gt;remotely updating my clients was suboptimal&lt;/a&gt;. I started learning a little bit of &lt;a href=&quot;http://knockoutjs.com/&quot;&gt;Knockout&lt;/a&gt; and &lt;a href=&quot;https://angular.io/&quot;&gt;Angular&lt;/a&gt; on my nights and weekends. Guess whom got hired to work exclusively writing Angular web applications?&lt;/p&gt;

&lt;p&gt;I can share other similar examples but I don’t want to bore you.&lt;/p&gt;

&lt;p&gt;Whatever technology you learn, whatever activity you engage with: blogging, giving presentations, etc, is actively benefiting your employer. People rarely unlearn things they learn on their free time. I’m going to take it even a step further, that lunch you ate at your desk so you could code on that &lt;a href=&quot;https://kotlinlang.org/&quot;&gt;Kotlin&lt;/a&gt; project… that is also good for your employer.&lt;/p&gt;

&lt;h2 id=&quot;employees&quot;&gt;Employees&lt;/h2&gt;
&lt;p&gt;Learn new things, have personal projects, share your knowledge, go to meetups, contribute to open source projects. Well rounded professionals know many different technologies, even if they are not related to their day-to-day jobs.&lt;/p&gt;

&lt;h2 id=&quot;employers&quot;&gt;Employers&lt;/h2&gt;
&lt;p&gt;Encourage your employees to learn for the sake of learning. You’ll reap the benefits. Encourage your employees to have side projects, this will create more mature employees that can schedule their time better.&lt;/p&gt;

&lt;h3 id=&quot;do-you-prefer-to-be-this-guy&quot;&gt;Do you prefer to be this guy?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/side-projects/office-space-boss.jpg&quot; alt=&quot;Office Space&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;or-this-guy&quot;&gt;Or this guy&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/side-projects/markzuckerberg.jpg&quot; alt=&quot;Zuckerberg&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;but-what-if&quot;&gt;But what if?&lt;/h2&gt;

&lt;h3 id=&quot;my-employees-work-on-their-personal-projects-all-the-time&quot;&gt;My employees work on their personal projects all the time:&lt;/h3&gt;
&lt;p&gt;Is their work done?:
&lt;strong&gt;Yes&lt;/strong&gt;. Great, maybe that employee needs bigger challenges. Give it to them before they go somewhere else.&lt;br /&gt;
&lt;strong&gt;No&lt;/strong&gt;. Have an honest conversation and share this information with the employee. Take corrective actions if the problem persists.&lt;/p&gt;

&lt;h3 id=&quot;my-employees-are-preparing-for-their-next-interview&quot;&gt;My employees are preparing for their next interview:&lt;/h3&gt;
&lt;p&gt;Nobody can prevent an employee from wanting to go somewhere else. Figure out why would they want to leave and solve their problem.&lt;/p&gt;

&lt;h3 id=&quot;this-company-have-enough-work-for-three-lifetimes-they-should-be-working-for-me-100-of-their-time&quot;&gt;This company have enough work for three lifetimes, they should be working for me 100% of their time:&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Are the things to do publicly and readily available?&lt;/li&gt;
  &lt;li&gt;Are your employees aware that they can work 100% of their time?&lt;/li&gt;
  &lt;li&gt;Are you willing to pay for somebody directly working for you 100% of their time?&lt;/li&gt;
  &lt;li&gt;Do you need to hire more people?&lt;/li&gt;
  &lt;li&gt;Do you need to work smarter rather than harder?&lt;/li&gt;
  &lt;li&gt;Are you solving the right problems?&lt;/li&gt;
  &lt;li&gt;I’ve had countless situations where a good night of sleep solved my problem better than an entire afternoon glued to a keyboard. Some context switching can be beneficial sometimes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;i-want-to-know-what-everybody-is-doing-all-the-time-in-a-spreadsheet&quot;&gt;I want to know what everybody is doing all the time in a spreadsheet&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/side-projects/i_cant_even-fainting.gif&quot; alt=&quot;I can't even&quot; /&gt;&lt;/p&gt;
</description>
          <pubDate>Tue, 17 Oct 2017 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2017/10/17/side-projects/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2017/10/17/side-projects/</guid>
        </item>
      
    
      
        <item>
          <title>Ubuntu installation on KVM</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://coreos.com/&quot;&gt;Ubuntu&lt;/a&gt; is a very popular Linux distribution &lt;em&gt;[citation_needed]&lt;/em&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine&quot;&gt;KVM&lt;/a&gt; its default virtualization infrastructure. Unfortunately, creating Ubuntu virtual machines for KVM is not a simple process. &lt;a href=&quot;https://github.com/camilin87/kvm-automation&quot;&gt;I built a tool to automate parts of this process&lt;/a&gt; and here’s how to use it.&lt;/p&gt;

&lt;h2 id=&quot;prerequitistes&quot;&gt;Prerequitistes&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.ubuntu.com/XenialXerus/ReleaseNotes?_ga=1.8579765.1603762589.1484423368/&quot;&gt;Ubuntu Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cyberciti.biz/faq/installing-kvm-on-ubuntu-16-04-lts-server/&quot;&gt;KVM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.tddapps.com/2015/06/24/Git-Workflow/&quot;&gt;Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ruby-lang.org/en/&quot;&gt;Ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://releases.ubuntu.com/16.04/&quot;&gt;Ubuntu Server Installation ISO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone &lt;span class=&quot;nt&quot;&gt;--depth&lt;/span&gt; 1 https://github.com/camilin87/kvm-automation.git &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;kvm-automation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;create-the-virtual-machine&quot;&gt;Create the virtual machine&lt;/h2&gt;

&lt;p&gt;This command creates an Ubuntu virtual machine named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vm1&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/vms&lt;/code&gt; folder. The new machine will have two CPUs, four GB of RAM, seven GB of hard drive, and will use the bridge network adapter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;br0&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ruby vm_task.rb UbuntuVmCreationTask                     &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; ~/vms/ &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; vm1                             &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--cpu&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--ram&lt;/span&gt; 4096 &lt;span class=&quot;nt&quot;&gt;--hdd&lt;/span&gt; 7 &lt;span class=&quot;nt&quot;&gt;--br&lt;/span&gt; br0                  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--os-variant&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ubuntu16.04&quot;&lt;/span&gt;                           &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--img&lt;/span&gt; ~/vm-templates/ubuntu-16.04.2-server-amd64.iso &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--vnc-port&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;5921&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;complete-the-installation&quot;&gt;Complete the installation&lt;/h2&gt;

&lt;p&gt;Fire off your favorite &lt;a href=&quot;https://en.wikipedia.org/wiki/Virtual_Network_Computing&quot;&gt;VNC client&lt;/a&gt; and connect to your KVM host on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5921&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ubuntu-kvm/ubuntu-installation-start.png&quot; alt=&quot;Ubuntu Installation&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;kvm-automation-caveats&quot;&gt;&lt;a href=&quot;https://github.com/camilin87/kvm-automation&quot;&gt;kvm-automation&lt;/a&gt; caveats&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Supports only CoreOS and Ubuntu&lt;/li&gt;
  &lt;li&gt;Designed to use exclusively bridge network adapters using DHCP&lt;/li&gt;
  &lt;li&gt;Does not automate the entire Ubuntu machine provisioning&lt;/li&gt;
  &lt;li&gt;Deletes pre-existing machines with the specified name&lt;/li&gt;
  &lt;li&gt;The VNC traffic is not encrypted and it is available on all the network interfaces of the host&lt;/li&gt;
  &lt;li&gt;The new machine will not be accessible over VNC if the port is already being used by another machine&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Thu, 30 Mar 2017 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2017/03/30/ubuntu-installation-on-kvm/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2017/03/30/ubuntu-installation-on-kvm/</guid>
        </item>
      
    
      
        <item>
          <title>CoreOS installation on KVM</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://coreos.com/&quot;&gt;CoreOS&lt;/a&gt; is a lightweight Linux distribution designed specifically to run containers. &lt;a href=&quot;https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine&quot;&gt;KVM&lt;/a&gt; is the default virtualization infrastructure for &lt;a href=&quot;https://www.ubuntu.com/&quot;&gt;Ubuntu&lt;/a&gt;. Unfortunately, creating a CoreOS virtual machine for KVM is not a simple process. &lt;a href=&quot;https://github.com/camilin87/kvm-automation&quot;&gt;I built a tool to automate this process&lt;/a&gt; and here’s how to use it.&lt;/p&gt;

&lt;h2 id=&quot;prerequitistes&quot;&gt;Prerequitistes&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.ubuntu.com/XenialXerus/ReleaseNotes?_ga=1.8579765.1603762589.1484423368/&quot;&gt;Ubuntu Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.tddapps.com/2017/01/15/how-to-use-your-keybase-key-for-ssh/&quot;&gt;RSA key&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cyberciti.biz/faq/installing-kvm-on-ubuntu-16-04-lts-server/&quot;&gt;KVM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.tddapps.com/2015/06/24/Git-Workflow/&quot;&gt;Git&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/wget/&quot;&gt;Wget&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.bzip.org/&quot;&gt;bzip2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.ruby-lang.org/en/&quot;&gt;Ruby&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone &lt;span class=&quot;nt&quot;&gt;--depth&lt;/span&gt; 1 https://github.com/camilin87/kvm-automation.git &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;kvm-automation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;create-the-virtual-machine&quot;&gt;Create the virtual machine&lt;/h2&gt;

&lt;p&gt;This command creates a CoreOS virtual machine named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vm1&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/vms&lt;/code&gt; folder. The new machine will have two CPUs, four GB of RAM, seven GB of hard drive, and will use the bridge network adapter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;br0&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ruby vm_task.rb CoreOsVmCreationTask         &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--path&lt;/span&gt; ~/vms/ &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; vm1 &lt;span class=&quot;nt&quot;&gt;--dwnld&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;    &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--cpu&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--ram&lt;/span&gt; 4096 &lt;span class=&quot;nt&quot;&gt;--hdd&lt;/span&gt; 7 &lt;span class=&quot;nt&quot;&gt;--br&lt;/span&gt; br0      &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-does-it-work&quot;&gt;How does it work?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/camilin87/kvm-automation&quot;&gt;kvm-automation&lt;/a&gt; generates a hard drive after downloading the &lt;a href=&quot;https://coreos.com/os/docs/latest/booting-with-qemu.html&quot;&gt;latest Stable CoreOS QEMU Image&lt;/a&gt;. It then builds a &lt;a href=&quot;https://coreos.com/os/docs/latest/cloud-config.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yml&lt;/code&gt;&lt;/a&gt; out of the &lt;a href=&quot;https://www.tddapps.com/2017/01/15/how-to-use-your-keybase-key-for-ssh/&quot;&gt;public RSA key&lt;/a&gt; and the machine name. The tool then uses this information to create a KVM virtual machine that will autostart on system boot.&lt;/p&gt;

&lt;h3 id=&quot;kvm-automation-caveats&quot;&gt;&lt;a href=&quot;https://github.com/camilin87/kvm-automation&quot;&gt;kvm-automation&lt;/a&gt; caveats&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Supports only CoreOS and Ubuntu&lt;/li&gt;
  &lt;li&gt;Designed to use exclusively bridge network adapters using DHCP&lt;/li&gt;
  &lt;li&gt;Does not allow guest customization through the &lt;a href=&quot;https://coreos.com/os/docs/latest/cloud-config.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Deletes pre-existing machines with the specified name&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Tue, 28 Mar 2017 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2017/03/28/coreos-installation-on-kvm/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2017/03/28/coreos-installation-on-kvm/</guid>
        </item>
      
    
      
        <item>
          <title>How to run any container on a schedule</title>
          <description>&lt;p&gt;Configuring a recurring task on a Docker container requires an &lt;a href=&quot;https://www.tddapps.com/2016/05/05/how-to-run-node-cron-jobs-in-a-docker-container/&quot;&gt;innumerable number of steps&lt;/a&gt;. Moreover, an image designed to &lt;em&gt;“compute an hourly database health report”&lt;/em&gt; can rarely be reused to &lt;em&gt;“check the api server status every three minutes”&lt;/em&gt;. &lt;a href=&quot;https://github.com/camilin87/docker-cron&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;camilin87/docker-cron&lt;/code&gt;&lt;/a&gt; is a Docker image that interacts with the &lt;a href=&quot;https://docs.docker.com/engine/api/v1.25/&quot;&gt;Docker Engine Api&lt;/a&gt; to create and run containers on a schedule. The following examples describe its usage and inner workings.&lt;/p&gt;

&lt;h2 id=&quot;task-1-ping-wwwgooglecom&quot;&gt;Task 1: Ping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.google.com&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;This snippet creates a new container named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlStatusChecker&lt;/code&gt; based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dperson/smokeping:latest&lt;/code&gt; image. It will run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping www.google.com -c 1&lt;/code&gt; command on startup.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; urlStatusChecker &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  dperson/smokeping:latest              &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  ping www.google.com &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;task-11-ping-wwwgooglecom-every-three-minutes&quot;&gt;Task 1.1: Ping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.google.com&lt;/code&gt; every three minutes&lt;/h3&gt;

&lt;p&gt;The following snippet also creates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlStatusChecker&lt;/code&gt; container to ping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.google.com&lt;/code&gt;. However, this new container will get recreated and executed every three minutes.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; containerInfo.json &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOL&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
{
    &quot;Image&quot;: &quot;dperson/smokeping:latest&quot;,
    &quot;Name&quot;: &quot;urlStatusChecker&quot;,
    &quot;Cmd&quot;: [
        &quot;ping&quot;,
        &quot;www.google.com&quot;,
        &quot;-c&quot;,
        &quot;1&quot;
    ]
}
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOL

&lt;/span&gt;docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; docker-cron                         &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock                &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TASK_SCHEDULE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*/3 * * * *'&lt;/span&gt;                           &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;/containerInfo.json:/usr/src/containerInfo.json      &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  camilin87/docker-cron
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-does-it-work&quot;&gt;How does it work?&lt;/h3&gt;

&lt;p&gt;The code from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task 1.1&lt;/code&gt; is running a container named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-cron&lt;/code&gt; based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;camilin87/docker-cron&lt;/code&gt; image. That container will in turn create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlStatusChecker&lt;/code&gt; container based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dperson/smokeping:latest&lt;/code&gt; every three minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TASK_SCHEDULE&lt;/code&gt;&lt;/strong&gt;: an environment variable to configure the frequency&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerInfo.json&lt;/code&gt;&lt;/strong&gt;: a file that contains the detailed specification of the task to run. It can describe any aspect of the spawned container -&lt;em&gt;volumes, networks, cmd, …&lt;/em&gt;- as long as it is supported on the &lt;a href=&quot;https://docs.docker.com/engine/api/v1.25/&quot;&gt;Docker Engine Api&lt;/a&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;camilin87/docker-cron&lt;/code&gt; image is built to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; this file to the &lt;a href=&quot;https://docs.docker.com/engine/api/v1.25/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/containers/create&lt;/code&gt; Docker Engine Api method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/run/docker.sock&lt;/code&gt;&lt;/strong&gt;: &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/dockerd/#/daemon-socket-option&quot;&gt;Is the socket where the Docker Daemon is listening to&lt;/a&gt;. It needs to be mounted as a volume for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-cron&lt;/code&gt; container to interact with the &lt;a href=&quot;https://docs.docker.com/engine/api/v1.25/&quot;&gt;Docker Engine Api&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;things-we-didnt-have-to-do&quot;&gt;Things we didn’t have to do&lt;/h3&gt;

&lt;p&gt;Let’s analyze some tasks we didn’t have to do to ping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.google.com&lt;/code&gt; every three minutes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We did not have to create a new Docker image. We were able to reuse the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dperson/smokeping:latest&lt;/code&gt; image we were using before.&lt;/li&gt;
  &lt;li&gt;We did not have to install nor configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cron&lt;/code&gt; nor any other dependency.&lt;/li&gt;
  &lt;li&gt;We did not have to configure a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cron&lt;/code&gt; task and all the complexities it involves with configuration files and log rotation.&lt;/li&gt;
  &lt;li&gt;We don’t have to worry about memory leaks or performance degradation over time. Simply because a brand new instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dperson/smokeping:latest&lt;/code&gt; is created and destroyed every three minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;task-2-parameterize-the-ping-url-and-count&quot;&gt;Task 2: Parameterize the Ping url and count&lt;/h2&gt;

&lt;p&gt;The following task is very similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task 1&lt;/code&gt;. However, in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task 2&lt;/code&gt; the url and the number of requests can be parameterized through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PING_URL&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PING_COUNT&lt;/code&gt; environment variables.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; urlStatusChecker     &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PING_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;www.facebook.com&quot;&lt;/span&gt;         &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PING_COUNT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1                        &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  dperson/smokeping:latest                  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  sh &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ping &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;PING_URL -c &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;PING_COUNT&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;task-21-ping-wwwfacebookcom-every-three-minutes&quot;&gt;Task 2.1: Ping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.facebook.com&lt;/code&gt; every three minutes&lt;/h3&gt;

&lt;p&gt;This would be the equivalent parameterized scheduled Ping.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; containerInfo.json &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOL&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
{
    &quot;Image&quot;: &quot;dperson/smokeping:latest&quot;,
    &quot;Name&quot;: &quot;urlStatusChecker&quot;,
    &quot;Cmd&quot;: [
        &quot;sh&quot;,
        &quot;-c&quot;,
        &quot;ping &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;PING_URL -c &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;PING_COUNT&quot;
    ]
}
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOL

&lt;/span&gt;docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; docker-cron                         &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock                &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TASK_SCHEDULE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*/3 * * * *'&lt;/span&gt;                           &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PING_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'www.facebook.com'&lt;/span&gt;                           &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PING_COUNT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1                                          &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;COPY_ENV_VARS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'PING_URL,PING_COUNT'&lt;/span&gt;                   &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;/containerInfo.json:/usr/src/containerInfo.json      &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  camilin87/docker-cron
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;how-does-it-work-1&quot;&gt;How does it work?&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;camilin87/docker-cron&lt;/code&gt; image copies environment variables from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-cron&lt;/code&gt; container into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urlStatusChecker&lt;/code&gt; container. However, -&lt;em&gt;for security reasons&lt;/em&gt;- it does not copy all the environment variables. It only copies those environment variables specified in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY_ENV_VARS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;camilin87/docker-cron&lt;/code&gt; is leveraging existing functionality of the &lt;a href=&quot;https://docs.docker.com/engine/api/v1.25/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/containers/create&lt;/code&gt; Docker Engine Api method&lt;/a&gt;. It is injecting the environment variables specified in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY_ENV_VARS&lt;/code&gt; into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Env&lt;/code&gt; field of the Api request.&lt;/p&gt;

&lt;h3 id=&quot;why-so-much-hassle-with-environment-variables&quot;&gt;Why so much hassle with environment variables?&lt;/h3&gt;

&lt;p&gt;Environment variables have traditionally been the foundation to provide non-persistent configuration to containers. Any decent container scheduler should provide a secure way to deal with them.&lt;/p&gt;

&lt;h2 id=&quot;bonus-docker-swarm-support&quot;&gt;Bonus: Docker Swarm support&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.docker.com/products/docker-swarm&quot;&gt;Docker Swarm&lt;/a&gt; provides some nice monitoring capabilities out of the box. The following snippet has the same functionality as the one from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Task 2&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker service create                                                                       &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--replicas&lt;/span&gt; 1                                                                              &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; docker-cron                                                                        &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;TASK_SCHEDULE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'*/3 * * * *'&lt;/span&gt;                                                         &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PING_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'www.facebook.com'&lt;/span&gt;                                                         &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PING_COUNT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1                                                                        &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;COPY_ENV_VARS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'PING_URL,PING_COUNT'&lt;/span&gt;                                                 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--mount&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;bind&lt;/span&gt;,source&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/run/docker.sock,target&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/run/docker.sock                 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--mount&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;bind&lt;/span&gt;,source&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;/containerInfo.json,destination&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/src/containerInfo.json  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  camilin87/docker-cron
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;additional-resources&quot;&gt;Additional resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Visit &lt;a href=&quot;http://www.nncron.ru/help/EN/working/cron-format.htm&quot;&gt;The Cron Format&lt;/a&gt; to learn more about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cron&lt;/code&gt; job frequencies&lt;/li&gt;
  &lt;li&gt;All the source &lt;a href=&quot;https://github.com/camilin87/docker-cron&quot;&gt;can be found here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;This &lt;a href=&quot;https://github.com/camilin87/docker-cron/blob/master/readme.md&quot;&gt;readme file&lt;/a&gt; contains additional usage and configuration details&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;camilin87/docker-cron&lt;/code&gt; uses the &lt;a href=&quot;https://docs.docker.com/engine/api/v1.24/&quot;&gt;Docker Engine Api 1.24&lt;/a&gt; for compatibility reasons with Docker 1.12&lt;/li&gt;
&lt;/ul&gt;

</description>
          <pubDate>Sat, 18 Feb 2017 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2017/02/18/how-to-run-any-container-on-a-schedule/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2017/02/18/how-to-run-any-container-on-a-schedule/</guid>
        </item>
      
    
      
        <item>
          <title>CoreOS baremetal server installation</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://coreos.com/&quot;&gt;CoreOS&lt;/a&gt; is a lightweight Linux distribution designed specifically to run containers. Unfortunately its installation on baremetal servers is not as streamlined as &lt;a href=&quot;https://www.ubuntu.com/download/desktop/install-ubuntu-desktop&quot;&gt;other Linux distributions&lt;/a&gt;. These are the steps I took to install CoreOS on a couple of physical machines.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://coreos.com/os/docs/latest/booting-with-iso.html&quot;&gt;Download and burn a CoreOS ISO&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://www.tddapps.com/2017/01/15/how-to-use-your-keybase-key-for-ssh/&quot;&gt;Generate ssh keys to log into the boxes&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Generate a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yml&lt;/code&gt; file&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; cat &amp;gt; cloud-config.yaml &amp;lt;&amp;lt;EOL
 #cloud-config
 ssh-authorized-keys:
     - `cat id_rsa.pub`
 EOL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;em&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yaml&lt;/code&gt; can be reused to provision multiple computers&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yaml&lt;/code&gt; file to a flash drive&lt;br /&gt;
 &lt;em&gt;&lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-use-cloud-config-for-your-initial-server-setup&quot;&gt;Additional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yaml&lt;/code&gt; help&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;prepare-the-installation&quot;&gt;Prepare the installation&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Connect the computer to the LAN&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Boot up from the CD&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Wait until a command prompt appears&lt;br /&gt;
 &lt;em&gt;&lt;a href=&quot;https://coreos.com/os/docs/latest/installing-to-disk.html&quot;&gt;More information on the CoreOS installation to disk&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;copy-the-cloud-configyaml-into-the-server&quot;&gt;Copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yaml&lt;/code&gt; into the server&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Plug in the flash drive with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yaml&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;List the physical disks&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; lsblk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Determine what is the first partition of the USB drive. In this example we’ll use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sdb1&lt;/code&gt; where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sdb&lt;/code&gt; is the physical device&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-config.yaml&lt;/code&gt;&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; sudo su - root
 mkdir ~/flash
 mount -o ro /dev/sdb1 ~/flash
 cp ~/flash/cloud-config.yaml ~/
 umount /dev/sdb1
 rm -Rf ~/flash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Unplug the flash drive&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;run-the-installation&quot;&gt;Run the installation&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;coreos-install -d /dev/sda -c ~/cloud-config.yaml
reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point the server should boot up using DHCP. Its console will display its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IP&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MAC&lt;/code&gt; address.&lt;/p&gt;

&lt;h2 id=&quot;remote-into-the-server&quot;&gt;Remote into the server&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; into the box &lt;a href=&quot;https://www.cyberciti.biz/faq/force-ssh-client-to-use-given-private-key-identity-file/&quot;&gt;using your private RSA key&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh -i id_rsa core@192.168.1.80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success!&lt;/p&gt;

&lt;h2 id=&quot;bonus-configure-the-network-to-use-a-static-ip&quot;&gt;BONUS: Configure the network to use a static IP&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;List the Network Interfaces&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; ifconfig
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Determine the name of the Ethernet Adapter. In this example we’ll use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eno1&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Execute the following commands using the appropriate values for your network&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; sudo su - root

 cat &amp;gt; /etc/systemd/network/static.network &amp;lt;&amp;lt;EOL
 [Match]
 Name=eno1

 [Network]
 Address=192.168.1.10/24
 Gateway=192.168.1.1
 DNS=8.8.8.8
 EOL

 reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://coreos.com/os/docs/latest/network-config-with-networkd.html&quot;&gt;More information on CoreOS network configuration&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point the server should boot up using the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IP&lt;/code&gt;.&lt;/p&gt;
</description>
          <pubDate>Mon, 16 Jan 2017 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2017/01/16/core-os-baremetal-server-installation/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2017/01/16/core-os-baremetal-server-installation/</guid>
        </item>
      
    
      
        <item>
          <title>How to use your Keybase key for ssh</title>
          <description>&lt;p&gt;Extracting an &lt;a href=&quot;https://en.wikipedia.org/wiki/RSA_(cryptosystem)&quot;&gt;RSA&lt;/a&gt; key out of &lt;a href=&quot;https://keybase.io/&quot;&gt;Keybase&lt;/a&gt; is not straightforward for security beginners. These are the steps I took to automate the process.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;Manually managing usernames and passwords for &lt;a href=&quot;https://en.wikipedia.org/wiki/Secure_Shell&quot;&gt;ssh&lt;/a&gt; into servers can be a difficult task. All those different passwords need to be backed up, synchronized across multiple computers, and typed every single time. Although password managers mitigate the credentials problem there’s a better mechanism to &lt;a href=&quot;http://www.rebol.com/docs/ssh-auto-login.html&quot;&gt;remote into boxes without typing passwords&lt;/a&gt;. The process involves generating public and private &lt;a href=&quot;https://en.wikipedia.org/wiki/RSA_(cryptosystem)&quot;&gt;RSA&lt;/a&gt; key files.&lt;/p&gt;

&lt;p&gt;These RSA keys need to be backed up and shared across different computers. While some use &lt;a href=&quot;https://db.tt/mawxtzeB&quot;&gt;Dropbox&lt;/a&gt; to keep their keys synchronized others use &lt;a href=&quot;https://keybase.io/&quot;&gt;Keybase&lt;/a&gt;. It is an online service to store &lt;a href=&quot;https://en.wikipedia.org/wiki/Pretty_Good_Privacy&quot;&gt;PGP&lt;/a&gt; keys and map them to public identities such as Twitter accounts and domain registrations.&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Have a &lt;a href=&quot;https://keybase.io/&quot;&gt;Keybase&lt;/a&gt; account.&lt;/li&gt;
  &lt;li&gt;Have a working &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; installation&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;

&lt;p&gt;The following commands generate ssh keys out of the Keybase PGP keys&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;TMP_KEY_STORAGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/devroot/keys
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TMP_KEY_STORAGE&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TMP_KEY_STORAGE&lt;/span&gt;:/home ubuntu:16.04 /bin/bash

apt-get update &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;
apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;curl monkeysphere &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; https://prerelease.keybase.io/keybase_amd64.deb
dpkg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; keybase_amd64.deb
apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;keybase_amd64.deb

groupadd &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; 1001 user_data
useradd &lt;span class=&quot;nt&quot;&gt;--create-home&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; user_data &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; 1001 user_data
su user_data
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /home/user_data

keybase login

&lt;span class=&quot;c&quot;&gt;# Exporting your Keybase public key to keybase.public.key&lt;/span&gt;
keybase pgp &lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; keybase.public.key
&lt;span class=&quot;c&quot;&gt;# Exporting your Keybase private key to keybase.private.key&lt;/span&gt;
keybase pgp &lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--unencrypted&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; keybase.private.key

&lt;span class=&quot;c&quot;&gt;# Import your Keybase public key&lt;/span&gt;
gpg &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--import&lt;/span&gt; keybase.public.key
&lt;span class=&quot;c&quot;&gt;# Import your Keybase private key&lt;/span&gt;
gpg &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--allow-secret-key-import&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--import&lt;/span&gt; keybase.private.key
&lt;span class=&quot;c&quot;&gt;# The key import process produces a short hexadecimal hash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# We need to extract this hash and use it to generate the RSA key&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# The hash is temporarily saved into hash.key&lt;/span&gt;
gpg &lt;span class=&quot;nt&quot;&gt;--list-keys&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'^pub\s*.*\/*.\s.*'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-oEi&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\/(.*)\s'&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 2- | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{$1=$1};1'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; hash.key

&lt;span class=&quot;nv&quot;&gt;ENC_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;hash.key&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ENC_KEY&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Generate the RSA private key using the hexadecimal hash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# The private key will be saved in the id_rsa file&lt;/span&gt;
gpg &lt;span class=&quot;nt&quot;&gt;--export-options&lt;/span&gt; export-reset-subkey-passwd,export-minimal,no-export-attributes &lt;span class=&quot;nt&quot;&gt;--export-secret-keys&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-armor&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ENC_KEY&lt;/span&gt; | openpgp2ssh &lt;span class=&quot;nv&quot;&gt;$ENC_KEY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; id_rsa
&lt;span class=&quot;c&quot;&gt;# Secure the private RSA key file  &lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;400 id_rsa
&lt;span class=&quot;c&quot;&gt;# Generate the public RSA key file  &lt;/span&gt;
ssh-keygen &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; id_rsa &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; id_rsa.pub

&lt;span class=&quot;c&quot;&gt;# Remove all the temporary files  &lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.key

&lt;span class=&quot;nb&quot;&gt;exit
exit

chmod &lt;/span&gt;400 &lt;span class=&quot;nv&quot;&gt;$TMP_KEY_STORAGE&lt;/span&gt;/user_data/id_rsa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;bonus-replace-the-default-ssh-key&quot;&gt;Bonus: Replace the default ssh key&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Delete any pre-existing key  &lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ~/.ssh/id_rsa.pub
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ~/.ssh/id_rsa

&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~/.ssh/

&lt;span class=&quot;c&quot;&gt;# Create symbolic links to the default  &lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TMP_KEY_STORAGE&lt;/span&gt;/user_data/id_rsa.pub ~/.ssh/id_rsa.pub
&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TMP_KEY_STORAGE&lt;/span&gt;/user_data/id_rsa ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;want-to-know-more&quot;&gt;Want to know more?&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.integralist.co.uk/posts/security-basics.html#7.3&quot;&gt;Security basics with GPG, OpenSSH, OpenSSL and Keybase&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://sysmic.org/dotclear/index.php?post/2010/03/24/Convert-keys-betweens-GnuPG%2C-OpenSsh-and-OpenSSL&quot;&gt;Convert keys between GnuPG, OpenSsh and OpenSSL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Sun, 15 Jan 2017 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2017/01/15/how-to-use-your-keybase-key-for-ssh/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2017/01/15/how-to-use-your-keybase-key-for-ssh/</guid>
        </item>
      
    
      
        <item>
          <title>2016 Retrospective</title>
          <description>&lt;p&gt;These are my most dearest professional achievements and setbacks of 2016. Happy new year folks!!&lt;/p&gt;

&lt;h2 id=&quot;sharing&quot;&gt;Sharing&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;5 public presentations&lt;/li&gt;
  &lt;li&gt;10 hours of in-house workshops&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.tddapps.com/&quot;&gt;22 blogposts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/camilin87&quot;&gt;1677 public commits&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/~camilin87&quot;&gt;7 npm packages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/camilin87/&quot;&gt;11 docker hub images&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;learning&quot;&gt;Learning&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;21 books read
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ilbgYU&quot;&gt;The Phoenix Project&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2hwjufv&quot;&gt;Think and Grow Rich&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ihxcSi&quot;&gt;First Break All the Rules&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2iISHi5&quot;&gt;Tribal Leadership&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2iIpC26&quot;&gt;Death by Meeting&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2iIV8B7&quot;&gt;The Ideal Team Player&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2hwvrlp&quot;&gt;Remote&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2iIVA2g&quot;&gt;Eat that Frog&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ibE2uN&quot;&gt;Good to Great&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ibCXTE&quot;&gt;Originals&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ihzlgT&quot;&gt;So Good they Cant Ignore You&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ibJU75&quot;&gt;The Goal&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ihy1dP&quot;&gt;Rework&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2iudpyO&quot;&gt;The Four Hour Work Week&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2ibF5ej&quot;&gt;A Random Walk Down Wall Street&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2hQLto3&quot;&gt;The Little book of Common Sense Investing&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2iIQJhL&quot;&gt;Continuos Integration&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://amzn.to/2hwmo4h&quot;&gt;Harry Potter One to Four&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;60 hours of dedicated training
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://umbraco.com/products/training&quot;&gt;Umbraco&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/learning/azure-training.aspx&quot;&gt;Azure&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;http://learn.mapr.com/&quot;&gt;MAPR&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;58 personal &lt;em&gt;private&lt;/em&gt; notes&lt;/li&gt;
  &lt;li&gt;a gazillion of podcasts listened to&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;side-projects&quot;&gt;Side projects&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://sf.tddapps.com/&quot;&gt;Supplement Facts Calculator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitterutils.tddapps.com/&quot;&gt;Twitter Utils&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;3 apps removed from the appstore&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Wed, 28 Dec 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/12/28/2016-retrospective/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/12/28/2016-retrospective/</guid>
        </item>
      
    
      
        <item>
          <title>Miami Scala Meetup Presentation Notes</title>
          <description>&lt;p&gt;These are my notes of my &lt;a href=&quot;https://www.meetup.com/Miami-Scala-Enthusiasts/events/235992205/&quot;&gt;Docker Presentation&lt;/a&gt;. Held at the &lt;a href=&quot;https://www.microsoftinnovationcenters.com/locations/miami&quot;&gt;Microsoft Innovation Center&lt;/a&gt; on December 14th, 2016.&lt;/p&gt;

&lt;h1 id=&quot;example-1&quot;&gt;Example 1&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run ubuntu &lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;example-2&quot;&gt;Example 2&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run ubuntu &lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;example-3-long-lived-containers&quot;&gt;Example 3: Long lived Containers&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:80 seqvence/static-site

docker ps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://localhost:8080&quot;&gt;Open localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;example-4-environment-variables&quot;&gt;Example 4: Environment Variables&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AUTHOR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Miami&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:80 seqvence/static-site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://localhost:8080&quot;&gt;Open localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;example-5-multiple-containers&quot;&gt;Example 5: Multiple Containers&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AUTHOR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Miami&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8081:80 seqvence/static-site
docker run &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AUTHOR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Downtown&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8082:80 seqvence/static-site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://localhost:8081&quot;&gt;Open Site 1&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://localhost:8082&quot;&gt;Open Site 2&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;example-6-docker-compose&quot;&gt;Example 6: Docker Compose&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nano docker-compose.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;site1&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;seqvence/static-site&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AUTHOR=Miami&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8081:80&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;site2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;seqvence/static-site&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AUTHOR=Downtown&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8082:80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://localhost:8081&quot;&gt;Open Site 1&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://localhost:8082&quot;&gt;Open Site 2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cleanup &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose down&lt;/code&gt;&lt;/p&gt;

&lt;h1 id=&quot;example-7-dockerfile&quot;&gt;Example 7: Dockerfile&lt;/h1&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nano app.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;express&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Hello World!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Example app listening on port 3000!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm i express --save 
node app.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://localhost:3000&quot;&gt;Sample Express Site&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nano Dockerfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM node

RUN &lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /usr/src/app

COPY app.js /usr/src/app/

RUN npm i express &lt;span class=&quot;nt&quot;&gt;--save&lt;/span&gt;

CMD &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;node&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;/usr/src/app/app.js&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker build -t camilin87/docker-intro-node .
docker run -p 8080:3000 camilin87/docker-intro-node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://localhost:8080&quot;&gt;Open Node Site&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;example-8-publish-an-image&quot;&gt;Example 8: Publish an Image&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker push camilin87/docker-intro-node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://hub.docker.com/r/camilin87/docker-intro-node/&quot;&gt;View Our published Image&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run it yourself &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -p 8080:3000 camilin87/docker-intro-node&lt;/code&gt;&lt;/p&gt;

&lt;h1 id=&quot;cleanup&quot;&gt;Cleanup&lt;/h1&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;docker ps &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
          <pubDate>Wed, 14 Dec 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/12/14/miami-scala-presentation-notes/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/12/14/miami-scala-presentation-notes/</guid>
        </item>
      
    
      
        <item>
          <title>How to forward your Heroku logs to AWS Cloudwatch for free</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt; is a great platform to run apps with little infrastructure involvement. Most of the time, you just push your code and you get a website up and running in seconds. Heroku even supports third party addons for those times when extra functionality is required. However, I could not find any addon to send my application logs to &lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS Cloudwatch&lt;/a&gt;. The only logical solution was to &lt;a href=&quot;/2016/10/09/my-own-aws-cloudwatch-forwarder-as-an-npm-package/&quot;&gt;build my own AWS Cloudwatch log forwarder&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-to-forward-the-logs-to-aws-cloudwatch&quot;&gt;How to forward the logs to AWS Cloudwatch?&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The following steps are for &lt;a href=&quot;https://nodejs.org/&quot;&gt;node&lt;/a&gt; based apps. If you have a Heroku app made in something else drop me a line and I’ll help you set it up.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Have a deployed NodeJs application in Heroku. &lt;a href=&quot;https://devcenter.heroku.com/articles/getting-started-with-nodejs&quot;&gt;How?&lt;/a&gt;&lt;br /&gt;
&lt;strong&gt;Step 2&lt;/strong&gt;: &lt;a href=&quot;/2016/07/01/configure-AWS-cloudwatch-for-log-forwarders/&quot;&gt;Get your AWS credentials&lt;/a&gt;&lt;br /&gt;
&lt;strong&gt;Step 3&lt;/strong&gt;: Setup the following &lt;a href=&quot;https://devcenter.heroku.com/articles/config-vars&quot;&gt;Heroku environment variables&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;heroku config:set &lt;span class=&quot;nv&quot;&gt;AWS_REGION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'us-east-1'&lt;/span&gt;
heroku config:set &lt;span class=&quot;nv&quot;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'XXXXXXXXXXXXXXX'&lt;/span&gt;
heroku config:set &lt;span class=&quot;nv&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'XXXXXXXXXXXXXXX'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Install &lt;a href=&quot;https://www.npmjs.com/package/aws-cloudwatch-forwarder&quot;&gt;aws-cloudwatch-forwarder&lt;/a&gt; on your project&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm i aws-cloudwatch-forwarder &lt;span class=&quot;nt&quot;&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: Use it in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start&lt;/code&gt; script in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; in a similar way to this:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;aws-cloudwatch-forwarder 'node web.js'&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;want-to-see-it-in-action&quot;&gt;Want to see it in action?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/camilin87/test-heroku-cron&quot;&gt;Here’s some sample code&lt;/a&gt;&lt;/p&gt;
</description>
          <pubDate>Sun, 16 Oct 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/10/16/how-to-forward-your-heroku-logs-to-aws-cloudwatch-for-free/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/10/16/how-to-forward-your-heroku-logs-to-aws-cloudwatch-for-free/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: So good they can't ignore you</title>
          <description>&lt;p&gt;Do you feel stuck in your job? Do you feel you don’t like any profession? You have no idea what to study? &lt;a href=&quot;http://amzn.to/2en85eW&quot;&gt;So good they can’t ignore you&lt;/a&gt; is the right book for you. It is a data driven book about real life examples of successful, and not so successful individuals. &lt;a href=&quot;http://amzn.to/2en85eW&quot;&gt;So good they can’t ignore you&lt;/a&gt; revolves around the idea that following your passion won’t get you anywhere. Instead it proposes to look at your job with a craftsman’s mindset until you become so good they can’t ignore you.&lt;/p&gt;

&lt;h2 id=&quot;control&quot;&gt;Control&lt;/h2&gt;
&lt;p&gt;Industry leaders usually have significant amounts of control over what they do. They choose &lt;strong&gt;what&lt;/strong&gt; to work on and &lt;strong&gt;how&lt;/strong&gt; to do things. Control is what many of us crave for. &lt;a href=&quot;http://amzn.to/2en85eW&quot;&gt;So good they can’t ignore you&lt;/a&gt; illustrates several individuals’ path to gain control over their careers. &lt;strong&gt;Hint&lt;/strong&gt;: None of them followed their passion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveat&lt;/strong&gt;: Only pursue more control if you have evidence other people are willing to pay for it.&lt;/p&gt;

&lt;h2 id=&quot;career-capital&quot;&gt;Career Capital&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;Career capital are the skills you have that are both rare and valuable and that can be used as leverage in defining your career&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Making enough Career capital is the safest way to gain control over your career.&lt;/p&gt;

&lt;p&gt;Employers will usually promote you once you have enough career capital. Beware though, most of these promotions will limit your ability to gain control over your career.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: The best QA technician gets promoted to QA supervisor. Suddenly she’s working twice the hours and has practically no time to spend with her family.&lt;/p&gt;

&lt;h2 id=&quot;the-craftsmans-mindset&quot;&gt;The Craftsman’s mindset&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://amzn.to/2en85eW&quot;&gt;So good they can’t ignore you&lt;/a&gt; illustrates how the Craftsman’s mindset is the best way to gather Career capital. Think about your job like a craftsman would do. Practice deliberately to become better. You need to get good at what you do before you can get good work. Moreover, try to incorporate frequent feedback into the practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: Don’t write the entire book before somebody else reads it. Share each chapter with as many people as possible and incorporate their feedback.&lt;/p&gt;

&lt;h3 id=&quot;deliberate-practice&quot;&gt;Deliberate Practice&lt;/h3&gt;
&lt;p&gt;Talent is overrated. The majority of the leaders in their fields are so because they spend countless hours practicing. Practice outside of your comfort zone. Practice to get better.&lt;/p&gt;

&lt;h2 id=&quot;breakthroughs&quot;&gt;Breakthroughs&lt;/h2&gt;
&lt;p&gt;Finally, the book describes the concept of the Adjacent Possible. How breakthroughs are only possible if they’re built incrementally on top of what’s already possible.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://amzn.to/2en85eW&quot;&gt;So good they can’t ignore you&lt;/a&gt; is an excellent book that will help you advance your career. If you don’t like reading books, you can always &lt;a href=&quot;http://amzn.to/2dL6GOh&quot;&gt;listen to it as an audiobook&lt;/a&gt;.&lt;/p&gt;
</description>
          <pubDate>Thu, 13 Oct 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/10/13/so-good-they-cant-ignore-you-review/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/10/13/so-good-they-cant-ignore-you-review/</guid>
        </item>
      
    
      
        <item>
          <title>I built my own AWS Cloudwatch log forwarder as an npm package</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS Cloudwatch&lt;/a&gt; is an outstanding monitoring platform. With competitive prices, it is extremely configurable and extensible. Unfortunately, monitoring resources outside of AWS &lt;a href=&quot;http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/QuickStartEC2Instance.html&quot;&gt;is not straightforward&lt;/a&gt;. To solve this problem I created my own &lt;a href=&quot;https://www.npmjs.com/package/aws-cloudwatch-forwarder&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cloudwatch log forwarder&lt;/code&gt;&lt;/a&gt; as a non-intrusive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; package.&lt;/p&gt;

&lt;h2 id=&quot;whats-wrong-with-the-existing-log-forwarding-tools&quot;&gt;What’s wrong with the existing log forwarding tools?&lt;/h2&gt;

&lt;p&gt;Amazon provides a &lt;a href=&quot;http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html&quot;&gt;CloudWatch Logs Agent&lt;/a&gt; to forward logs. This agent must be installed as a service running on a server. While this is easy on EC2 instances, it is impossible to do in shared environments such as &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Amazon built the &lt;a href=&quot;http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/Welcome.html&quot;&gt;CloudWatch Logs API&lt;/a&gt; and SDKs for developers to interact with the system. While these SDKs are relatively simple to use, they still require a fair amount of work and understanding of the API.&lt;/p&gt;

&lt;p&gt;Third-party developers have built &lt;a href=&quot;https://www.npmjs.com/search?q=cloudwatch&quot;&gt;their own forwarding tools&lt;/a&gt; based on the Cloudwatch SDKs. However, they all share the same problem. The application code would have to change to &lt;strong&gt;explicitly&lt;/strong&gt; log to Cloudwatch. Moreover, the majority of the javascript tools can only be used from other javascript applications.&lt;/p&gt;

&lt;h2 id=&quot;why-i-built-the-aws-cloudwatch-forwarder&quot;&gt;Why I built the &lt;a href=&quot;https://www.npmjs.com/package/aws-cloudwatch-forwarder&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-cloudwatch-forwarder&lt;/code&gt;&lt;/a&gt;?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;It can be used outside of AWS&lt;/strong&gt;:  The &lt;a href=&quot;https://www.npmjs.com/package/aws-cloudwatch-forwarder&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-cloudwatch-forwarder&lt;/code&gt;&lt;/a&gt; will process logs from anywhere.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;It doesn’t need an agent installation&lt;/strong&gt;: Built as a regular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; package, it can be used as a standalone tool such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt;. Yes, you can use it to send your &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt; logs to &lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS Cloudwatch&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;No need to change any existing code&lt;/strong&gt;: It can be used with existing applications. Even if they’re not built with javascript.&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  aws-cloudwatch-forwarder 'echo &quot;sample application&quot;'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-do-i-use-it&quot;&gt;How do I use it?&lt;/h2&gt;

&lt;p&gt;Visit the &lt;a href=&quot;https://www.npmjs.com/package/aws-cloudwatch-forwarder&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws-cloudwatch-forwarder&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; page&lt;/a&gt; for the latest documentation&lt;/p&gt;

&lt;h2 id=&quot;i-want-to-see-how-it-works&quot;&gt;I want to see how it works&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/camilin87/aws-cloudwatch-forwarder&quot;&gt;aws-cloudwatch-forwarder is entirely open source&lt;/a&gt;&lt;/p&gt;
</description>
          <pubDate>Sun, 09 Oct 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/10/09/my-own-aws-cloudwatch-forwarder-as-an-npm-package/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/10/09/my-own-aws-cloudwatch-forwarder-as-an-npm-package/</guid>
        </item>
      
    
      
        <item>
          <title>Docker Miami Meetup Presentation Notes</title>
          <description>&lt;p&gt;These are the notes of my &lt;a href=&quot;https://www.meetup.com/Docker-Miami/events/233290123/&quot;&gt;Docker Miami presentation&lt;/a&gt;. Held at &lt;a href=&quot;http://learn01.io/&quot;&gt;learn01.io&lt;/a&gt; on August 24th, 2016.&lt;/p&gt;

&lt;h2 id=&quot;run-the-logspout-container&quot;&gt;Run the logspout container&lt;/h2&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AWS_ACCESS_KEY_ID=XXXXXXXXXXXXX&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AWS_SECRET_ACCESS_KEY=XXXXXXXXXXX&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LOGSPOUT=ignore&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/run/docker.sock:/tmp/docker.sock &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;logspout &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; mdsol/logspout &lt;span class=&quot;s1&quot;&gt;'cloudwatch://us-east-1?DEBUG=1&amp;amp;NOEC2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;run-the-heartbeat-container&quot;&gt;Run the heartbeat container&lt;/h2&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;fast-heartbeat &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LOGSPOUT_GROUP=docker-talk-logs&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    camilin87/fast-heartbeat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;view-the-logs-in-aws-cloudwatch&quot;&gt;View the logs in &lt;a href=&quot;https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logs:&quot;&gt;AWS Cloudwatch&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id=&quot;create-a-metric-on-the-search-term-heartbeat&quot;&gt;Create a metric on the search term &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Heartbeat&lt;/code&gt;&lt;/h2&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws logs put-metric-filter &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--log-group-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;docker-talk-logs&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--filter-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hb-transmission&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--filter-pattern&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&quot;Heartbeat&quot;'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--metric-transformations&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;metricName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'hb-transmission'&lt;/span&gt;,metricNamespace&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;LogMetrics,metricValue&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configure-an-alert-for-our-metric&quot;&gt;Configure an alert for our metric&lt;/h2&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws cloudwatch put-metric-alarm &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--alarm-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hb-error&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--metric-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hb-transmission&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; LogMetrics &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--statistic&lt;/span&gt; Sum &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--period&lt;/span&gt; 60 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--threshold&lt;/span&gt; 10 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--comparison-operator&lt;/span&gt; LessThanThreshold &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--evaluation-periods&lt;/span&gt; 1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--alarm-actions&lt;/span&gt; arn:aws:sns:us-east-1:511794458722:NotifyMe &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--ok-actions&lt;/span&gt; arn:aws:sns:us-east-1:511794458722:NotifyMe &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--insufficient-data-actions&lt;/span&gt; arn:aws:sns:us-east-1:511794458722:NotifyMe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
          <pubDate>Wed, 24 Aug 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/08/24/docker-miami-meetup-presentation-notes/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/08/24/docker-miami-meetup-presentation-notes/</guid>
        </item>
      
    
      
        <item>
          <title>Send your Docker container logs to AWS Cloudwatch using logspout</title>
          <description>&lt;p&gt;As a general rule Docker containers print to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STDOUT&lt;/code&gt; anything that should be logged. This simple approach is nothing but powerful and extensive. &lt;a href=&quot;https://github.com/gliderlabs/logspout&quot;&gt;Logspout&lt;/a&gt; is a lightweight container that forwards the logs of other containers running on the same server. It is built in an extensible way with support for multiple log destinations. This guide will show how to configure &lt;a href=&quot;https://github.com/mdsol/logspout-cloudwatch&quot;&gt;Logspout-Cloudwatch&lt;/a&gt; to forward our logs to &lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS CloudWatch&lt;/a&gt;.&lt;sup id=&quot;fnref:why_cloudwatch&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:why_cloudwatch&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The instructions of this guide are based on &lt;a href=&quot;https://docs.docker.com/v1.9/engine/reference/logging/overview/&quot;&gt;Docker 1.9&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;step-1-configure-aws-cloudwatch&quot;&gt;Step 1: Configure &lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS Cloudwatch&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We need an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Access Key Id&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secret Access Key&lt;/code&gt; that can forward logs to Cloudwatch. Here’s how to &lt;a href=&quot;https://www.tddapps.com/2016/07/01/configure-AWS-cloudwatch-for-log-forwarders/&quot;&gt;configure AWS Cloudwatch for Log Forwarders&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-2-run-the-logspout-cloudwatch-container&quot;&gt;Step 2: Run the &lt;a href=&quot;https://github.com/mdsol/logspout-cloudwatch&quot;&gt;logspout-cloudwatch&lt;/a&gt; container&lt;/h2&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxx&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxx&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LOGSPOUT=ignore&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--volume&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/run/docker.sock:/tmp/docker.sock &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;logspout &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; mdsol/logspout &lt;span class=&quot;s1&quot;&gt;'cloudwatch://us-east-1?DEBUG=1&amp;amp;NOEC2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;What is this?&lt;/em&gt;: Start and configure a container of the image &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mdsol/logspout&lt;/code&gt;. More details on the configuration can be &lt;a href=&quot;https://github.com/mdsol/logspout-cloudwatch&quot;&gt;found here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-3-run-a-container-that-generates-logs&quot;&gt;Step 3: Run a container that generates logs&lt;/h2&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LOGSPOUT_GROUP=sample-docker-logs&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  ubuntu bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'for i in {1..10}; do echo &quot;Hi, the date is $(date)&quot; ; sleep 1; done'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;What is this?&lt;/em&gt;: Start a container that will log the current date ten times and send its logs to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sample-docker-logs&lt;/code&gt; log group.&lt;/p&gt;

&lt;h2 id=&quot;step-4-review-the-logs&quot;&gt;Step 4: Review the logs&lt;/h2&gt;

&lt;p&gt;1- Open the CloudWatch &lt;a href=&quot;https://console.aws.amazon.com/cloudwatch/home?#logStream:group=sample-docker-logs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sample-docker-logs&lt;/code&gt; group&lt;/a&gt;&lt;br /&gt;
2- Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Search Events&lt;/code&gt; &lt;img src=&quot;/images/aws-docker-logs/search-events.png&quot; alt=&quot;Search Events&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Your should be able to see your logs&lt;br /&gt;
&lt;img src=&quot;/images/aws-docker-logs/logs-uploaded.png&quot; alt=&quot;Container logs&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;bonus-docker-cloud-configuration&quot;&gt;Bonus: &lt;a href=&quot;https://cloud.docker.com/&quot;&gt;Docker Cloud&lt;/a&gt; configuration&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.docker.com/&quot;&gt;Docker Cloud&lt;/a&gt; is a SaaS tool run by Docker to manage and automate container deployments. It is very powerful and yet simple to use. &lt;a href=&quot;https://www.docker.com/products/docker-cloud&quot;&gt;More info here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;1- Create and Start a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logging&lt;/code&gt; Stack&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;logspout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mdsol/logspout&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;deployment_strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;every_node&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;autoredeploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;on-failure&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/var/run/docker.sock:/tmp/docker.sock&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxx&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LOGSPOUT=ignore&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cloudwatch://us-east-1?DEBUG=1&amp;amp;NOEC2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;What is this?&lt;/em&gt;: A &lt;a href=&quot;https://support.tutum.co/support/solutions/articles/5000583471-stack-yaml-reference&quot;&gt;Stack YAML file&lt;/a&gt; to configure a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logspout&lt;/code&gt; service. Docker Cloud will deploy a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mdsol/logspout&lt;/code&gt; container on every node under the account. Moreover, it will make sure our container gets restarted in case of a failure.&lt;/p&gt;

&lt;p&gt;2- &lt;a href=&quot;https://cloud.docker.com/_/stack/wizard&quot;&gt;Create and Start &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello-world&lt;/code&gt; Stack&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;helloworld&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;target_num_containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;LOGSPOUT_GROUP=sample-docker-logs&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;bash -c 'for i in {1..10}; do echo &quot;Hi, the date is $(date)&quot; ; sleep 1; done'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:why_cloudwatch&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;em&gt;Why Cloudwatch?&lt;/em&gt; Cloudwatch has excellent support for &lt;a href=&quot;https://blog.opsgenie.com/2014/08/how-to-use-cloudwatch-to-generate-alerts-from-logs&quot;&gt;alerts&lt;/a&gt; and &lt;a href=&quot;http://techblog.netflix.com/2012/01/auto-scaling-in-amazon-cloud.html&quot;&gt;autoscaling&lt;/a&gt;. &lt;a href=&quot;#fnref:why_cloudwatch&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Sat, 02 Jul 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/07/02/send-your-docker-container-logs-to-AWS-Cloudwatch-using-logspout/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/07/02/send-your-docker-container-logs-to-AWS-Cloudwatch-using-logspout/</guid>
        </item>
      
    
      
        <item>
          <title>Configure AWS Cloudwatch for Log Forwarders</title>
          <description>&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS CloudWatch&lt;/a&gt; is a monitoring service to collect logs. It can be configured to accept multiple log sources. As with other AWS services Cloudwatch has detailed security and access control support. These are the steps I take to configure any log forwarder to Cloudwatch.&lt;/p&gt;

&lt;p&gt;This guide will produce an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Access Key Id&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secret Access Key&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;configure-an-access-policy&quot;&gt;Configure an Access Policy&lt;/h3&gt;

&lt;p&gt;Policies are the backbone of AWS security. It is a good practice to write them as restrictive as possible. &lt;sup id=&quot;fnref:policies&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:policies&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;1- Open the &lt;a href=&quot;https://console.aws.amazon.com/iam/home#policies&quot;&gt;IAM Policies&lt;/a&gt; section&lt;br /&gt;
2- Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create Policy&lt;/code&gt; &lt;img src=&quot;/images/aws-docker-logs/create-policy-button.png&quot; alt=&quot;Create Policy&quot; /&gt;&lt;br /&gt;
3- Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create Your Own Policy&lt;/code&gt;&lt;br /&gt;
4- Name it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CloudWatchLogSender&lt;/code&gt;&lt;br /&gt;
5- Add the following text to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Policy Document&lt;/code&gt; section&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2016-07-02&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs:CreateLogGroup&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs:CreateLogStream&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs:DescribeLogGroups&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs:DescribeLogStreams&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs:PutLogEvents&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;What is this?&lt;/em&gt;: A policy that only Allows the actions &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateLogGroup&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateLogStream&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DescribeLogGroups&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DescribeLogStreams&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PutLogEvents&lt;/code&gt; on any resource.&lt;/p&gt;

&lt;h3 id=&quot;create-an-aws-user&quot;&gt;Create an AWS User&lt;/h3&gt;

&lt;p&gt;Having a specialized user just to forward logs can significantly limit the impact of any attack on the account. &lt;sup id=&quot;fnref:overkill&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:overkill&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;1- Open the &lt;a href=&quot;https://console.aws.amazon.com/iam/home#users&quot;&gt;User Management Module&lt;/a&gt;&lt;br /&gt;
2- Create a new user named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CloudWatchLogSender&lt;/code&gt;.&lt;br /&gt;
Make sure to &lt;strong&gt;save these security credentials&lt;/strong&gt; because &lt;strong&gt;this is the last time you’ll see them&lt;/strong&gt;&lt;br /&gt;
&lt;img src=&quot;/images/aws-docker-logs/user-created-2.png&quot; alt=&quot;User Created&quot; /&gt;&lt;br /&gt;
3- Open the &lt;a href=&quot;https://console.aws.amazon.com/iam/home#users/CloudWatchLogSender&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CloudWatchLogSender&lt;/code&gt; user details page&lt;/a&gt;&lt;br /&gt;
4- Click the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Attach Policy&lt;/code&gt; button in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Permissions&lt;/code&gt; tab&lt;br /&gt;
&lt;img src=&quot;/images/aws-docker-logs/attach-policy-button.png&quot; alt=&quot;Attach Policy&quot; /&gt;&lt;br /&gt;
5- Attach the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CloudWatchLogSender&lt;/code&gt; policy&lt;/p&gt;

&lt;p&gt;Your user summary should look like this&lt;br /&gt;
&lt;img src=&quot;/images/aws-docker-logs/user-summary.png&quot; alt=&quot;User Summary&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;
&lt;p&gt;We have created the necessary security provisions to forward logs to AWS Cloudwatch from any source. Moreover, we have credentials that can be used by any forwarder compatible with Cloudwatch.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:policies&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html&quot;&gt;More Info on AWS policies&lt;/a&gt;. &lt;a href=&quot;#fnref:policies&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:overkill&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Restrictive access controls limit the risk of somebody using your account &lt;a href=&quot;http://www.theregister.co.uk/2015/01/06/dev_blunder_shows_github_crawling_with_keyslurping_bots/&quot;&gt;to mine Bitcoins&lt;/a&gt; or some other crazy thing. &lt;a href=&quot;#fnref:overkill&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Fri, 01 Jul 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/07/01/configure-AWS-cloudwatch-for-log-forwarders/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/07/01/configure-AWS-cloudwatch-for-log-forwarders/</guid>
        </item>
      
    
      
        <item>
          <title>Send your Docker container logs to AWS using the log-driver</title>
          <description>&lt;p&gt;Docker supports several &lt;a href=&quot;https://docs.docker.com/engine/admin/logging/overview/&quot;&gt;logging drivers&lt;/a&gt; to forward container logs. These are the steps to configure the &lt;a href=&quot;https://github.com/docker/docker/blob/3effe484e6f572298d0c3490517f57391617aa51/docs/reference/logging/awslogs.md&quot;&gt;AWS CloudWatch log driver&lt;/a&gt; to ship the &lt;a href=&quot;https://hub.docker.com/_/hello-world/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello-world&lt;/code&gt; container&lt;/a&gt; logs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The instructions of this guide are based on &lt;a href=&quot;https://docs.docker.com/v1.9/engine/reference/logging/overview/&quot;&gt;Docker 1.9&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;step-1-configure-aws-cloudwatch&quot;&gt;Step 1: Configure &lt;a href=&quot;https://aws.amazon.com/cloudwatch/&quot;&gt;AWS Cloudwatch&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We need an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Access Key Id&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secret Access Key&lt;/code&gt; that can forward logs to Cloudwatch. Here’s how to &lt;a href=&quot;https://www.tddapps.com/2016/07/01/configure-AWS-cloudwatch-for-log-forwarders/&quot;&gt;configure AWS Cloudwatch for Log Forwarders&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-2-configure-your-docker-daemon-to-use-the-aws-credentials&quot;&gt;Step 2: Configure your docker daemon to use the AWS credentials&lt;/h2&gt;

&lt;h3 id=&quot;mac-os-x&quot;&gt;Mac OS X&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;These instructions are for the &lt;a href=&quot;https://www.docker.com/products/docker-toolbox&quot;&gt;Docker Toolbox&lt;/a&gt; because &lt;a href=&quot;https://docs.docker.com/engine/installation/mac/&quot;&gt;Docker for Mac&lt;/a&gt; is still in beta.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;1- Open a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Docker QuickStart Terminal&lt;/code&gt;&lt;br /&gt;
2- Stop the the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default&lt;/code&gt; docker-machine&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-machine stop default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3- Create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awslogs&lt;/code&gt; docker-machine with the AWS credentials&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-machine create &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; virtualbox &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--engine-env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AWS_REGION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;us-east-1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--engine-env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;xxxxxxxxxxxxxxxx &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--engine-env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;xxxxxxxxxxxxxxxxxxxxxx &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    awslogs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ubuntu&quot;&gt;Ubuntu&lt;/h3&gt;
&lt;p&gt;The following &lt;a href=&quot;https://github.com/docker/docker/issues/16551&quot;&gt;GitHub Issue&lt;/a&gt; details the steps to configure AWS credentials on Ubuntu.&lt;/p&gt;

&lt;h3 id=&quot;docker-cloud&quot;&gt;Docker Cloud&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://forums.docker.com/t/is-it-possible-to-set-the-logging-driver/6666&quot;&gt;No support for log drivers yet&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-3-run-the-container&quot;&gt;Step 3: Run the Container&lt;/h2&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--log-driver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;awslogs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--log-opt&lt;/span&gt; awslogs-region&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;us-east-1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--log-opt&lt;/span&gt; awslogs-group&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;sample-docker-logs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--log-opt&lt;/span&gt; awslogs-stream&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;hello-world-logs &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    hello-world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-4-review-the-logs&quot;&gt;Step 4: Review the logs&lt;/h2&gt;

&lt;p&gt;1- Open the CloudWatch &lt;a href=&quot;https://console.aws.amazon.com/cloudwatch/home?#logStream:group=sample-docker-logs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sample-docker-logs&lt;/code&gt; group&lt;/a&gt;&lt;br /&gt;
2- Open the &lt;a href=&quot;https://console.aws.amazon.com/cloudwatch/home?#logEvent:group=sample-docker-logs;stream=hello-world-logs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello-world-logs&lt;/code&gt; stream&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your should be able to see your logs&lt;br /&gt;
&lt;img src=&quot;/images/aws-docker-logs/hello-world-logs-uploaded.png&quot; alt=&quot;Hello-World container logs&quot; /&gt;&lt;/p&gt;
</description>
          <pubDate>Sun, 26 Jun 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/06/26/send-your-docker-container-logs-to-AWS-using-the-log-driver/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/06/26/send-your-docker-container-logs-to-AWS-using-the-log-driver/</guid>
        </item>
      
    
      
        <item>
          <title>Book Review: The Ideal Team Player</title>
          <description>&lt;p&gt;As with other Lencioni books &lt;a href=&quot;http://amzn.to/1UlWXv4&quot;&gt;The Ideal Team Player&lt;/a&gt; is hard to put down once you start reading it. It emphasizes the significative advantage teamwork can bring to a company. The book revolves around the “Humble, Hungry, Smart” model. What is it and how ideal team players should follow it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/1UlWXv4&quot;&gt;The Ideal Team Player&lt;/a&gt; is far from being a theory book. It provides practical applications of the “Humble, Hungry, Smart” model: interview techniques, interview questions, employee assessment and development techniques, techniques to embed the model in the culture.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/1UlWXv4&quot;&gt;&lt;img src=&quot;/images/books/the-ideal-team-player.jpg&quot; alt=&quot;The Ideal Team Player&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The book describes several personalities I’ve encountered during my years working: the Pawn, the Bulldozer, the Charmer, the Lovable Slacker, the Skillful Politician.&lt;/p&gt;

&lt;p&gt;My one sentence summary would be: “Don’t hire jackasses. If you keep them for too long then the non-jackasses will leave”.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/1UlWXv4&quot;&gt;&lt;img src=&quot;/images/books/no-jackasses.jpg&quot; alt=&quot;No Jackasses&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/1UlWXv4&quot;&gt;Go read it now&lt;/a&gt; and find out for yourself.&lt;/p&gt;
</description>
          <pubDate>Mon, 06 Jun 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/06/06/Book-Review-the-Ideal-team-Player/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/06/06/Book-Review-the-Ideal-team-Player/</guid>
        </item>
      
    
      
        <item>
          <title>How to run Node cron jobs inside a Docker container</title>
          <description>&lt;p&gt;Although there are several tutorials on how to &lt;a href=&quot;https://www.ekito.fr/people/run-a-cron-job-with-docker/&quot;&gt;run cronjobs inside Docker containers&lt;/a&gt;. They mostly focus on very simple tasks that don’t need to read environment variables. Moreover, there are &lt;a href=&quot;https://www.npmjs.com/package/node-schedule&quot;&gt;node specific solutions&lt;/a&gt;. I didn’t like those either because they rely on the stability on a long running process. The following guide is my solution to the problem after many hours of searching through &lt;a href=&quot;http://stackoverflow.com/&quot;&gt;StackOverflow&lt;/a&gt; and good old experimentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;TL;DR:&lt;/em&gt;&lt;/strong&gt; Create your own &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfile&lt;/a&gt; with the following contents&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM camilin87/node-cron:latest

ENV &lt;span class=&quot;nv&quot;&gt;TASK_SCHEDULE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'* * * * *'&lt;/span&gt;
COPY &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /usr/src/app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-base-image&quot;&gt;The base image&lt;/h2&gt;
&lt;p&gt;Given the complexity of scheduling a node cron job, the best way I could find to abstract that out was to create my &lt;a href=&quot;https://hub.docker.com/r/camilin87/node-cron/&quot;&gt;own base image&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;inputs&quot;&gt;Inputs&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/src/app&lt;/code&gt;: Directory where the executable code shall reside. The image will run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt; from this directory.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TASK_SCHEDULE&lt;/code&gt;: Environment variable with the cron schedule. e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TASK_SCHEDULE='* * * * *'&lt;/code&gt; configures a task that runs every minute &lt;a href=&quot;https://help.ubuntu.com/community/CronHowto&quot;&gt;Cron HowTo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;outputs&quot;&gt;Outputs&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/cron.log&lt;/code&gt;: All the execution logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-use-it&quot;&gt;How to use it?&lt;/h2&gt;
&lt;ol&gt;
  &lt;li&gt;Create your own &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfile&lt;/a&gt; based off the &lt;a href=&quot;https://hub.docker.com/r/camilin87/node-cron/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;camilin87/node-cron&lt;/code&gt;&lt;/a&gt; image.&lt;/li&gt;
  &lt;li&gt;Make sure to copy your code into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/src/app&lt;/code&gt;. You should have a &lt;a href=&quot;https://docs.npmjs.com/getting-started/using-a-package.json&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;&lt;/a&gt; file with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start&lt;/code&gt; script. The image will run &lt;a href=&quot;https://docs.npmjs.com/cli/start&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt;&lt;/a&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/src/app&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Specify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TASK_SCHEDULE&lt;/code&gt; environment variable. It can be done in the Dockerfile or in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; command.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a sample &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; for a task that runs every minute.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM camilin87/node-cron:latest

ENV &lt;span class=&quot;nv&quot;&gt;TASK_SCHEDULE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'* * * * *'&lt;/span&gt;
COPY &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /usr/src/app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;how-does-it-work&quot;&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;Our base image is nothing more than a Dockerfile and a couple of shell scripts. &lt;a href=&quot;https://github.com/camilin87/node-cron&quot;&gt;Its source code is free&lt;/a&gt; and &lt;a href=&quot;https://github.com/camilin87/learn-docker&quot;&gt;here’s a sample application&lt;/a&gt; that uses it. If you feel that something can be improved please create a pull request. Let’s deconstruct the base image step by step.&lt;/p&gt;

&lt;h3 id=&quot;dockerfile&quot;&gt;&lt;a href=&quot;https://github.com/camilin87/node-cron/blob/master/Dockerfile&quot;&gt;Dockerfile&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM node:latest                                   &lt;span class=&quot;c&quot;&gt;# base this image off the official node image&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# if you need a specific node version&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# simply change latest for what you want&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# more info here =&amp;gt; https://hub.docker.com/_/node/&lt;/span&gt;

RUN apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; cron      &lt;span class=&quot;c&quot;&gt;# install cron&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# more info =&amp;gt; https://help.ubuntu.com/community/CronHowto&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;#           =&amp;gt; https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#apt-get&lt;/span&gt;

RUN &lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /usr/src/app                          &lt;span class=&quot;c&quot;&gt;# our code will be shipped with the new container&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# into the /usr/src/app directory&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# this line creates the directory preemptively&lt;/span&gt;

COPY ./templates/crontab /tmp/crontab              &lt;span class=&quot;c&quot;&gt;# cronjobs are configured as files&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# with a very specific format&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# the file ./templates/crontab will be the base template&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# for all of our cron jobs&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# it will be explained in details later on&lt;/span&gt;

RUN &lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; /etc/cron.d/my-cron-job                  &lt;span class=&quot;c&quot;&gt;# our cron job configuration will end up in /etc/cron.d/my-cron-job&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# this line creates the file preemptively&lt;/span&gt;

RUN &lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;0644 /etc/cron.d/my-cron-job             &lt;span class=&quot;c&quot;&gt;# give permissions to the cron job configuration file&lt;/span&gt;

RUN &lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; /var/log/cron.log                        &lt;span class=&quot;c&quot;&gt;# the logs from our job&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# this line creates the log file preemptively&lt;/span&gt;

COPY ./templates/setupCron.sh /tmp/setupCron.sh    &lt;span class=&quot;c&quot;&gt;# to execute our cronjob we must run multiple commands&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# it is not possible to do it directly from the Dockerfile&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# the file ./templates/setupCron.sh will be&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# executed when the container runs&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# it will be explained in details later &lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
                                                   &lt;span class=&quot;c&quot;&gt;# more info =&amp;gt; https://docs.docker.com/engine/reference/builder/#cmd&lt;/span&gt;

RUN &lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x /tmp/setupCron.sh                     &lt;span class=&quot;c&quot;&gt;# give execution permissions to the command&lt;/span&gt;

CMD &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/tmp/setupCron.sh&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;                          &lt;span class=&quot;c&quot;&gt;# configure the command to run when the container runs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;templatescrontab&quot;&gt;&lt;a href=&quot;https://github.com/camilin87/node-cron/blob/master/templates/crontab&quot;&gt;templates/crontab&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./templates/crontab&lt;/code&gt; contains the initial template of our &lt;a href=&quot;https://help.ubuntu.com/community/CronHowto&quot;&gt;cron&lt;/a&gt; job.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; root &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /usr/src/app &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/cron.log 2&amp;gt;&amp;amp;1
&lt;span class=&quot;c&quot;&gt;# An empty line is required&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cron-configuration-breakdown&quot;&gt;Cron configuration breakdown&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;User&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Working Directory&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/src/app&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Logs&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/cron.log&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Command&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt; This base image is highly opinionated on that sense. Since it’s running a node cron job it assumes what the command to execute is.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Schedule&lt;/strong&gt;: The schedule is missing from this configuration template because it will be determined when the image runs the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt;. That way it can be configurable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;templatessetupcronsh&quot;&gt;&lt;a href=&quot;https://github.com/camilin87/node-cron/blob/master/templates/setupCron.sh&quot;&gt;templates/setupCron.sh&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The most important file of our image. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./templates/setupCron.sh&lt;/code&gt; is the command that will be executed when our image runs. It will be responsible for making the container environment variables visible to the cron job.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;                                           &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /tmp/.env                   &lt;span class=&quot;c&quot;&gt;# save all the environment variables&lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# into /tmp/.env&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /tmp/.env                                 &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/cron.d/my-cron-job     &lt;span class=&quot;c&quot;&gt;# write the environment&lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# variables into the cron job configuration&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TASK_SCHEDULE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; - /tmp/crontab &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/cron.d/my-cron-job     &lt;span class=&quot;c&quot;&gt;# write the TASK_SCHEDULE variable&lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# into the cron job configuration&lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# along with the contents &lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# of the /tmp/crontab file&lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# which is nothing more than the&lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# ./templates/crontab file from the image repo&lt;/span&gt;

cron &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /var/log/cron.log                                            &lt;span class=&quot;c&quot;&gt;# run cron and append its logs&lt;/span&gt;
                                                                             &lt;span class=&quot;c&quot;&gt;# to the log file from our task&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;why-so-complicated&quot;&gt;Why so complicated?&lt;/h3&gt;
&lt;p&gt;The biggest hurdle was making the environment variables visible to the cron job. &lt;a href=&quot;http://unix.stackexchange.com/a/27291/134094&quot;&gt;Since cron knows nothing about the running shell&lt;/a&gt; the best way I could find was to include them in the crontab file.&lt;/p&gt;

&lt;p&gt;The other caveat was that none of this variable replacing magic could be done in the Dockerfile. Why? Because most of the environment variables are being set only when the image is run. The substitution had to be done at runtime. Thus the need for a big shell script.&lt;/p&gt;
</description>
          <pubDate>Thu, 05 May 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/05/05/how-to-run-node-cron-jobs-in-a-docker-container/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/05/05/how-to-run-node-cron-jobs-in-a-docker-container/</guid>
        </item>
      
    
      
        <item>
          <title>Death by Meeting</title>
          <description>&lt;p&gt;Like &lt;a href=&quot;http://amzn.to/1T2ppr0&quot;&gt;other Patrick Lencioni books&lt;/a&gt; &lt;a href=&quot;http://amzn.to/23qr2hT&quot;&gt;Death by Meeting&lt;/a&gt; is very enjoyable and easy to read. The book is targeted at making your meetings more effective rather than abolishing them altogether.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/23qr2hT&quot;&gt;&lt;img src=&quot;/images/books/death-by-meeting.jpg&quot; alt=&quot;Death by Meeting&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;meeting-requirements&quot;&gt;Meeting Requirements&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://amzn.to/23qr2hT&quot;&gt;Death by Meeting&lt;/a&gt; clearly illustrates the necessity of Conflict and Contextual Structure for effective meetings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conflict&lt;/strong&gt;: Meeting leaders should mine for conflict out of the participants. They should remember attendees that conflict is good and expected. Conflicts not addressed in meetings can easily become politics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contextual Structure&lt;/strong&gt;: Some meetings should be focused on solving an immediate problem while others should be purely brainstorming. Do not mix meeting structures.&lt;/p&gt;

&lt;h2 id=&quot;suggested-meetings&quot;&gt;Suggested Meetings&lt;/h2&gt;
&lt;p&gt;Near the end of the book the author describes the four types of meetings companies should have.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Daily CheckIn&lt;/strong&gt;: Five minutes standing up. Report current activities.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Weekly Tactical&lt;/strong&gt;: 45 to 90 minutes. 60 seconds per participant to provide context. 5 minutes of progress review based on key business metrics. Realtime agenda derived from the progress review. Limited to address short term topics.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Monthly Strategic&lt;/strong&gt;: Deep dive into one or two topics that will impact the business. Two hours per topic. Can be scheduled ad-hoc too for critical issues. Require proper preparation.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quarterly Offsite Review&lt;/strong&gt;: Analyze the business in a more complete manner. One or two days. Team and strategy review. Competitors and industry reviews. Aim to improve team unity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Straight to the point book. Mandatory read for meeting organizers and participants.&lt;/p&gt;
</description>
          <pubDate>Wed, 04 May 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/05/04/Death-by-Meeting/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/05/04/Death-by-Meeting/</guid>
        </item>
      
    
      
        <item>
          <title>The Goal and The Phoenix Project</title>
          <description>&lt;p&gt;These two books are very hard to put down. They both make an emphasis in looking at the big picture and what’s relevant for the company as opposed to individual departments.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;a href=&quot;http://amzn.to/1TKXn0f&quot;&gt;&lt;img src=&quot;/images/books/the-phoenix-project.png&quot; alt=&quot;The Phoenix Project&quot; /&gt;&lt;/a&gt;&lt;/th&gt;
      &lt;th&gt;&lt;a href=&quot;http://amzn.to/1TntDDx&quot;&gt;&lt;img src=&quot;/images/books/the-goal-new.jpg&quot; alt=&quot;The Goal&quot; /&gt;&lt;/a&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;http://amzn.to/1TKXn0f&quot;&gt;The Phoenix Project on Amazon&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;http://amzn.to/1TntDDx&quot;&gt;The Goal on Amazon&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;It doesn’t matter how hard you work if it’s not aligned with the company’s goal&lt;/li&gt;
  &lt;li&gt;A factory where every station is working at 100% of  its capacity is very inefficient&lt;/li&gt;
  &lt;li&gt;The company’s velocity is limited by the slowest link in the chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Written in a very similar style both books favor the idea that asking questions is the only true way of teaching. They both clearly illustrate the importance of cross-team collaboration to impact the company’s bottomline.&lt;/p&gt;
</description>
          <pubDate>Mon, 02 May 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/05/02/the-goal-and-the-phoenix-project/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/05/02/the-goal-and-the-phoenix-project/</guid>
        </item>
      
    
      
        <item>
          <title>Automating Level3 Cache Invalidation</title>
          <description>&lt;p&gt;One of the companies I’ve worked for uses &lt;a href=&quot;http://www.level3.com/&quot;&gt;Level3&lt;/a&gt; as their CDN. As part of the release process for one of the products we had to manually clear the CDN cache. This was a manual multi-step process that -more often than not- was forgotten. Automation to the rescue.&lt;/p&gt;

&lt;h2 id=&quot;the-api&quot;&gt;The API&lt;/h2&gt;
&lt;p&gt;Fortunately, &lt;a href=&quot;http://www.level3.com/&quot;&gt;Level3&lt;/a&gt; has a &lt;a href=&quot;https://mediaportal.level3.com/webhelp/help/Content/API/APIGetStarted.htm&quot;&gt;REST API&lt;/a&gt; with all sort of capabilities. They even provide some &lt;a href=&quot;https://mediaportal.level3.com/webhelp/help/Content/API/API_SampleCode.htm&quot;&gt;Sample Code&lt;/a&gt; in a variety of languages. One of this samples is a &lt;a href=&quot;https://mediaportal.level3.com/webhelp/help/Content/API/API_Debugger.htm&quot;&gt;full-blown debugging tool&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;step-1-get-an-api-key&quot;&gt;Step 1: Get an API Key&lt;/h3&gt;
&lt;p&gt;In order to use the Level3 API you must first &lt;a href=&quot;https://mediaportal.level3.com/webhelp/help/Content/API/API_Key_UI.htm&quot;&gt;get an API Key&lt;/a&gt;. This needs to be done by an account administrator.&lt;/p&gt;

&lt;h2 id=&quot;the-application&quot;&gt;The Application&lt;/h2&gt;
&lt;p&gt;After &lt;del&gt;ruthlessly copying&lt;/del&gt; reviewing the sample code provided by Level3. I was able to create a C# console application to automate the cache invalidation. It receives the following arguments:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level3_api_key&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level3_api_secret&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level3_invalidation_urls&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;level3_notification_email&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the necessary code is &lt;a href=&quot;https://github.com/camilin87/level3_cache_invalidator&quot;&gt;available in GitHub&lt;/a&gt; and &lt;a href=&quot;https://github.com/camilin87/level3_cache_invalidator/blob/master/readme.md&quot;&gt;here’s a rough description&lt;/a&gt; of how it works.&lt;/p&gt;

&lt;h2 id=&quot;octopus-integration&quot;&gt;Octopus Integration&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://octopus.com/&quot;&gt;Octopus&lt;/a&gt; is a great tool to automate and manage deployment pipelines. The Level3 Cache Invalidation was crafted with Octopus in mind so that it can be invoked as part of any automated deployment. This &lt;a href=&quot;https://github.com/camilin87/level3_cache_invalidator/blob/master/readme.md&quot;&gt;Readme file&lt;/a&gt; indicates how the code can be used with Octopus.&lt;/p&gt;
</description>
          <pubDate>Fri, 01 Apr 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/04/01/Automating-Level3-Cache-Invalidation/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/04/01/Automating-Level3-Cache-Invalidation/</guid>
        </item>
      
    
      
        <item>
          <title>Tribal Leadership and why you should read it</title>
          <description>&lt;p&gt;First things first. I think the title of the book is misleading. This is not a business administration book. Moreover, it is far from teaching anyone how to be a leader.&lt;/p&gt;

&lt;p&gt;The book is a self-discovery book that gives names to common behaviors encountered throughout almost every workplace. And a very good one to be fair.&lt;/p&gt;

&lt;p&gt;The book answers &lt;em&gt;Why does it feel like:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Every company has the same issues&lt;/li&gt;
  &lt;li&gt;It is very hard to fix broken processes&lt;/li&gt;
  &lt;li&gt;Perennial failure in spite of being constantly working&lt;/li&gt;
  &lt;li&gt;The people around can’t be as good&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/1olJJW6&quot;&gt;Tribal Leadership&lt;/a&gt; categorizes these and several other behaviors into a set of five simple stages. Not only that, it provides tools to measure and increase the throughput of your organization.&lt;/p&gt;

&lt;p&gt;I don’t want to give too much away. &lt;a href=&quot;http://amzn.to/1olJJW6&quot;&gt;Go read it&lt;/a&gt; if you’re interested in learning more about yourself and your organization dynamics.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/1olJJW6&quot;&gt;&lt;img src=&quot;/images/tribal-leadership/triballeadership.jpg&quot; alt=&quot;Tribal Leadership&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
          <pubDate>Wed, 30 Mar 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/03/30/Tribal-Leadership-and-why-you-should-read-it/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/03/30/Tribal-Leadership-and-why-you-should-read-it/</guid>
        </item>
      
    
      
        <item>
          <title>Demystifying the Open Source</title>
          <description>&lt;p&gt;A lot has been said about open source technologies. Many &lt;a href=&quot;http://fortune.com/2010/08/16/how-corporate-america-went-open-source/&quot;&gt;Fortune 500 businesses run open source software&lt;/a&gt;. However, many misconceptions about the open source still lurk around the enterprise world. This is my best attempt to debunk them.&lt;/p&gt;

&lt;h2 id=&quot;myth-open-source-is-less-secure-because-anyone-can-make-changes&quot;&gt;Myth: Open Source is less secure because anyone can make changes&lt;/h2&gt;
&lt;p&gt;Although there are &lt;a href=&quot;https://www.wikipedia.org/&quot;&gt;open source projects where anyone can freely make changes&lt;/a&gt; this is definitely not the norm.&lt;/p&gt;

&lt;p&gt;Open source projects can be very selective with the contributions they accept.&lt;/p&gt;

&lt;p&gt;Don’t believe me? Read &lt;a href=&quot;https://lkml.org/lkml/2012/12/23/75?cm_mc_uid=71870513194014591758915&amp;amp;cm_mc_sid_50200000=1459175891&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;http://lkml.iu.edu/hypermail/linux/kernel/1510.3/02866.html&quot;&gt;this&lt;/a&gt;!&lt;/p&gt;

&lt;h2 id=&quot;myth-open-source-is-less-secure-because-anyone-can-see-the-code&quot;&gt;Myth: Open Source is less secure because anyone can see the code&lt;/h2&gt;
&lt;p&gt;Having your code open sourced, widely reviewed, and used by the entire world, &lt;a href=&quot;http://heartbleed.com/&quot;&gt;won’t magically protect you from exploits&lt;/a&gt;. Even more, exploits also &lt;a href=&quot;https://www.theiphonewiki.com/wiki/Jailbreak_Exploits&quot;&gt;affect proprietary software where the code is not available&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That being said, having the ability of inspecting a library’s source before using it is way more secure than trusting a proprietary software vendor sales team.&lt;/p&gt;

&lt;p&gt;Want to learn more? Read &lt;a href=&quot;http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/open-source-security.html&quot;&gt;this&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Open-source_software_security&quot;&gt;this&lt;/a&gt; and &lt;a href=&quot;https://www.ibm.com/developerworks/mydeveloperworks/blogs/6e6f6d1b-95c3-46df-8a26-b7efd8ee4b57/entry/is_open_source_software_less_secure230?lang=en&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;myth-open-source-is-not-production-ready&quot;&gt;Myth: Open Source is not production ready&lt;/h2&gt;
&lt;p&gt;This has been the hardest myth to debunk because of its absurdity.&lt;/p&gt;

&lt;p&gt;Seventy-eight percent of respondents to the &lt;a href=&quot;https://www.blackducksoftware.com/future-of-open-source&quot;&gt;Ninth Annual Future of Open Source Survey of 2015&lt;/a&gt; determined that their companies run part or all of its operations on open source software. It doesn’t get more production ready than that.&lt;/p&gt;

&lt;h2 id=&quot;myth-open-source-tools-development-stalls&quot;&gt;Myth: Open Source tools development stalls&lt;/h2&gt;
&lt;p&gt;If anything open source drives innovation even faster.&lt;/p&gt;

&lt;h3 id=&quot;example-1-containers&quot;&gt;Example 1: Containers&lt;/h3&gt;
&lt;p&gt;Containers have proven to be the de facto standard for deployments. The open source community has been running containers for years. However, Windows based enterprises have had to wait several years for Microsoft to provide container support.&lt;/p&gt;

&lt;h3 id=&quot;example-2-your-cms-in-the-cloud&quot;&gt;Example 2: Your CMS in the Cloud&lt;/h3&gt;
&lt;p&gt;Many enterprises are migrating their platforms to the cloud. &lt;a href=&quot;http://umbraco.com&quot;&gt;Umbraco&lt;/a&gt; users can &lt;a href=&quot;https://azure.microsoft.com/en-us/blog/scalable-umbraco-cms-solution-for-azure-web-apps/&quot;&gt;seamlessly host their existing websites on Azure with very little effort&lt;/a&gt;. &lt;a href=&quot;https://wordpress.com/&quot;&gt;WordPress&lt;/a&gt; has had &lt;a href=&quot;https://wpengine.com/&quot;&gt;PaaS support&lt;/a&gt; for ages. I wish the same could be said of &lt;a href=&quot;http://www.sitecoreonazure.net/&quot;&gt;other proprietary CMS&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;myth-open-source-is-not-a-viable-business-model&quot;&gt;Myth: Open Source is not a viable business model&lt;/h2&gt;
&lt;p&gt;These are just a few out of many successful companies that have been built on open source software:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.redhat.com/en/about/company&quot;&gt;RedHat&lt;/a&gt; &lt;a href=&quot;https://finance.yahoo.com/q/ks?s=RHT&quot;&gt;Valuation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.mysql.com/about/&quot;&gt;MySql&lt;/a&gt; &lt;a href=&quot;http://techcrunch.com/2008/01/16/sun-picks-up-mysql-for-1-billion-open-source-is-a-legitimate-business-model/&quot;&gt;Purchased for One Billion dollars&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://automattic.com/about/&quot;&gt;Automattic&lt;/a&gt; &lt;a href=&quot;http://blogs.wsj.com/venturecapital/2014/05/05/automattic-valued-at-1-16-billion-says-it-doesnt-need-ipo/&quot;&gt;Valuation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.docker.com/company&quot;&gt;Docker&lt;/a&gt; &lt;a href=&quot;http://www.forbes.com/sites/mikekavis/2015/07/16/5-reasons-why-docker-is-a-billion-dollar-company/#6591791619c5&quot;&gt;Valuation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/company&quot;&gt;MongoDb&lt;/a&gt; &lt;a href=&quot;http://blogs.wsj.com/digits/2015/01/14/big-data-startup-mongodb-now-valued-at-1-6-bililon/&quot;&gt;Valuation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://hortonworks.com/about-us/quick-facts/&quot;&gt;Horton Works&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;myth-open-source-is-free&quot;&gt;Myth: Open Source is free&lt;/h2&gt;
&lt;p&gt;While downloading binaries for the majority of open source software is free. Releasing your product to production may not. The companies behind open source projects make their money with &lt;a href=&quot;http://www.cnet.com/news/nginx-tries-converting-web-server-popularity-into-money/&quot;&gt;Corporate Support&lt;/a&gt;, &lt;a href=&quot;http://www.labnol.org/internet/blogging/how-wordpress-makes-money/7576/&quot;&gt;VIP Hosting&lt;/a&gt;, or &lt;a href=&quot;http://www.businessinsider.com/docker-introduces-commercial-subscription-plan-2015-6&quot;&gt;Commercial Subscriptions&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;myth-open-source-is-hard-for-it&quot;&gt;Myth: Open Source is hard for IT&lt;/h2&gt;
&lt;p&gt;This is debatable. As with anything different there will be a learning curve. The same can be said of proprietary tools with bad documentation and no access to the code.&lt;/p&gt;

&lt;h2 id=&quot;closing-remarks&quot;&gt;Closing Remarks&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Should you drop your proprietary stack and build everything with open source technologies? Certainly not. Unless a full rewrite is imminent and open source technologies can help you achieve your goals in a cleaner and faster way.&lt;/li&gt;
  &lt;li&gt;Should you favor open source over proprietary? It depends on the scenario. Always look at the problem you’re trying to solve and the impact of the tools on your organization.&lt;/li&gt;
  &lt;li&gt;Please stop mystifying open source tools as only good for hackers and startups. The open source ecosystem is more than prepared to solve your enterprise problems.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Mon, 28 Mar 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/03/28/demystifying-the-open-source/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/03/28/demystifying-the-open-source/</guid>
        </item>
      
    
      
        <item>
          <title>The Importance of Logs</title>
          <description>&lt;p&gt;Logs are one of the most important components of an application. They provide a quick and reliable way to verify behavior and diagnose errors. Properly written logs can be mined to take business decisions. And yet, they are often neglected.&lt;/p&gt;

&lt;h2 id=&quot;antipattern-no-logs-on-a-production-running-system&quot;&gt;Antipattern: No logs on a production running system&lt;/h2&gt;
&lt;p&gt;No matter what your software does, it should have logs. &lt;sup id=&quot;fnref:corner_cases&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:corner_cases&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Logs will give you a quick way to verify the application behavior.&lt;/p&gt;

&lt;h2 id=&quot;antipattern-logging-only-errors&quot;&gt;Antipattern: Logging ONLY errors&lt;/h2&gt;
&lt;p&gt;Logging errors is certainly necessary. However, normal system behavior should also be logged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Because it gives visibility of how the software is being used. It creates the infrastructure to monitor the business performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: This website serves approximately 15,000 registered users a day. Logging every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Successful Login&lt;/code&gt; event gives the ability to configure an alert when the number of logins falls below 10,000. A low number of logins can impact the business and needs to be thoroughly analyzed. Moreover, if the number of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Successful Login&lt;/code&gt; events goes above 25,000 there could be an issue with another part of the system such as the Session Storage Server.&lt;/p&gt;

&lt;h2 id=&quot;myth-logging-will-impact-performance&quot;&gt;Myth: Logging will impact performance&lt;/h2&gt;
&lt;p&gt;Garbage Collection too and that hasn’t stopped us from using it.&lt;/p&gt;

&lt;p&gt;Although Logging can definitely add some minimal overhead not knowing how your system is behaving can &lt;strong&gt;certainly&lt;/strong&gt; impact performance. &lt;sup id=&quot;fnref:optimization&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:optimization&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;best-practice-auditable-logs&quot;&gt;Best Practice: Auditable logs&lt;/h2&gt;
&lt;p&gt;Properly written logs &lt;strong&gt;clearly&lt;/strong&gt; describe the chronological history of every user interaction since they started using the application until they finished.&lt;/p&gt;

&lt;p&gt;Include in every log message enough information to quickly filter all the related events. e.g. In a web store this would be the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sessionId&lt;/code&gt;. In a mobile banking app this could be a unique UUID generated when the application started.&lt;/p&gt;

&lt;p&gt;Try to include as much information as possible to determine the log line originator such as the class and method.&lt;/p&gt;

&lt;h2 id=&quot;best-practice-log-levels&quot;&gt;Best Practice: Log Levels&lt;/h2&gt;
&lt;p&gt;Not every log line is written for the same audience. A business analyst could make little sense out of a stack trace. Some events are written to help engineers diagnose an intermittent issue while others are written to build business performance dashboards.&lt;/p&gt;

&lt;p&gt;Segregate your log lines into their appropriate levels.&lt;/p&gt;

&lt;h3 id=&quot;level-selection-guidelines&quot;&gt;Level Selection Guidelines&lt;/h3&gt;
&lt;p&gt;Although the number of log levels may vary these four are almost ubiquitous to every development platform.&lt;/p&gt;

&lt;h4 id=&quot;error&quot;&gt;Error&lt;/h4&gt;
&lt;p&gt;The application encountered an irrecoverable error.&lt;/p&gt;

&lt;p&gt;There should be alerts configured to notify the required personnel when any of these appear. An Error event should be treated as an outage. e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[ERROR] Database Connection Timeout&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When logging errors try to include as much information as possible such as stack trace and method parameters. These will help you diagnose issues faster.&lt;/p&gt;

&lt;h4 id=&quot;warning&quot;&gt;Warning&lt;/h4&gt;
&lt;p&gt;The application encountered a non-fatal error.&lt;/p&gt;

&lt;p&gt;These should be monitored but are not urgent. e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[WARNING] 404 Not Found&lt;/code&gt; errors.&lt;/p&gt;

&lt;p&gt;Alerts can certainly be configured on warning messages. For example an abnormal number of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[WARNING] Failed Captcha Attempt&lt;/code&gt; could indicate a problem with the website rather than user error.&lt;/p&gt;

&lt;p&gt;The number of Errors in your logs should be zero. Any abnormal behavior that is not an emergency should be a Warning. This practice will simplify your decision process.&lt;/p&gt;

&lt;h4 id=&quot;debug&quot;&gt;Debug&lt;/h4&gt;
&lt;p&gt;Information to be read only by developers while diagnosing an issue.&lt;/p&gt;

&lt;p&gt;e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[DEBUG] Model Info retrieved from the Database&lt;/code&gt;. This log level will help verify the behavior of the program but may add a lot of noise for non-debugging operations.&lt;/p&gt;

&lt;h4 id=&quot;info&quot;&gt;Info&lt;/h4&gt;
&lt;p&gt;Important application behavior.&lt;/p&gt;

&lt;p&gt;This level would be read by developers as well as business analysts and log mining tools. This is the most important level because it provides the infrastructure to analyze the business performance in real time. e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[INFO] Payment Completed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Keep you Info logs as readable as possible in a format that anyone can understand.&lt;/p&gt;

&lt;h2 id=&quot;best-practice-log-aggregation-tool&quot;&gt;Best Practice: Log Aggregation tool&lt;/h2&gt;
&lt;p&gt;Gone are the days of manually searching through log files.&lt;/p&gt;

&lt;p&gt;Several powerful log aggregation tools are available that can streamline your logs processing workflow. They take care of shipping the logs to a central location to be aggregated and searched through a nice web GUI. These tools come with lots of capabilities such as dashboards and alerts.&lt;/p&gt;

&lt;p&gt;My personal favorite is &lt;a href=&quot;http://www.splunk.com/&quot;&gt;Splunk&lt;/a&gt;. If its cost seems prohibitive, there are free alternatives such as the &lt;a href=&quot;https://www.elastic.co/webinars/introduction-elk-stack&quot;&gt;ELK Stack&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;best-practice-log-key-value-pairs-as-opposed-to-events&quot;&gt;Best Practice: Log key value pairs as opposed to events&lt;/h2&gt;
&lt;p&gt;A lot of benefit can be reaped from logging events as key value pairs. Favor the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[INFO] Actor=LoginService; Action=Login_Attempt; Result=True
[INFO] Actor=LoginService; Action=Login_Attempt; Result=False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;in opposition to:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[INFO] LoginService; Login Succeeded
[INFO] LoginService; Login Failed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Key value pairs can greatly help you aggregate and analyze events in a similar way to a database table.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Properly written logs are a powerful tool that can generate realtime feedback for the business as well as for the technical team. Use them wisely to unleash their full potential.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:corner_cases&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Sometimes, it is impractical for applications to write anything due to their low latency and high volume requirements. Chances are your app is not one of those. Regardless, every application needs a way to verify its behavior. &lt;a href=&quot;#fnref:corner_cases&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:optimization&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Nine out of ten times your production systems will have more than enough capacity to handle the load so you won’t have to tweak anything. If performance becomes an issue because of the logs buy more/better hardware. Just leave the logs alone. &lt;a href=&quot;#fnref:optimization&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Tue, 22 Mar 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/03/22/The-importance-of-logs/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/03/22/The-importance-of-logs/</guid>
        </item>
      
    
      
        <item>
          <title>General POS design considerations</title>
          <description>&lt;p&gt;For the first years of my career I built a few Point of Sale (POS) systems using C# and WPF. All of these systems had the same working principle: the software was installed in a remote terminal that customers would use to make transactions. They also shared the same pain points such as updates, database migrations, and physical devices reliability. The following are lessons learned from painstaking experiences and a few nights without sleep. The earlier in the project these get incorporated the more stable the overall system will be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR: Building a POS system is hard. Brace yourself.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;support&quot;&gt;Support&lt;/h2&gt;

&lt;p&gt;Distributed systems are very hard to manage. [&lt;em&gt;citation_needed&lt;/em&gt;] The better your support tools are the more stable your system will be. &lt;sup id=&quot;fnref:zapier_support&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:zapier_support&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Invest time building support tools rather than fire-fighting outages.&lt;/p&gt;

&lt;h3 id=&quot;comprehensive-and-detailed-transactional-logging&quot;&gt;Comprehensive and detailed transactional logging&lt;/h3&gt;
&lt;p&gt;Logs are an important part of every POS system. They are the backbone of the verification and diagnostics process. Unfortunately, not all logs are properly written nor actively read. Gone are the days where logs were only used to debug errors. Integrate a log aggregation tool such as &lt;a href=&quot;https://www.elastic.co/&quot;&gt;Elastic Search&lt;/a&gt; or &lt;a href=&quot;http://www.splunk.com/&quot;&gt;Splunk&lt;/a&gt; into your overall design.&lt;/p&gt;

&lt;p&gt;For traceability and debugging purposes, it is important to correlate related events. Devise a way to link all the events related to the same transaction, whether using the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Id&lt;/code&gt; field in multiple log lines or logging everything all at once in the same log line. This practice will help you automate system performance profiling.&lt;/p&gt;

&lt;p&gt;Last but not least, avoid having multiple log files. They are harder to read.&lt;/p&gt;

&lt;h3 id=&quot;system-heartbeat&quot;&gt;System heartbeat&lt;/h3&gt;
&lt;p&gt;Make each terminal periodically transmit a heartbeat with all the relevant diagnostics information. This heartbeat will allow you to create a comprehensive dashboard of the entire ecosystem health.&lt;/p&gt;

&lt;p&gt;Moreover, you’d be able to proactively fix errors such as shipping supplies when the printer is low on paper.&lt;/p&gt;

&lt;h3 id=&quot;profile-memory-consumption-early-and-frequently&quot;&gt;Profile memory consumption early and frequently&lt;/h3&gt;
&lt;p&gt;Almost every system running for long periods of time will be subject to some sort of performance degradation due to memory leaks.&lt;/p&gt;

&lt;h3 id=&quot;readily-available-remote-access-to-the-terminals&quot;&gt;Readily available remote access to the terminals&lt;/h3&gt;
&lt;p&gt;Every once in a while a support technician needs to log into a terminal to diagnose a weird issue or help a customer setup something. The frequency of these interactions is inversely proportional to the system robustness.&lt;/p&gt;

&lt;p&gt;Plan for a mechanism to remote into the terminals and control their screen. &lt;a href=&quot;http://www.teamviewer.com/&quot;&gt;TeamViewer&lt;/a&gt; has a very comprehensive product that can maintain a database of terminal connection details.&lt;/p&gt;

&lt;h3 id=&quot;lock-and-control-every-software-aspect&quot;&gt;Lock and control every software aspect&lt;/h3&gt;
&lt;p&gt;Customers can be your worst enemies when fiddling with the system. Lock everything in the remote machines. Disable the keyboard. Disable the USB ports. It will save a lot of support calls.&lt;/p&gt;

&lt;p&gt;If possible ship a clean Ghost image embedded into the machine, so that troubleshooting becomes as simple as re-image the computer and apply the configuration.&lt;/p&gt;

&lt;h3 id=&quot;one-click-diagnostics-tool&quot;&gt;One-Click Diagnostics tool&lt;/h3&gt;
&lt;p&gt;Build a One-Click Diagnostics tool that users and support personnel can use to verify all the components are working as expected. Include new dependencies in the tool before adding them to production.&lt;/p&gt;

&lt;h3 id=&quot;reprint-anything&quot;&gt;Reprint anything&lt;/h3&gt;
&lt;p&gt;Design the system in a way that can reprint any receipt as the customer saw it. Build some functionality to make these receipts available from a website.&lt;/p&gt;

&lt;h3 id=&quot;rotate-your-developers-through-support&quot;&gt;Rotate your developers through support&lt;/h3&gt;
&lt;p&gt;Make your developers take support calls. Incorporate their feedback into subsequent updates. Your system will become more robust and resilient in a blink of an eye. &lt;sup id=&quot;fnref:37s_support&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:37s_support&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;updates&quot;&gt;Updates&lt;/h2&gt;
&lt;p&gt;Updates are the backbone of distributed systems. Upcoming system versions should make it to the remote locations in a fast and reliable manner.&lt;/p&gt;

&lt;h3 id=&quot;embedded-updates-system-from-day-one&quot;&gt;Embedded updates system from day one&lt;/h3&gt;
&lt;p&gt;Consider the updates system from the early project stages. Integrate this system into the main application. Don’t build it as a black box. Beware, this is not a permission to build a monolithic application.&lt;/p&gt;

&lt;p&gt;Design the updates system in a way that doesn’t interrupt the business flow. If possible implement online updates that require none to very little downtime.&lt;/p&gt;

&lt;p&gt;Many locations have their own IT Security team with all sort of specific firewall rules. Always download files over HTTPS on port 443 to streamline terminal setup.&lt;/p&gt;

&lt;h3 id=&quot;limit-the-number-of-external-dependencies-upgrade-them-as-regular-application-updates&quot;&gt;Limit the number of external dependencies. Upgrade them as regular application updates.&lt;/h3&gt;
&lt;p&gt;Limit the number of external dependencies as much as possible. External dependencies impose a big burden on system and compatibility testing. Automate their installation as part of the update process. It will prevent a lot of support calls.&lt;/p&gt;

&lt;h3 id=&quot;one-click-installationconfiguration&quot;&gt;One-click installation/configuration&lt;/h3&gt;
&lt;p&gt;This cannot be stressed enough. It will prevent countless user input errors. It is one of those things that once you have it cannot be lived without.&lt;/p&gt;

&lt;h3 id=&quot;same-binaries-for-updates-and-installation&quot;&gt;Same binaries for updates and installation&lt;/h3&gt;
&lt;p&gt;This will simplify your build, deployment, and support process. Make the installer smart enough to detect when a brand new installation or an upgrade is taking place.&lt;/p&gt;

&lt;h2 id=&quot;databases&quot;&gt;Databases&lt;/h2&gt;
&lt;p&gt;There will come a time when the terminals will need a local database. Try to limit their usage to store the data that hasn’t been transmitted. Databases come with their fair share of issues too.&lt;/p&gt;

&lt;h3 id=&quot;use-an-embedded-lightweight-database&quot;&gt;Use an embedded lightweight database&lt;/h3&gt;
&lt;p&gt;Avoid heavyweight systems such as SQL server, it will eat up your memory. Moreover, it will be a pain to upgrade. Choose an embedded database system such as &lt;a href=&quot;https://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; which is easier to maintain.&lt;/p&gt;

&lt;h3 id=&quot;easy-db-migrations&quot;&gt;Easy db migrations&lt;/h3&gt;
&lt;p&gt;Your schema will change. Be prepared.&lt;/p&gt;

&lt;h2 id=&quot;configuration&quot;&gt;Configuration&lt;/h2&gt;
&lt;p&gt;Configuration problems can bring any system to halt. Manage your configurations with an iron fist.&lt;/p&gt;

&lt;h3 id=&quot;exactly-one-readonly-configuration-that-can-only-be-changed-by-developers&quot;&gt;Exactly one readonly configuration that can only be changed by developers&lt;/h3&gt;
&lt;p&gt;The readonly part is very important, it would guarantee the integrity of the system once it gets deployed. The last thing you want is customers fiddling with settings such as COM port speed or toggling ON unfinished features.&lt;/p&gt;

&lt;p&gt;Avoid having multiple configurations it will make troubleshooting and support harder.&lt;/p&gt;

&lt;h3 id=&quot;exactly-one-readwrite-configuration-that-can-change-at-any-time&quot;&gt;Exactly one read/write configuration that can change at any time&lt;/h3&gt;
&lt;p&gt;This would be the configuration for things your users can change such as the printer COM port or the terminal number that identifies the store.&lt;/p&gt;

&lt;p&gt;Design your system in a way that these configuration changes can occur on the fly without the need of a restart or any other provision. This will save countless hours of troubleshooting. Moreover these configuration changes can potentially be remotely triggered by support personnel.&lt;/p&gt;

&lt;h3 id=&quot;the-configuration-for-each-individual-location-should-be-transmitted-and-accounted-for-in-the-servers&quot;&gt;The configuration for each individual location should be transmitted and accounted for in the servers&lt;/h3&gt;
&lt;p&gt;Uncountable benefits can be achieved by storing all the configurations in a centralized location.&lt;/p&gt;

&lt;p&gt;On the event of hardware failure the configuration could be restored into a separate machine and the business could be up and running in minutes. Configuration errors could be pro-actively diagnosed by scheduled jobs.&lt;/p&gt;

&lt;h2 id=&quot;development&quot;&gt;Development&lt;/h2&gt;
&lt;p&gt;The following are development guidelines to keep in mind. In retrospective they look a lot like &lt;a href=&quot;http://martinfowler.com/articles/continuousIntegration.html&quot;&gt;Continuous Integration&lt;/a&gt; best practices.&lt;/p&gt;

&lt;h3 id=&quot;dev-should-not-use-absolute-paths&quot;&gt;Dev should not use absolute paths&lt;/h3&gt;
&lt;p&gt;Never use absolute paths in the code. It will prevent you from having multiple versions of the system installed in the same box. It will lock you down to a specific folder structure. Enforce this practice with strict code review.&lt;/p&gt;

&lt;h3 id=&quot;version-number-should-be-specified-in-only-one-place&quot;&gt;Version number should be specified in only one place&lt;/h3&gt;
&lt;p&gt;Have a single version number in a single place. The database and the software should have the same version. Display and transmit this version number as frequently as possible.&lt;/p&gt;

&lt;h3 id=&quot;automated-builds&quot;&gt;Automated builds&lt;/h3&gt;
&lt;p&gt;I remember working in a system where Build Day was Pizza Night. Please don’t do that. &lt;a href=&quot;http://martinfowler.com/articles/continuousIntegration.html#AutomateTheBuild&quot;&gt;Continuous Integration - Automate the Build&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;automated-releases&quot;&gt;Automated releases&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://martinfowler.com/articles/continuousIntegration.html#AutomateDeployment&quot;&gt;Continuous Integration - Automate Deployment&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;move-functionality-from-the-terminals-off-to-the-servers&quot;&gt;Move functionality from the terminals off to the servers&lt;/h3&gt;
&lt;p&gt;Terminals should only process transactions. Reports and other sorts of housekeeping functionality should be implemented on the servers. This will allow the system in the store to be operational while other housekeeping tasks are in progress. Consequently, system enhancements and updates will be less costly because it is easier to update a few servers than ship binaries to thousands of stores.&lt;/p&gt;

&lt;h3 id=&quot;binaries-signing&quot;&gt;Binaries signing&lt;/h3&gt;
&lt;p&gt;Sign your binaries during the build process. It will prevent malicious users from tampering with them and compromising your system.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:zapier_support&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://zapier.com/blog/support-driven-development/&quot;&gt;Support Driven Development&lt;/a&gt; &lt;a href=&quot;#fnref:zapier_support&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:37s_support&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://signalvnoise.com/posts/3676-everyone-on-support&quot;&gt;Everyone on Support&lt;/a&gt; &lt;a href=&quot;#fnref:37s_support&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Tue, 02 Feb 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/02/02/General-POS-design-considerations/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/02/02/General-POS-design-considerations/</guid>
        </item>
      
    
      
        <item>
          <title>Compiler Warning CS0659</title>
          <description>&lt;p&gt;The majority of C# developers have found &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/xxhbfytk.aspx&quot;&gt;Compiler Warning CS0659&lt;/a&gt; at some point of their careers. Ignoring this warning can produce unexpected program behavior. The following is summary of the impact this warning can have on a codebase and how to prevent it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR: Override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; whenever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equals&lt;/code&gt; is overridden&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;reference-equality&quot;&gt;Reference Equality&lt;/h2&gt;
&lt;p&gt;Let’s consider the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dog&lt;/code&gt; class in C#.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the following usage of it.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The snippet of code from above prints &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;False&lt;/code&gt;. For reference types the default implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equals&lt;/code&gt; compares the references instead of the values where they are pointing to. &lt;sup id=&quot;fnref:object_equals&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:object_equals&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/camilin87/CS0659/commit/5d488e6d6127111167e9a8f36fe83a771e36cb9c?diff=unified&quot;&gt;Github Commit 5d488e6d6127111167e9a8f36fe83a771e36cb9c&lt;/a&gt; contains the sample source code up to this point&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;semantic-equality&quot;&gt;Semantic Equality&lt;/h2&gt;

&lt;p&gt;Let’s enhance the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dog&lt;/code&gt; class so that comparisons between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dogs&lt;/code&gt; have a better semantic meaning.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After overriding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equals&lt;/code&gt; method the following code prints &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/camilin87/CS0659/commit/7abeb0d9b8dbf7c09124485fb0f7e3bd18257b18&quot;&gt;Github Commit 7abeb0d9b8dbf7c09124485fb0f7e3bd18257b18&lt;/a&gt; contains the sample source code up to this point&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;warning-cs0659&quot;&gt;Warning CS0659&lt;/h2&gt;
&lt;p&gt;Once we compile the code we get the following warning.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;warning CS0659: ‘Dog’ overrides Object.Equals(object o) but does not override Object.GetHashCode()&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/xxhbfytk.aspx&quot;&gt;Compiler Warning (level 3) CS0659&lt;/a&gt; is straightforward and direct to the point: If you override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equals&lt;/code&gt; you need to override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;This is not one of those warnings that can safely be ignored. &lt;sup id=&quot;fnref:warning_levels&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:warning_levels&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; It will come back to bite you. Let’s consider the following usages of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dog&lt;/code&gt; class.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dogShelter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Pete&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dogShelter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The code from above prints &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;False&lt;/code&gt; because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fido&lt;/code&gt; cannot be found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dogShelter&lt;/code&gt;.&lt;br /&gt;
&lt;img src=&quot;https://i.giphy.com/vdLRwjtIZ7g3K.gif&quot; alt=&quot;There comes the bite in the ass&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;why&quot;&gt;Why?&lt;/h3&gt;
&lt;p&gt;Overriding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equals&lt;/code&gt; is not enough. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; method provides a numeric value for quick equality checks such as those used in collections like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dictionary&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashTable&lt;/code&gt;. &lt;sup id=&quot;fnref:gethashcode&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:gethashcode&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/camilin87/CS0659/commit/d91504bfedcb60ab86fffe72dcac88fb775405de&quot;&gt;Github Commit d91504bfedcb60ab86fffe72dcac88fb775405de&lt;/a&gt; contains the sample source code up to this point&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;

&lt;p&gt;Override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; whenever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Equals&lt;/code&gt; is overridden. The following is an implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dog&lt;/code&gt; where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; is overridden. &lt;sup id=&quot;fnref:resharper_implementation&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:resharper_implementation&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;unchecked&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;397&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With this implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dog&lt;/code&gt; in place, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fido&lt;/code&gt; can be found in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dogShelter&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dogShelter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Pete&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dogShelter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/camilin87/CS0659/commit/98c9adc1fff2aee2362fd494f3a43cdb7f4d3d8a&quot;&gt;Github Commit 98c9adc1fff2aee2362fd494f3a43cdb7f4d3d8a&lt;/a&gt; contains the sample source code up to this point&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;in-a-nutshell&quot;&gt;In a nutshell&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object.GetHashCode&lt;/code&gt; is as important as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object.Equals&lt;/code&gt; for equality matters. Never override one without the other.&lt;/p&gt;

&lt;h2 id=&quot;update-equality-is-hard-&quot;&gt;Update: Equality is Hard &lt;sup id=&quot;fnref:dotnet_equality&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:dotnet_equality&quot; class=&quot;footnote&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/h2&gt;
&lt;p&gt;Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dog&lt;/code&gt; is a mutable object &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; will return different values when either of its properties change making it impossible to be found in dictionaries. Down the rabbit hole we go. To overcome this issue we have to make its properties readonly.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;unchecked&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;397&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;^&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now our Program looks as follows:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dogShelter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Pete&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Fido&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dogShelter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ContainsKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fido&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/camilin87/CS0659/commit/dffbd97927070b05c623192a788a33b97e7101c7&quot;&gt;Github Commit dffbd97927070b05c623192a788a33b97e7101c7&lt;/a&gt; contains the sample source code up to this point&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;value-types-to-the-rescue&quot;&gt;Value Types to the Rescue&lt;/h3&gt;
&lt;p&gt;An easier way to get rid of all of this Equality nonsense would be to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dog&lt;/code&gt; a value type. That way we’d get equality for free.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dog&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Weight&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/camilin87/CS0659/commit/b756e87a63abec9ea8c069bc5d9c09b2a2376311&quot;&gt;Github Commit b756e87a63abec9ea8c069bc5d9c09b2a2376311&lt;/a&gt; contains the sample source code up to this point&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:object_equals&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/bsc2ak47(v=vs.110).aspx#&quot;&gt;Object.Equals Method (Object)&lt;/a&gt; &lt;a href=&quot;#fnref:object_equals&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:warning_levels&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://irisclasson.com/2012/11/19/stupid-question-87-warning-levels-what-are-they-which-level-to-use-and-should-they-be-treated-as-errors/&quot;&gt;Warning levels, what are they, which level to use, and should they be treated as errors?&lt;/a&gt; &lt;a href=&quot;#fnref:warning_levels&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:gethashcode&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx&quot;&gt;Object.GetHashCode Method ()&lt;/a&gt; &lt;a href=&quot;#fnref:gethashcode&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:resharper_implementation&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.jetbrains.com/resharper/&quot;&gt;ReSharper&lt;/a&gt; users can benefit from automatic generation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetHashCode&lt;/code&gt; implementations &lt;a href=&quot;#fnref:resharper_implementation&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:dotnet_equality&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://www.aaronstannard.com/overriding-equality-in-dotnet/&quot;&gt;The Right Way to do Equality in C#&lt;/a&gt; &lt;a href=&quot;#fnref:dotnet_equality&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Sat, 30 Jan 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/01/30/Compiler-Warning-CS0659/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/01/30/Compiler-Warning-CS0659/</guid>
        </item>
      
    
      
        <item>
          <title>Waste Prevention</title>
          <description>&lt;p&gt;Waste is the enemy of value. It is usually manifested as building the wrong product. The following are two waste prevention techniques I’ve learned during my years as an engineer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the third article of the Deliver Value Fallacy series. &lt;a href=&quot;/2016/01/26/The-Deliver-Value-Fallacy?source=part3&quot;&gt;Part 1&lt;/a&gt; covers Delivering Waste and &lt;a href=&quot;/2016/01/28/The-Deliver-Value-Fallacy-Technical-Debt-Part-2?source=part3&quot;&gt;Part 2&lt;/a&gt; covers Technical Debt.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;data-driven&quot;&gt;Data Driven&lt;/h2&gt;
&lt;p&gt;Aim to back every decision with data. While not always possible, most of the times it is possible. Engineers are as responsible for the company as everybody else. Always -&lt;em&gt;politely&lt;/em&gt;- question business decisions made out of sheer inspiration. Be ready to educate and share how data driven decisions help the business while intuition can be wrong and costly.&lt;/p&gt;

&lt;h2 id=&quot;simplify-simplify-simplify&quot;&gt;Simplify. Simplify. Simplify&lt;/h2&gt;
&lt;p&gt;Engineers have a tendency to overthink problems. Ask business owners questions about their decision drivers. Learn the business needs. With the clarity these provide you can simplify design aspects that are not really needed.&lt;/p&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;
&lt;p&gt;The following are real world scenarios I’ve been exposed to recently. &lt;sup id=&quot;fnref:obfuscation&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:obfuscation&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; They illustrate how the waste prevention measures can be applied to achieve optimal results that help the business move faster in the correct direction.&lt;/p&gt;

&lt;h3 id=&quot;example-1-the-cms&quot;&gt;Example 1: The CMS&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/waste-prevention/cms-testing.jpg&quot; alt=&quot;CMS Testing&quot; /&gt;&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: Rebuild the website using the company’s CMS.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: Why do you need a CMS? CMS’es are hard to test, their upgrades are very costly, they impose a great burden on the infrastructure, and they’re very hard to move away from.&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: We need a CMS because we need to frequently update the images of the website. And you cannot do it because you’re always busy.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: How often do you have to change those images? Is there anything else that you frequently need to change other than the images?&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: Just the images on a quarterly basis.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: What if we block our calendar one week per quarter so we can work together updating the images?&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: Unacceptable &lt;strong&gt;ಠ_ಠ&lt;/strong&gt;.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: Would you be willing to look into solutions other than a CMS that would allow you to change the images in a day? Granted, they would not be as easy to use as your very expensive CMS but they would certainly take less time to build. Plus we’d properly train you on how to use it.&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: Maybe, but we need to make sure it does all that we want.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: Fair enough, we’ll schedule a meeting in three days to showcase a potential solution.&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: Mutters, ineligible.&lt;/p&gt;

&lt;h3 id=&quot;example-2-database-all-the-things&quot;&gt;Example 2: Database all the things&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/waste-prevention/database-all-the-things.jpg&quot; alt=&quot;Database all the things&quot; /&gt;&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: We need to start paying attention to the customer’s date of birth (DOB). Let’s save it in the database and create some Crystal Reports off from it.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: Whoa! Hold your horses. A database field? What is it for? Why do you need this new information?&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: We want to be able to create reports of transactions linked to the customers birthday. We want to study if there’s a correlation between birthdays and sales. If it turns out that people buy themselves expensive gifts before their birthdays we want to kick off a new marketing project to exploit this trend.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: Excellent idea. However, to be able to create Crystal Reports off customer’s DOB we’d need to write code to save it in the database. Then we’d need to modify the database schema across all the different database environments. We’d need to create a change request with the DBA team for this. Once the data starts flowing we’d need to involve the Reporting Team and bring them up to speed with the database schema and the relationships between entities. Based on the weather conditions this could take from four to six weeks.&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: &lt;em&gt;Obviously annoyed slowly turns red…&lt;/em&gt;&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: There’s an alternative though. It would help us get feedback faster. What if we log the customers DOB and we create the reports using our current installation of Elastic Search (ELK). All this would take from us is writing a log line. Once the data starts flowing we can sit together to create the ELK reports. You’d have enough data for the marketing initiative in two weeks at most.&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: But these ELK reports don’t feel real. They’re not coming from a database.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: ELK is a database too. It is simply a log-based database. ELK and other logging aggregation tools can be a compliment to traditional reporting and database tools. Important business decisions can be made based on ELK data as well as on SQL data.
&lt;strong&gt;The business&lt;/strong&gt;: When I was a developer we used to store everything in SQL.&lt;br /&gt;
&lt;strong&gt;Engineers&lt;/strong&gt;: How about the following approach: Let’s implement the ELK reports first. If after two weeks you don’t have all the necessary information to kick off your marketing campaign, we’ll store the DOB in the database.&lt;br /&gt;
&lt;strong&gt;The business&lt;/strong&gt;: Mutters, ineligible.&lt;/p&gt;

&lt;h3 id=&quot;example-3-architectural-decisions&quot;&gt;Example 3: Architectural decisions&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/waste-prevention/architectural-diagram.jpg&quot; alt=&quot;Architectural Diagram&quot; /&gt;&lt;br /&gt;
&lt;strong&gt;Happy Trigger&lt;/strong&gt;: Let’s have an architectural discussion to make sure that all our web apps can be loaded as angular modules.&lt;br /&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt;: There’s something I don’t understand. You want to create a single web app that would be the entry point to all of the other web apps?&lt;br /&gt;
&lt;strong&gt;Happy Trigger&lt;/strong&gt;: Exactly!&lt;br /&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt;: What for?&lt;br /&gt;
&lt;strong&gt;Happy Trigger&lt;/strong&gt;: To reduce code duplication. And reuse the same CSS resources.&lt;br /&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt;: Interesting. Wouldn’t that create a monolithic application with a single point of failure?&lt;br /&gt;
&lt;strong&gt;Happy Trigger&lt;/strong&gt;: Not really because each module can…&lt;br /&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt;: &lt;em&gt;Interrupts hurriedly&lt;/em&gt; We have over thirty different apps. Some of them were built way before angular ever existed. They serve very different business functions there’s very little code duplication going on. I’m sure there are other ways of sharing CSS resources that don’t involve angular. Have you looked into those?&lt;br /&gt;
&lt;strong&gt;Happy Trigger&lt;/strong&gt;: Angular is cool. We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bower&lt;/code&gt; and make web APIs for everything.&lt;br /&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt;: What business problem does this Unification Initiative solves?&lt;br /&gt;
&lt;strong&gt;Happy Trigger&lt;/strong&gt;: It would create an architectural framework.&lt;br /&gt;
&lt;strong&gt;Senior Engineer&lt;/strong&gt;: Wouldn’t locking every application into a single technology be a bad idea? I heard there’s a new completely incompatible version of angular around the corner. We need hard numbers to justify this to the business. It needs to pay off technical debt or create business value. An architectural framework helps nobody unless it is really needed.&lt;br /&gt;
&lt;strong&gt;Happy Trigger&lt;/strong&gt;: Mutters, ineligible.&lt;/p&gt;

&lt;h2 id=&quot;foreword&quot;&gt;Foreword&lt;/h2&gt;
&lt;p&gt;That’s it, no magic bullet. Data Driven Decisions and Simplification are simple concepts that will save countless hours and headaches. Do you have to always push back on everything? For God’s sake no. However, keep an open eye for situations that can create waste.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:obfuscation&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;The details are obfuscated to protect the innocent. &lt;a href=&quot;#fnref:obfuscation&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Fri, 29 Jan 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/01/29/waste-prevention/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/01/29/waste-prevention/</guid>
        </item>
      
    
      
        <item>
          <title>The Deliver Value Fallacy: Technical Debt (Part 2)</title>
          <description>&lt;p&gt;The term Deliver Value is widely [&lt;em&gt;ab&lt;/em&gt;]used. It is regularly used as an excuse to avoid following engineering best practices. In this second installment I plan to illustrate some of the common misconceptions associated with Delivering Value and Technical Debt.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please head to the &lt;a href=&quot;/2016/01/26/The-Deliver-Value-Fallacy/?source=part2&quot;&gt;first post&lt;/a&gt; to learn about Delivering Waste.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;debt&quot;&gt;Debt&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;something, typically money, that is owed or due.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;a-tale-of-two-companies&quot;&gt;A tale of two companies&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;YouSail, Inc.&lt;/em&gt; and &lt;em&gt;GreatShoes, LLC.&lt;/em&gt; are two different companies in the same position: &lt;strong&gt;Online sales are low&lt;/strong&gt;. &lt;sup id=&quot;fnref:fake_names&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:fake_names&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;In a nutshell &lt;em&gt;YouSail, Inc.&lt;/em&gt; is big on outsourcing but relies heavily on business metrics while &lt;em&gt;GreatShoes, LLC.&lt;/em&gt; has a strong engineering culture that gets usually overruled by intuition and lack of data. Both of the companies devise relatively different plans to address their website issues.&lt;/p&gt;

&lt;p&gt;The following is a summary of the website redesign roadmap each company plans to implement. &lt;em&gt;Full details behind the decision processes and company cultures can be found in the &lt;a href=&quot;/2016/01/26/The-Deliver-Value-Fallacy/?source=part2&quot;&gt;Part 1&lt;/a&gt; of this series.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;company-tally&quot;&gt;Company tally&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;YouSail, Inc.&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;GreatShoes, LLC.&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Reaction Time&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Six Months&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;One Year&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Decision Time&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;One Week&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Multiple Weeks&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Root Cause Analysis&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Data Driven. Payment Flow&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No Data. Entire Design&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Time to Feedback&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;After Every Single Page&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;After project completion&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Projected Completion&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Six Months&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Three months&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;delivering-value&quot;&gt;Delivering Value&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Which of the two teams is delivering more value to the business?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The cultural differences between the two companies clearly impact their performance. The well-qualified team behind &lt;em&gt;GreatShoes, LLC.&lt;/em&gt; has built a highly-maintainable product. The initial engineering plan would rebuild the entire site -on top of the existing one- in the same time that it would take &lt;em&gt;YouSail, Inc.&lt;/em&gt; to get a single portion of their website rebuilt. The good engineering principles applied over the years empower the business to quickly adapt and react. Moreover, this is a professional team that understands risks and proposes an adequate plan to minimize them.&lt;/p&gt;

&lt;p&gt;Technical Debt is simply that: &lt;strong&gt;Debt&lt;/strong&gt;. It needs to be paid off or will eventually bring businesses down to a halt. If the executive team behind &lt;em&gt;YouSail, Inc.&lt;/em&gt; would regularly spend more time and energy on their website they’d be able to replace the entire Payment Flow in under a month.&lt;/p&gt;

&lt;p&gt;Sometimes it can be very hard to condone the actions behind &lt;em&gt;YouSail, Inc.&lt;/em&gt;. Yes, they’re a making decisions based on business metrics and data but the state of neglect in which they keep their website is significantly hurting their bottomline. &lt;sup id=&quot;fnref:phoenix_project&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:phoenix_project&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Delivering value does not mean cutting corners. On the contrary, Delivering Value is about delivering just enough functionality just in time to meet the business needs, while maintaining high engineering standards that will allow the product to adapt as needed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visit &lt;a href=&quot;/2016/01/29/waste-prevention/?source=part2&quot;&gt;Part 3&lt;/a&gt; of this series to learn how to Prevent Waste.&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:fake_names&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Not their real names. These scenarios are based on real world companies but the details obfuscated to protect the innocent. &lt;a href=&quot;#fnref:fake_names&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:phoenix_project&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://www.amazon.com/The-Phoenix-Project-Helping-Business/dp/0988262592?tag=capr04-20&quot;&gt;The Phoenix Project&lt;/a&gt; illustrates the struggles of a similar organization  with financial problems. It is a great read that clearly paints the need to involve and consider IT in the decision process. &lt;a href=&quot;#fnref:phoenix_project&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Thu, 28 Jan 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/01/28/The-Deliver-Value-Fallacy-Technical-Debt-Part-2/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/01/28/The-Deliver-Value-Fallacy-Technical-Debt-Part-2/</guid>
        </item>
      
    
      
        <item>
          <title>The Deliver Value Fallacy: Waste (Part 1)</title>
          <description>&lt;p&gt;The term Deliver Value is widely [&lt;em&gt;ab&lt;/em&gt;]used. It can be a wildcard to avoid following engineering best practices or build utterly complex solutions that solve no real business problem. In this multi-part article I plan to illustrate some of the common misconceptions associated with Delivering Value. The series will start with delivering waste.&lt;/p&gt;

&lt;h2 id=&quot;waste&quot;&gt;Waste&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;an act or instance of using or expending something carelessly, extravagantly, or to no purpose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;a-tale-of-two-companies&quot;&gt;A tale of two companies&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;YouSail, Inc.&lt;/em&gt; and &lt;em&gt;GreatShoes, LLC.&lt;/em&gt; are two different companies in the same position: &lt;strong&gt;Online sales are low&lt;/strong&gt;. This is how they plan to solve their problem. &lt;sup id=&quot;fnref:fake_names&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:fake_names&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;yousail-inc&quot;&gt;YouSail, Inc.&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;YouSail, Inc.&lt;/em&gt; rents luxury yachts. The executive team reasons that since they’re not a technology company there’s not a clear need for a technology team. &lt;sup id=&quot;fnref:phoenix_project&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:phoenix_project&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; They strongly believe in paying specialized vendors for their expertise rather than having to deal with more employees. Their online sales have been down for the last six months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Website&lt;/strong&gt;: The company’s website has always been outsourced. Countless contracting companies have worked on it over the years. It is a tree branch waiting to fall made out of stitched pieces. The fact that it works is a great indicator of how good technology has become over the last years. In spite of not owning the website development, &lt;em&gt;YouSail, Inc.&lt;/em&gt; has a strong hold over important business metrics, backed up by lots of analytics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The redesign&lt;/strong&gt;: A quick analysis of the metrics indicate that traffic is up. More people are starting reservations. Nevertheless, they’re abandoning the rental during the payment flow. A plan to redesign just the payment section of the website is kicked off in under a week. The redesign project will work on a single page at a time. Moreover, the two versions of the page will be simultaneously served to validate that the new website is working better than the old one. All of this development work will be contracted out to a development agency from Argentina. The projected completion date is six months.&lt;/p&gt;

&lt;h2 id=&quot;greatshoes-llc&quot;&gt;GreatShoes, LLC.&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;GreatShoes, LLC.&lt;/em&gt; sells shoes online. They pride themselves in being agile and Deliver Value frequently. The technology department is the second largest department in the company with dozens of well-qualified highly-trained technology professionals. The CEO can often be heard saying: &lt;em&gt;“nowadays, every company is a technology company”&lt;/em&gt;. And he’s right to say so because most of the company’s income comes through sales made directly to customers from their website. Unfortunately, sales have been on a steady decline for over a year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The redesign&lt;/strong&gt;: The business owners think sales are down because the website does not look chic enough. Finally, after several weeks of approval meetings and budget lobbying they kick off a plan to redesign the entire website. Besides, they think that they need a CMS. How is a CMS going to help with the sales? They reason that by changing the content frequently customers should be more engaged. They ask their development team to come up with a plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The proposal&lt;/strong&gt;: Fortunately, the development team at &lt;em&gt;GreatShoes, LLC.&lt;/em&gt; understands the risk behind a big redesign like this. They propose to gradually change each individual portion of the site while comparing its performance against the old version. Only when the redesign is completed they will start to incorporate the CMS one page at a time. The engineers thought this proposal would have the least risk and would gather feedback from customers faster. The projected completion date is six months as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request Denied&lt;/strong&gt;: Business owners think the whole incremental approach would add a lot of overhead. Plus they want their CMS from day one. They don’t understand why a CMS is harder to test nor to maintain in the long run. The proposal is unacceptable. They &lt;em&gt;graciously&lt;/em&gt; ask the development team to build everything at once in three months.&lt;/p&gt;

&lt;h2 id=&quot;company-tally&quot;&gt;Company tally&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;YouSail, Inc.&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;GreatShoes, LLC.&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Reaction Time&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Six Months&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;One Year&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Decision Time&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;One Week&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Multiple Weeks&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Root Cause Analysis&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Data Driven. Payment Flow&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;No Data. Entire Design&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Time to Feedback&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;After Every Single Page&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;After project completion&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Projected Completion&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Six Months&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;Three months&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Notice how the projected completion date is closer for &lt;em&gt;GreatShoes, LLC.&lt;/em&gt; -half of &lt;em&gt;YouSail, Inc.&lt;/em&gt;’s-.  They are going to have an entire new website in three months. And yet it is all going to waste because they don’t know that they’re losing sales because of the website, or because they need a mobile app, or because the competition has better prices. They simply don’t know. Moreover, there are no clear plans to incorporate performance feedback loops that can help steer the project based on customer needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;YouSail, Inc.&lt;/em&gt; is happy to spend six months in rebuilding just a single portion of their entire site. The cost is hefty but they know that every step they make in that direction will be validated with an increase in sales. Moreover, the risk is low. If the project cost gets too high or the business needs to focus on another direction, they can still benefit from whatever new pages they got time to build.&lt;/p&gt;

&lt;h2 id=&quot;delivering-value&quot;&gt;Delivering Value&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Which of the two teams is delivering more value to the business?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After a simple analysis it becomes clear that the team from Argentina is delivering more value to the business. Simply because they are building the correct product. They are solving a clear and measurable business problem. On the other hand the second team is building waste. They are making decisions based on intuition as opposed to measurable metrics.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visit &lt;a href=&quot;/2016/01/28/The-Deliver-Value-Fallacy-Technical-Debt-Part-2/?source=part1&quot;&gt;Part 2&lt;/a&gt; of this series to learn Delivering Value and Technical Debt.&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:fake_names&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Not their real names. These scenarios are based on real world companies but the details obfuscated to protect the innocent. &lt;a href=&quot;#fnref:fake_names&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:phoenix_project&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://www.amazon.com/The-Phoenix-Project-Helping-Business/dp/0988262592?tag=capr04-20&quot;&gt;The Phoenix Project&lt;/a&gt; illustrates the struggles of a similar organization  with financial problems. It is a great read that clearly paints the need to involve and consider IT in the decision process. &lt;a href=&quot;#fnref:phoenix_project&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
          <pubDate>Tue, 26 Jan 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/01/26/The-Deliver-Value-Fallacy/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/01/26/The-Deliver-Value-Fallacy/</guid>
        </item>
      
    
      
        <item>
          <title>The best book I read in 2015</title>
          <description>&lt;p&gt;&lt;a href=&quot;http://amzn.to/1KaRSBC&quot;&gt;The Lean Startup&lt;/a&gt; -by Eric Ries- diametrically changed many of the notions I had about how software projects should be carried on.&lt;/p&gt;

&lt;p&gt;It helped me understand what being agile truly means. True agility means going full cycle -from idea to the customers- in two weeks or a month instead of the infamous timeline nonsense. Agile teams control their deployment and technology stack.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://amzn.to/1KaRSBC&quot;&gt;The Lean Startup&lt;/a&gt; movement is based on the importance of reducing waste during the product development lifecycle. The book showcases how building the wrong product is the ultimate form of waste. The only effective measure against waste is ship often and validate with the users at every step.&lt;/p&gt;

&lt;p&gt;The book depicts with multiple examples from the real world how product ideas are initially wrong in most of the cases. Preconceived notions of how customers will use the products are generally wrong.   Yes, your idea is probably wrong too! Moreover, businesses should not get too attached to an idea, once the growth stops, businesses should be ready to pivot into a different direction.&lt;/p&gt;

&lt;p&gt;Be not confused with its title. The teachings of this masterpiece can also be applied in large and well established enterprises. It is certainly a mandatory read for every technology professional.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/the-lean-startup/the-lean-startup-book-cover_a.png&quot; alt=&quot;The Lean Startup&quot; /&gt;&lt;/p&gt;
</description>
          <pubDate>Fri, 22 Jan 2016 00:00:00 +0000</pubDate>
          <link>https://www.tddapps.com/2016/01/22/The-best-book-I-read-in-2015/</link>
          <guid isPermaLink="true">https://www.tddapps.com/2016/01/22/The-best-book-I-read-in-2015/</guid>
        </item>
      
    
  </channel>
</rss>
