<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Marc Littlemore</title>
  <link href="https://www.marclittlemore.com/feed.xml" rel="self"/>
  <link href="https://www.marclittlemore.com"/>
  <updated>2026-01-25T00:00:00Z</updated>
  <id>https://www.marclittlemore.com/</id>
  <author>
    <name>Marc Littlemore</name>
    <email>marc@marclittlemore.com</email>
  </author>
  
  <entry>
    
    <title>Rescuing My Bricked M1 MacBook Pro</title>
    
    <link href="https://www.marclittlemore.com/rescuing-my-bricked-m1-macbook-pro/"/>
    <published>2026-01-25T00:00:00Z</published>
    
    <updated>2026-01-25T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/rescuing-my-bricked-m1-macbook-pro/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Back in 2021, I joined a logistics startup called Beacon. I didn&#39;t stay long, but I walked away with a company MacBook Pro M1. It should have been sent back, but a major family health emergency happened at the same time and I completely forgot about it in the back of my wardrobe.&lt;/p&gt;
&lt;p&gt;Fast forward to September 2025. My son had just gone off to university and, within a week, he&#39;d managed to damage his laptop screen with an ill-timed AirPod case incident. Since he was struggling to use it, I went digging in the cupboards for a spare.&lt;/p&gt;
&lt;p&gt;I rediscovered the MacBook and thought I&#39;d see if it was still usable after I&#39;d previously wiped it. It booted up fine, but it was completely locked down with &lt;a href=&quot;https://www.jamf.com/&quot;&gt;JAMF&lt;/a&gt;, and I didn&#39;t have any credentials to log in. JAMF is a Mobile Device Management (MDM) system that companies use to manage their company devices.&lt;/p&gt;
&lt;p&gt;What followed was a massive rabbit hole of trying to unlock a machine that everyone told me was a &amp;quot;brick.&amp;quot; As I struggled to find any concrete information online, I thought I&#39;d share the process here for anyone else facing the same headache.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-red-100 border-red-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-red-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm32 224c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;strong&gt;This won&#39;t help you unlock a stolen MacBook.&lt;/strong&gt; This post is only for legitimately owned devices that have been released from MDM profiles. It won&#39;t help you bypass Activation Lock or features designed to prevent theft.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-mdm-brick-wall&quot;&gt;The MDM brick wall&lt;/h2&gt;&lt;p&gt;Beacon used JAMF to manage their devices. When I tried to boot the laptop, I was greeted with a JAMF login screen asking for a Google Account I no longer had.&lt;/p&gt;
&lt;p&gt;I contacted an old friend who is still there. As he&#39;s now the head of engineering, they were happy for me to keep the laptop and asked their IT provider to remove it from their MDM system and Apple Business Manager.&lt;/p&gt;
&lt;p&gt;I spoke to the provider on the phone and they confirmed it was done. However, when I tried to use the laptop, it still booted to that same JAMF screen. After another call, they told me I&#39;d have to reformat the machine to properly remove the profile.&lt;/p&gt;
&lt;p&gt;The problem? When I tried to boot into recovery mode to reinstall macOS, it asked for an admin recovery key. I&#39;d never been given one, and the IT provider had no record of it. It looked like I was stuck.&lt;/p&gt;
&lt;h2 id=&quot;understanding-mac-security&quot;&gt;Understanding Mac security&lt;/h2&gt;&lt;p&gt;This is where I went down a rabbit hole. Since this was an Apple Silicon M1 chip, the security works differently than the old Intel-based Macs.&lt;/p&gt;
&lt;p&gt;Essentially, there are two different layers of the security onion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FileVault&lt;/strong&gt; is disk encryption. It makes your data unreadable if the laptop is stolen, but it doesn&#39;t stop someone from wiping the whole machine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recovery Lock&lt;/strong&gt; is what I was dealing with. It&#39;s an MDM hurdle that locks the recovery partition itself. It&#39;s there to stop someone from simply hitting &amp;quot;factory reset&amp;quot; on a stolen laptop and reusing it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was eventually told by the IT provider to call Apple Support. They suggested that Apple could remove the recovery lock if I provided proof of purchase (which I had). When I finally escalated the issue to someone technical at Apple, he was a bit patronising but somewhat helpful. He confirmed they couldn&#39;t actually remove the recovery key as it&#39;s a core security feature.&lt;/p&gt;
&lt;p&gt;He suggested the MDM hadn&#39;t actually been removed since I could still see the login screen. It turned out that this wasn&#39;t actually correct, but it confirmed one thing: I was on my own to figure out how to wipe the hardware as nobody knew how to!&lt;/p&gt;
&lt;p&gt;Even after a device is removed from Apple Business Manager, M1 Macs seem to keep cached records that still see the machine as a corporate device. Without the recovery key, you&#39;re a bit buggered as you can&#39;t even enter recovery mode to wipe the drive.&lt;/p&gt;
&lt;h2 id=&quot;the-solution-dfu-restore&quot;&gt;The solution: DFU restore&lt;/h2&gt;&lt;p&gt;After a lot of research, I discovered that recovery locks on M1 Macs can be bypassed using DFU (Device Firmware Update) mode to completely restore the machine. Again, this can only be done if the laptop has been unenrolled from MDM.&lt;/p&gt;
&lt;p&gt;DFU mode allows you to reinstall the firmware and OS, bypassing all locks. The downside is that it erases everything on the Mac, but since I couldn&#39;t access it anyway, that wasn&#39;t an issue.&lt;/p&gt;
&lt;p&gt;To give this a go, I needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Another Mac running macOS Sonoma or later.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apps.apple.com/us/app/apple-configurator/id1037126344&quot;&gt;Apple Configurator 2&lt;/a&gt; (a free app from the App Store).&lt;/li&gt;
&lt;li&gt;A USB-C to USB-C data cable (&lt;strong&gt;not&lt;/strong&gt; a Thunderbolt cable).&lt;/li&gt;
&lt;li&gt;A hell of a lot of patience!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I found that one of my USB cables didn&#39;t work and I&#39;m not 100% sure why. You might have to try a few different ones to get it to click.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;booting-into-dfu-mode&quot;&gt;Booting into DFU mode&lt;/h2&gt;&lt;p&gt;Getting the MacBook Pro into DFU mode was by far the hardest part. The timing is incredibly precise and it took me multiple attempts over a few hours to get it right.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make sure the locked Mac is completely powered off (hold the power button for 15 seconds).&lt;/li&gt;
&lt;li&gt;Connect it to your working Mac with the USB-C cable.&lt;/li&gt;
&lt;li&gt;Open Apple Configurator 2 on your working Mac.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now for the &amp;quot;Voodoo&amp;quot; timing part:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press and release the power button on the locked Mac.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Immediately&lt;/strong&gt; press and hold: Power button + Left Control + Left Option + Right Shift.&lt;/li&gt;
&lt;li&gt;Hold all four for &lt;strong&gt;exactly 10 seconds&lt;/strong&gt;. (I found counting &amp;quot;one-thousand, two-thousand&amp;quot; out loud actually helped).&lt;/li&gt;
&lt;li&gt;After 10 seconds, release the three keys but &lt;strong&gt;keep holding the power button&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Wait until you see &amp;quot;DFU&amp;quot; appear in Apple Configurator 2 (usually another 5-10 seconds).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The screen should stay completely black. If you see an Apple logo, it&#39;s started booting normally and you need to try again. I had to try this at least 20 times before I got the timing right. Don&#39;t give up!&lt;/p&gt;
&lt;h2 id=&quot;the-ipsw-secret&quot;&gt;The IPSW secret&lt;/h2&gt;&lt;p&gt;Once I got into DFU mode, the restore process kept failing with a generic error while halfway through the update:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;AMRestoreErrorDomain error &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After some help from Claude, I found the key: don&#39;t let Apple Configurator download the firmware. Instead, download the macOS firmware file (called an IPSW file) manually.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a href=&quot;https://ipsw.me/&quot;&gt;ipsw.me&lt;/a&gt; on the working Mac.&lt;/li&gt;
&lt;li&gt;Select your specific MacBook model.&lt;/li&gt;
&lt;li&gt;Download the latest signed IPSW file (it&#39;s usually around 14GB).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In Apple Configurator 2, you just drag that downloaded IPSW file from your folder and drop it onto the large &amp;quot;DFU&amp;quot; icon. This single change made everything work on the first try.&lt;/p&gt;
&lt;h2 id=&quot;victory-was-mine&quot;&gt;Victory was mine&lt;/h2&gt;&lt;p&gt;The restore took about 30 minutes. When the MacBook finally restarted and showed the &amp;quot;Hello!&amp;quot; welcome screen, I think I actually shouted &amp;quot;FUCK YEAH!&amp;quot; 😂&lt;/p&gt;
&lt;p&gt;The recovery lock was gone. The MDM was gone. It was completely clean and ready to set up as a new laptop. I went through the setup, and it&#39;s now working perfectly for my son.&lt;/p&gt;
&lt;h2 id=&quot;what-i-learned&quot;&gt;What I learned&lt;/h2&gt;&lt;p&gt;If you&#39;re facing a similar situation, here are the small wins to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Cable Matters:&lt;/strong&gt; Not all USB-C cables are created equal. If DFU isn&#39;t triggering, swap the cable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manual Downloads:&lt;/strong&gt; Always get the IPSW file yourself. It&#39;s much more reliable than letting the app handle it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Counting Out Loud:&lt;/strong&gt; It sounds stupid, but it&#39;s the only way to hit that 10-second window accurately for DFU mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#39;s a frustrating process, but it&#39;s definitely possible to recover a bricked device if you have the patience to work through it! 😄&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Recovering My Apple Podcast Feed</title>
    
    <link href="https://www.marclittlemore.com/recovering-my-apple-podcast-feed/"/>
    <published>2026-01-20T00:00:00Z</published>
    
    <updated>2026-01-20T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/recovering-my-apple-podcast-feed/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;h2 id=&quot;discovering-podcasts&quot;&gt;Discovering podcasts&lt;/h2&gt;&lt;p&gt;Way back in the mid 2000s, I was still a mildly successful DJ under my alter ego of &lt;a href=&quot;https://www.djcruze.co.uk/&quot;&gt;DJ Cruze&lt;/a&gt;. I was regularly travelling to Germany and playing at various clubs and I wanted to share my mixes with a wider audience, and yes, try to get a few more DJ gigs!&lt;/p&gt;
&lt;p&gt;As I was a video games programmer at the time, I wasn&#39;t an expert on the web so I had naïvely set up my DJ Cruze website as a &lt;a href=&quot;https://www.blogger.com/&quot;&gt;Blogger&lt;/a&gt; site. This worked for a while but I moved over to self-hosted &lt;a href=&quot;https://wordpress.org/&quot;&gt;Wordpress&lt;/a&gt; to get a bit more control over it once I started hitting the limitations of Blogger. This move gave me the ability to learn about, and set up, an RSS feed for my blog posts. As it was a DJ site, I also wanted to start sharing my DJ mixes as mp3 files.&lt;/p&gt;
&lt;p&gt;I had just discovered podcasts for the first time and started listened to a few nerdy tech ones, mainly Leo Laporte&#39;s &lt;a href=&quot;https://twit.tv/shows/this-week-in-tech&quot;&gt;&amp;quot;This Week In Tech (TWIT)&amp;quot;&lt;/a&gt; and Adam Curry&#39;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Daily_Source_Code&quot;&gt;&amp;quot;The Daily Source Code&amp;quot;&lt;/a&gt;. Once I started to dig into the technology that powered podcasts, I discovered that it was just a variation of RSS under the hood. As I knew that Wordpress supported RSS, I found a couple of extra Wordpress plugins, and soon I had a podcast RSS feed ready to go.&lt;/p&gt;
&lt;p&gt;I started recording a few mixes, added some voice overs to avoid getting into trouble for sharing unreleased tracks in a DJ mix, compressed them as terrible quality MP3 files, and started &lt;a href=&quot;https://www.djcruze.co.uk/podcasts/&quot;&gt;The DJ Cruze Podcast&lt;/a&gt;. The files were kindly hosted on a friend&#39;s server for a while as I had no idea what I was doing nor any concept of bandwidth.&lt;/p&gt;
&lt;p&gt;Around the same time, Apple leaned heavily into podcasting and allowed you to submit your podcast to their iTunes directory. I submitted my podcast into the ether and thought nothing much of it. However, after a couple of months I soon gained some fans and quickly discovered that the iTunes crawler was reading my RSS feed over and over again. This quickly used up all of my hosting bandwidth. I quickly needed to cache my feed to stop Dreamhost from charging me more money, so I started looking for a better solution. And then I discovered Feedburner.&lt;/p&gt;
&lt;h2 id=&quot;wave-hello-feedburner&quot;&gt;👋 Hello Feedburner&lt;/h2&gt;&lt;p&gt;Feedburner was a web service that let you manage RSS feeds and, more importantly for me, it could cache them and handle all the bandwidth. I was a cheapskate back then and I didn&#39;t want to pay extra for better hosting so this seemed like the perfect solution. I quickly set up a redirect so my Wordpress feed URL would redirect to Feedburner instead. Bandwidth saved! 🎉&lt;/p&gt;
&lt;p&gt;I left it like this for years and didn&#39;t really think much of it. I stopped making the podcast in 2011 but the feed remained live on Feedburner for anyone who wanted to subscribe to it. In 2012, Google acquired Feedburner and then left it to rot, as they often do with services they buy. I didn&#39;t really think much of it for the next decade.&lt;/p&gt;
&lt;h2 id=&quot;moving-to-my-own-feed&quot;&gt;Moving to my own feed&lt;/h2&gt;&lt;p&gt;I eventually decided to move my blog away from Wordpress in the late 2010s and wanted to build a static site built with my favourite static site generator, Eleventy. I also wanted to take back control of my podcast feed. Back in 2021, I&#39;d actually written about &lt;a href=&quot;https://www.marclittlemore.com/create-an-eleventy-podcast-feed/&quot;&gt;how to create an Eleventy podcast feed&lt;/a&gt;, so I already had the technical understanding of how to do it. My new feed would live at &lt;code&gt;https://www.djcruze.co.uk/podcasts/feed.xml&lt;/code&gt; and I wanted to migrate all my old episodes over to this new feed.&lt;/p&gt;
&lt;p&gt;As I was hosting the new site on Netlify, I set up redirects from the old Wordpress feed URL to the new Eleventy feed URL. This meant that Feedburner would now be pulling from my new Eleventy feed instead of the old Wordpress one. And then Apple Podcasts would pull from the new RSS feed too.&lt;/p&gt;
&lt;p&gt;But I had one problem I wanted to solve: how do you tell Apple Podcasts and all your subscribers to switch to the new feed URL without all of the redirection?&lt;/p&gt;
&lt;p&gt;Apple Podcasts had a fancy new web portal for podcasters as podcasting had finally become a big deal. Although Apple still had my podcast listed in their directory, I had no way of managing it as I&#39;d never set up an account with them. I needed to prove ownership of the feed somehow.&lt;/p&gt;
&lt;p&gt;So what do you do?&lt;/p&gt;
&lt;h2 id=&quot;redirecting-subscribers&quot;&gt;Redirecting subscribers&lt;/h2&gt;&lt;p&gt;The answer is a special iTunes RSS tag called &lt;code&gt;&amp;lt;itunes:new-feed-url&amp;gt;&lt;/code&gt;. When Apple encounters this tag in the feed markup, they automatically update their subscription to point to the new URL.&lt;/p&gt;
&lt;p&gt;Here&#39;s what I added to my RSS podcast feed:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;new-feed-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;https://www.djcruze.co.uk/podcasts/feed.xml&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;new-feed-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tag tells Apple that you feed has moved to this new feed URL. You&#39;ll need to make sure you leave the old HTTP 301 redirect in place from the old feed URL to the new one as well.&lt;/p&gt;
&lt;p&gt;Apple &lt;a href=&quot;https://podcasters.apple.com/support/837-change-the-rss-feed-url&quot;&gt;recommends&lt;/a&gt; keeping this redirect in place for at least four weeks to give subscribers time to migrate. I&#39;ve kept mine in the feed forever just to be safe. After this, the old feed can be retired and all subscribers should be receiving updates from the new feed.&lt;/p&gt;
&lt;h2 id=&quot;the-importance-of-guids&quot;&gt;The importance of GUIDs&lt;/h2&gt;&lt;p&gt;Every episode in your podcast feed has a GUID (Globally Unique Identifier). This can be anything you want it to be but you can use URLs as these ideally should never change. If you&#39;ve not read it, then you should look at this &lt;a href=&quot;https://www.w3.org/Provider/Style/URI&quot;&gt;cool URIs never change&lt;/a&gt; article. Podcast apps use these GUIDs to track which episodes a subscriber has already downloaded or listened to.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example GUID from one of my episodes which has the original Wordpress post ID URL:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;guid&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;isPermaLink&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;http://www.djcruze.co.uk/cms/?p=820&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;guid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-red-100 border-red-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-red-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm32 224c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;strong&gt;If you change the GUIDs when migrating your feed, you&#39;re in for a world of pain.&lt;/strong&gt; Podcast apps will think all your episodes are brand new, and subscribers will re-download everything. They&#39;ll also lose their play progress and which episodes they&#39;ve already heard.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When I migrated from Feedburner to my own feed, I made absolutely certain that every episode kept its original Wordpress GUID. I went through my Feedburner feed XML, noted down each episode&#39;s GUID, and made sure my new Eleventy-generated feed used the exact same identifiers.&lt;/p&gt;
&lt;p&gt;This was tedious but I didn&#39;t want my 50+ podcast episodes to be redownloaded by any subscribers I might have left!&lt;/p&gt;
&lt;h2 id=&quot;claiming-ownership-in-apple-podcasts&quot;&gt;Claiming ownership in Apple Podcasts&lt;/h2&gt;&lt;p&gt;The final piece of the puzzle was claiming ownership of my podcast in Apple Podcasts. Because I&#39;d submitted the feed nearly 20 years ago through the old iTunes system, I&#39;d never had access to &lt;a href=&quot;https://podcasters.apple.com/&quot;&gt;Apple Podcasts Connect&lt;/a&gt;, their modern dashboard for podcast management.&lt;/p&gt;
&lt;p&gt;Thanks to Rob Knight&#39;s excellent article on &lt;a href=&quot;https://rknight.me/blog/how-to-verify-an-apple-podcasts-claim-in-your-rss-feed/&quot;&gt;verifying an Apple Podcast feed&lt;/a&gt;, I learned about the claiming process. Here&#39;s how it works:&lt;/p&gt;
&lt;h3 id=&quot;step-1-request-to-claim-your-podcast&quot;&gt;Step 1: Request to claim your podcast&lt;/h3&gt;&lt;p&gt;In Apple Podcasts Connect, you search for your podcast and request to claim it. Apple needs to verify you actually own the feed, so they generate a unique verification code, a UUID-style identifier.&lt;/p&gt;
&lt;h3 id=&quot;step-2-add-the-verification-tag-to-your-feed&quot;&gt;Step 2: Add the verification tag to your feed&lt;/h3&gt;&lt;p&gt;You add this verification code to your podcast feed XML as an &lt;code&gt;&amp;lt;itunes:applepodcastsverify&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;applepodcastsverify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;8c011b37-3cc1-4bb9-8c38-example&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;applepodcastsverify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tag goes in your RSS feed&#39;s channel section, alongside your other iTunes tags like &lt;code&gt;&amp;lt;itunes:author&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;itunes:category&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;step-3-complete-verification&quot;&gt;Step 3: Complete verification&lt;/h3&gt;&lt;p&gt;Once the tag is in your feed and live on your server, you go back to Apple Podcasts Connect and click to complete the verification. Apple&#39;s servers fetch your feed, find the verification tag, confirm it matches their generated code, and you are now verified as the owner of your podcast in their system.&lt;/p&gt;
&lt;p&gt;After verification succeeded, I had full access to my podcast&#39;s analytics, could update any metadata, and most importantly, I finally had complete control over my feed URL again. Success! 🎉&lt;/p&gt;
&lt;h2 id=&quot;own-your-content&quot;&gt;Own your content&lt;/h2&gt;&lt;p&gt;So after two decades, I finally have complete control of my podcast feed again. It lives on my domain, hosted on Netlify, with my RSS implementation, that I built and understand myself. If I want to change hosting providers, I can do it without anyone noticing. If I want to move to a different static site generator, the URL stays the same.&lt;/p&gt;
&lt;p&gt;With all of the current turmoil in the tech world, and big tech doing plenty of bad things, I feel much better knowing that my content is mine again. There&#39;s quite a backlash to using third-party services for hosting your content these days. Services come and go, get acquired and sold, or just shut down, so it&#39;s much better to own your own stuff.&lt;/p&gt;
&lt;p&gt;Just remember to preserve those podcast GUIDs, test thoroughly, and take back control!&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Writing Again</title>
    
    <link href="https://www.marclittlemore.com/writing-again/"/>
    <published>2025-01-11T00:00:00Z</published>
    
    <updated>2025-01-11T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/writing-again/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I was looking at my site the other day and realised that I&#39;ve managed to write quite a few blog posts in the past 10 years. I was really proud of writing 30+ articles in 2021 alone when I challenged myself to write more. However, looking at my the site markdown files, it dawned on me that I&#39;ve only managed to add 2 more to this site since 2022 and I was a bit disappointed in myself.&lt;/p&gt;
&lt;p&gt;But as everyone who has a partner or family knows, there&#39;s always a lot to deal with in real life.&lt;/p&gt;
&lt;h2 id=&quot;life-challenges&quot;&gt;Life challenges&lt;/h2&gt;&lt;p&gt;Over the past few years I&#39;ve been helping two teenage children to grow and thrive into young adults. This isn&#39;t always easy as they navigate growing up and the challenges of school, college, work, and their own relationships. My wife and I have had to become partial carers for my mum who sadly suffered from a stroke back in 2022. It&#39;s hard for her now that she&#39;s lost a lot of independence so we help by doing the shopping, washing, cooking, and taking her to the many medical appointments. And then in May 2024, I was, yet again, laid off from my tech job at Netlify after working there for 2 years. I had to spend a few months trying to find another role in a difficult market and that puts a lot of pressure on life. So I&#39;m sure you can understand that that blogging wasn&#39;t top of mind for me.&lt;/p&gt;
&lt;h2 id=&quot;social-media-sucks&quot;&gt;Social media sucks&lt;/h2&gt;&lt;p&gt;I used to post a lot on social media but my behaviour has changed over the past few years. Twitter has become a bit of a cesspool after Elmo took over so I&#39;ve not felt as comfortable sharing my thoughts there. Facebook now mostly serves posts from groups you&#39;re not part of rather than showing updates from your friends and it is also now allowing even more misinformation and hate speech due to &lt;a href=&quot;https://www.theguardian.com/technology/2025/jan/08/metas-changes-to-social-media-policing-will-lead-to-clash-with-eu-and-uk-say-experts&quot;&gt;the change in their policies&lt;/a&gt;. I found myself using Bluesky a little but more recently as it has been making strides to become a nice place to share your thoughts with the world. But as the world of media changes, I do feel like I need to keep my own little place on the internet and so I want to start posting a bit more on here again.&lt;/p&gt;
&lt;h2 id=&quot;writing-for-myself-again&quot;&gt;Writing for myself again&lt;/h2&gt;&lt;p&gt;I always felt like I had to have big ideas to write about and that my posts should be really insightful and more long-form in length. But I&#39;ve also read lots of great blog posts that are just a few paragraphs long and are often just someone sharing their thoughts and ideas. I have tried to start writing again a few times but I find myself paralysed by the thought that people would judge what I&#39;ve written or I&#39;m not knowledgeable enough to write on certain subjects. Thanks imposter syndrome!&lt;/p&gt;
&lt;p&gt;I&#39;ve come to realise that I should just write for myself and if people read it, then that&#39;s a bonus. ❤️&lt;/p&gt;
&lt;p&gt;I really should read my own blog and &lt;a href=&quot;https://www.marclittlemore.com/creating-a-writing-habit/&quot;&gt;create a writing habit&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&quot;inspiration-for-writing-more&quot;&gt;Inspiration for writing more&lt;/h2&gt;&lt;p&gt;I&#39;ve also been inspired by a few blog posts that I&#39;ve read recently.&lt;/p&gt;
&lt;p&gt;My old Netlify colleague Den Delimarsky wrote a great post about &lt;a href=&quot;https://den.dev/blog/be-a-property-owner-not-a-renter-on-the-internet/&quot;&gt;being a property owner and not a renter on the internet&lt;/a&gt;. He&#39;s writing a great post eschewing social media platforms and ensuring that we all help to build our own little homes on the internet where platform owners can&#39;t pull the rug from under us.&lt;/p&gt;
&lt;p&gt;The ever-brilliant Simon Willison has written lots about blogging on his site. I don&#39;t know how he manages to publish multiple blog posts per day but he has some great advice on his post called &lt;a href=&quot;https://simonwillison.net/2022/Nov/6/what-to-blog-about/&quot;&gt;what to blog about&lt;/a&gt;. He suggests writing more &amp;quot;today I learned&amp;quot; (TIL) posts about the challenges you encounter and how you fixed them. He also says you should write more about any projects you&#39;re working on so it&#39;s time to actually work on some more projects in the first place!&lt;/p&gt;
&lt;p&gt;This post from Danny Guo called &lt;a href=&quot;https://www.dannyguo.com/blog/why-i-blog/&quot;&gt;&amp;quot;Why I blog&amp;quot;&lt;/a&gt; makes some really great points on why you should blog. I love that writing helps to clarify your thinking as you have to write something down and make it coherent.&lt;/p&gt;
&lt;p&gt;There&#39;s also the classic post by Jeff Atwood on &lt;a href=&quot;https://blog.codinghorror.com/how-to-achieve-ultimate-blog-success-in-one-easy-step/&quot;&gt;how to achieve ultimate blog success&lt;/a&gt; from way back in 2007 which is still really relevant today.&lt;/p&gt;
&lt;p&gt;Finally, I&#39;m always inspired by my friend Sam Rose who has got some brilliant long-form articles on computer science topics on his wonderful &lt;a href=&quot;https://samwho.dev/&quot;&gt;blog&lt;/a&gt;. You should really check this out if you haven&#39;t already as he writes incredible articles on complex computer science topics and includes his own wonderful visualisations. His posts are really engaging and he writes so anyone can understand them.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What&#39;s next?&lt;/h2&gt;&lt;p&gt;So 2025 will be a year where I post more regularly and don&#39;t worry about what others think. Everything will continue to be syndicated via my &lt;a href=&quot;https://www.marclittlemore.com/feed.xml&quot;&gt;RSS feed&lt;/a&gt; so if you&#39;re still an old-school RSS reader like me, you can get all of my posts there.&lt;/p&gt;
&lt;p&gt;I&#39;m also planning on making more music under my &lt;a href=&quot;https://djcruze.co.uk/&quot;&gt;DJ Cruze&lt;/a&gt; moniker. I might even publish some videos or live-streams of me making the music too so keep your eyes peeled for that.&lt;/p&gt;
&lt;p&gt;Continue to be excellent to each other and see you online soon!&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Automate Your Static Site Rebuilds with Netlify Scheduled Functions</title>
    
    <link href="https://www.marclittlemore.com/automate-site-rebuilds-with-netlify-scheduled-functions/"/>
    <published>2024-03-17T00:00:00Z</published>
    
    <updated>2024-03-17T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/automate-site-rebuilds-with-netlify-scheduled-functions/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I laugh every year when I see this meme about updating your website footer:&lt;/p&gt;
&lt;p&gt;https://twitter.com/iamdevloper/status/1212320688050229249&lt;/p&gt;
&lt;p&gt;If you&#39;re a developer who has come from the world of Wordpress where using PHP to dynamically update the year in the footer then I&#39;m sure you&#39;re wondering what the fuss is about. As PHP is a server-side language, you&#39;re constantly rebuilding a page when someone requests a URL.&lt;/p&gt;
&lt;p&gt;But if you have a static site then your code only updates when you build it manually and redeploy the latest version. If you have dynamic content on the page, such as a year in your footer, then you have to remember to rebuild the site when the data changes or maybe use some client-side JavaScript.&lt;/p&gt;
&lt;p&gt;This site is generated with &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;11ty&lt;/a&gt; which is staticly generated so I need to remember to rebuild my site each year if I want to do something like update my footer. But clicking on a deploy button in the Netlify dashboard is a bit of a faff and I&#39;m likely to forget to do it. So why not automate it? 🤖&lt;/p&gt;
&lt;h2 id=&quot;lets-start-automating&quot;&gt;Let&#39;s start automating&lt;/h2&gt;&lt;p&gt;My original plan for rebuilding my sites was to trigger this rebuild via a cron service, like &lt;a href=&quot;https://cron-job.org/en/&quot;&gt;cron-job.org&lt;/a&gt; or &lt;a href=&quot;https://www.easycron.com/&quot;&gt;Easy Cron&lt;/a&gt; or automation platform like &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I tried using the automation service &lt;a href=&quot;https://make.com/&quot;&gt;make.com&lt;/a&gt; to schedule the yearly rebuild of my site. However, I soon hit the limits of their free tier which only allows 2 &amp;quot;scenarios&amp;quot; (a set of automation steps) and I wanted to update multiple websites. You could bundle multiple rebuild requests into a single scenario and update each site in a sequence, but then you lack single responsibility and it&#39;s harder to debug if anything goes wrong with a single update.&lt;/p&gt;
&lt;p&gt;So as my site was already hosted there, I decided to use &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: I am a Senior Software Engineering Manager at Netlify but I&#39;ve used their platform for years before I worked there.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;using-netlify-to-rebuild-your-site&quot;&gt;Using Netlify to rebuild your site&lt;/h2&gt;&lt;p&gt;I host all of my sites on Netlify and have done for a long time. Netlify provides you with the ability to rebuild your site via a &lt;a href=&quot;https://docs.netlify.com/configure-builds/build-hooks/&quot;&gt;build hook&lt;/a&gt;. This is special kind of &lt;a href=&quot;https://en.wikipedia.org/wiki/Webhook&quot;&gt;webhook&lt;/a&gt;, a function that responds to a HTTP POST request, which allows you to kick-off a new build and deployment of your site.&lt;/p&gt;
&lt;p&gt;While there are plenty of other options for running scheduled tasks, I thought I&#39;d just move over to using Netlify&#39;s &lt;a href=&quot;https://docs.netlify.com/functions/scheduled-functions/&quot;&gt;scheduled functions&lt;/a&gt; to automate this. This has the advantage of having complete control over the code and when the rebuild happens. I can also add in any additional notifications, like sending me a message on Slack or Telegram, and it allows me to commit it to my site&#39;s repository as part of the infrastructure, known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Infrastructure_as_code&quot;&gt;infrastructure as code (IaC)&lt;/a&gt;. This allows me to version my scheduled tasks along with the code and stops me from having to remember which service I used to rebuild the site.&lt;/p&gt;
&lt;p&gt;This approach is easy to implement, even for beginners with no prior experience with Netlify functions. So, buckle up and get ready to make website maintenance easy with the power of automation!&lt;/p&gt;
&lt;h2 id=&quot;create-a-netlify-build-hook&quot;&gt;Create a Netlify build hook&lt;/h2&gt;&lt;p&gt;First, we need to create a build hook in Netlify. This is a URL that you can use to trigger a new build and deployment of your Netlify site.&lt;/p&gt;
&lt;p&gt;Go to the Netlify dashboard and select the site you want to add your build hook to. Next, go to the &amp;quot;Build &amp;amp; deploy&amp;quot; section and scroll down to the &amp;quot;Build hooks&amp;quot; section.   Click the &amp;quot;Add build hook&amp;quot; button and give it a name. I called mine &lt;code&gt;build-production-site&lt;/code&gt; as, unsuprisingly, it rebuilds my production website.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/netlify-build-hooks.png&quot; alt=&quot;Netlify build hook&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-red-100 border-red-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-red-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm32 224c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;Remember that this build hook URL should be considered a secret and must be kept safe. If you accidentally expose it, you allow anyone to trigger a new build and deployment of your site. This might not seem like a big deal but you don&#39;t want people to be able to rack up build minutes on your behalf.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It will give you a URL that you can use to trigger a new build and deployment of your site and you can test it out using a CURL command to send a HTTP POST request to it.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; https://api.netlify.com/build_hooks/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;your-build-hook&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take a look at your deployments in the Netlify application and you should see a new deployment being created.&lt;/p&gt;
&lt;h3 id=&quot;add-the-build-hook-to-your-environment-variables&quot;&gt;Add the build hook to your environment variables&lt;/h3&gt;&lt;p&gt;To keep this URL safe, you can add it to your environment variables in your Netlify site settings. This will allow you to access it in your Netlify functions without exposing it in your code.&lt;/p&gt;
&lt;p&gt;Go to your Netlify site settings and select the &amp;quot;Build &amp;amp; deploy&amp;quot; section. Scroll down to the &amp;quot;Environment&amp;quot; section, click the &amp;quot;Add a variable&amp;quot; button, and choose &amp;quot;Add a single variable&amp;quot;. Add a new environment variable called &lt;code&gt;NETLIFY_REBUILD_HOOK&lt;/code&gt; and paste in the URL of your build hook.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: You can limit the scope of this to &amp;quot;Functions&amp;quot; as this will only be used in our scheduled functions.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/netlify-environment-variables.png&quot; alt=&quot;Netlify environment variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;Ok great! So now we&#39;ve got a build hook so let&#39;s create some code in a Netlify scheduled function to trigger it.&lt;/p&gt;
&lt;h2 id=&quot;adding-a-netlify-rebuild-scheduled-function&quot;&gt;Adding a Netlify rebuild scheduled function&lt;/h2&gt;&lt;p&gt;If you&#39;ve never used scheduled functions before, think of them as robot workers who can automate tasks for you while you sleep and do this on a consistent schedule! 🤖&lt;/p&gt;
&lt;p&gt;Create a new file in your &lt;code&gt;netlify/functions&lt;/code&gt; directory called &lt;code&gt;rebuild-yearly.mts&lt;/code&gt;.  This will be the scheduled code that calls our rebuild hook.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;Node.js supports two distinct module formats with different capabilities and APIs: ECMAScript modules (or ES modules), an official standard format for JavaScript packages, and CommonJS, a legacy format specific to Node.js.&lt;/p&gt;
&lt;p&gt;I&#39;m using the &lt;code&gt;.mts&lt;/code&gt; file extension to indicate that the file is an ECMAScript module.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To rebuild the site, we&#39;re going to make a &lt;code&gt;fetch&lt;/code&gt; request to the build hook URL to trigger a new build and deployment. Netlify&#39;s rebuild hooks allow us to add a query parameter to the URL called &lt;code&gt;trigger_title&lt;/code&gt; which gives us a clearer title for the build in the Netlify dashboard. This will help us to identify why we&#39;re rebuilding the site.&lt;/p&gt;
&lt;p&gt;Add the following code:&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Yearly rebuild scheduled function&lt;/span&gt; &lt;span class=&quot;&quot;&gt;./netlify/functions/rebuild-yearly.mts&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Config &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@netlify/functions&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Stop TypeScript from complaining about&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// the missing process.env.NETLIFY_REBUILD_HOOK&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; process &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    env&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;NETLIFY_REBUILD_HOOK&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// An asynchronous function to call&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// the Netlify build hook to rebuild your site&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;rebuildSite&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;triggerTitle&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Construct the URL for the Netlify rebuild hook&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NETLIFY_REBUILD_HOOK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Add the title to the query string&lt;/span&gt;
    url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;trigger_title&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; triggerTitle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Make a POST request to the Netlify webhook&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        method&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Always update your footer every year! :)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rebuildSite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Yearly rebuild to update footer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Netlify scheduled function cron syntax&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Run every year on the 1st of January at 00:00&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    schedule&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0 0 1 1 *&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have to &lt;code&gt;export&lt;/code&gt; a &lt;code&gt;config&lt;/code&gt; object with a &lt;code&gt;schedule&lt;/code&gt; property to tell Netlify when to run the function. The &lt;code&gt;schedule&lt;/code&gt; property uses the cron syntax to specify when the function should run. In my example, the function will run every year on the 1st of January at 00:00. You can use a cron generator like &lt;a href=&quot;https://crontab.guru/&quot;&gt;crontab.guru&lt;/a&gt; to help you generate the cron syntax but Netlify also supports &lt;a href=&quot;https://docs.netlify.com/functions/scheduled-functions/#supported-cron-extensions&quot;&gt;cron extensions&lt;/a&gt; like &lt;code&gt;@yearly&lt;/code&gt; which might be a bit easier to read.&lt;/p&gt;
&lt;h2 id=&quot;testing-the-scheduled-function-locally&quot;&gt;Testing the scheduled function locally&lt;/h2&gt;&lt;p&gt;You&#39;ll want to test the scheduled function locally using the Netlify CLI to check that it works as expected.&lt;/p&gt;
&lt;p&gt;Install the Netlify CLI if you haven&#39;t already:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; netlify-cli&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the following command to start the development server for your static site:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;netlify dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will start a local server and you can access your scheduled function at &lt;code&gt;http://localhost:8888/.netlify/functions/rebuild-yearly&lt;/code&gt; in your browser.&lt;/p&gt;
&lt;p&gt;You can&#39;t test scheduled functions, so you&#39;ll have to invoke the function manually. In another terminal window, you can trigger the function by running the following CURL command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;netlify functions:invoke rebuild-yearly&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: The name of the function matches the name of the file without the file extension.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can check the Netlify dashboard to see if a new deployment has been created. If it has, then your scheduled function is working as expected. 🎉&lt;/p&gt;
&lt;h2 id=&quot;updating-your-site-footer-with-the-latest-date&quot;&gt;Updating your site footer with the latest date&lt;/h2&gt;&lt;p&gt;Now that we&#39;ve got a scheduled function to rebuild our site, we can update the footer with the latest year. You can do this by using a JavaScript function to get the current year and update the footer text.&lt;/p&gt;
&lt;p&gt;For my 11ty site I use the following &lt;code&gt;liquid&lt;/code&gt; template code to update the footer:&lt;/p&gt;
&lt;pre class=&quot;language-liquid&quot;&gt;&lt;code class=&quot;language-liquid&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;footer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Copyright &lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;copy;&quot;&gt;&amp;amp;copy;&lt;/span&gt; 2007-&lt;span class=&quot;token liquid language-liquid&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;now&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token object&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%Y&quot;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;footer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Liquid templates allow the keyword &lt;code&gt;now&lt;/code&gt; or &lt;code&gt;today&lt;/code&gt; to get today&#39;s date and then we format it to only take the year. This will update the footer with the latest year when the site is rebuilt.&lt;/p&gt;
&lt;h2 id=&quot;deploying-the-scheduled-function&quot;&gt;Deploying the scheduled function&lt;/h2&gt;&lt;p&gt;Once you&#39;re happy with your scheduled function, you can deploy it to Netlify. If you have a git repository linked to a Netlify site, you can simply push your changes to the repository and Netlify will automatically deploy your scheduled function.&lt;/p&gt;
&lt;p&gt;You can also use the Netlify CLI. Run the following command to deploy your function to production (or leave off the &lt;code&gt;--prod&lt;/code&gt; flag to create a deploy preview):&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;netlify deploy &lt;span class=&quot;token parameter variable&quot;&gt;--prod&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will deploy your function to Netlify and you can check the Netlify dashboard to see your scheduled function in the &amp;quot;Functions&amp;quot; section.&lt;/p&gt;
&lt;p&gt;Your function will now run every year and you&#39;ll see it in your deploys in the Netlify dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/netlify-automated-production-deploy.png&quot; alt=&quot;Netlify automated production deploy&quot;&gt;&lt;/p&gt;
&lt;p&gt;And that&#39;s it! All done! 🎉&lt;/p&gt;
&lt;h2 id=&quot;example-use-cases&quot;&gt;Example use cases&lt;/h2&gt;&lt;p&gt;I&#39;ve used this to update the footer on my site but you can use this approach to automate any other tasks that you need to run on a schedule.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Weekly newsletter updates&lt;/strong&gt;: I use the &lt;a href=&quot;https://emailoctopus.com/&quot;&gt;EmailOctopus&lt;/a&gt; email service to send out a newsletter and each week I automatically update my site with the latest newsletter from their API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheduled blog posts&lt;/strong&gt;: If you&#39;re missing Wordpress scheduled posts, you can do the same thing with a static site by setting a publish date in your post metadata. Use Netlify scheduled functions to automatically publish them on your site on a specific date. Remy Sharp has a great post on &lt;a href=&quot;https://remysharp.com/2019/06/26/scheduled-and-draft-11ty-posts&quot;&gt;scheduled and draft blog posts with Eleventy&lt;/a&gt; which will show you how to do this.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Activity feed&lt;/strong&gt;: I use the GoodReads API to update my site with the latest books I&#39;ve read and you can regularly update this with a scheduled function. You could do the same with Strava for your latest runs or rides, or Last.fm for your latest music listens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bookmarks&lt;/strong&gt;: Use a bookmark manager like &lt;a href=&quot;https://getpocket.com/&quot;&gt;Pocket&lt;/a&gt; or &lt;a href=&quot;https://raindrop.io/&quot;&gt;Raindrop.io&lt;/a&gt; to save your favourite links and use their APIs to update your site with the latest bookmarks you&#39;ve saved.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;thats-all-folks&quot;&gt;That&#39;s all folks!&lt;/h2&gt;&lt;p&gt;Automating regular website updates for your static site using Netlify scheduled functions is a great way to set it and forget it. You can stop worrying about manual updates and focus on creating new content for your site.&lt;/p&gt;
&lt;p&gt;Go and automate your static site updates with Netlify scheduled functions today! 🤖&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Easily Create Gravatar Images With Eleventy</title>
    
    <link href="https://www.marclittlemore.com/easily-create-gravatar-images-with-eleventy/"/>
    <published>2022-01-30T00:00:00Z</published>
    
    <updated>2022-01-30T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/easily-create-gravatar-images-with-eleventy/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;If you&#39;re like me, your early blogs were probably hosted using &lt;a href=&quot;https://en-gb.wordpress.org/&quot;&gt;Wordpress&lt;/a&gt;. Most of my personal sites started off with a self-hosted version of Wordpress on a server that I had to maintain and update. I&#39;m getting too old for the constant updates that owning a server requires so I have moved to using static site generators and hosting them using &lt;a href=&quot;https://netlify.com/&quot;&gt;Netlify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;m a big fan of &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; as my static site generator as it&#39;s super simple to use. One of the most recent sites had lots of comments and I wanted to keep them in the new Eleventy build. All of the comments have a user image on them and they use a &lt;a href=&quot;https://en.gravatar.com/&quot;&gt;Gravatar&lt;/a&gt; image.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-gravatar&quot;&gt;What is a Gravatar?&lt;/h2&gt;&lt;p&gt;A Gravatar is a &lt;strong&gt;Globally Recognised Avatar&lt;/strong&gt;. It was an idea started by the ex-cofounder of GitHub, &lt;a href=&quot;https://en.wikipedia.org/wiki/Tom_Preston-Werner&quot;&gt;Tom Preston-Werner&lt;/a&gt;, and his Gravatar site and API was eventually bought by &lt;a href=&quot;https://automattic.com/&quot;&gt;Automattic&lt;/a&gt;, the company who build Wordpress. A Gravatar is a user profile image or &amp;quot;avatar&amp;quot; which is attached to a specified email address. It can be uploaded and set on the Gravatar website, and used on any websites that support them. It makes it easy to update your picture in a single place and for it to appear on multiple websites.&lt;/p&gt;
&lt;p&gt;So now we know what a Gravatar is, how do we generate one for Eleventy?&lt;/p&gt;
&lt;h2 id=&quot;adding-an-eleventy-shortcode&quot;&gt;Adding an Eleventy shortcode&lt;/h2&gt;&lt;p&gt;We&#39;re going to add an &lt;a href=&quot;https://www.11ty.dev/docs/shortcodes/&quot;&gt;Eleventy shortcode&lt;/a&gt; to convert an email address into an image URL. This will allow us to easily use it within either an HTML image tag, or from a Markdown file.&lt;/p&gt;
&lt;p&gt;First, we add a shortcode to the Eleventy configuration file &lt;code&gt;.eleventy.js&lt;/code&gt; to expose it in our template files. Let&#39;s call it &lt;code&gt;gravatar&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;.eleventy.js&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;gravatarShortcode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// TODO: Return the Gravatar URL&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


modules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Expose the shortcode for the template&lt;/span&gt;
    eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;gravatar&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; gravatarShortcode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Other configuration code...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding this shortcode into the Eleventy configuration file allows us to add it into one of our Markdown files to render an image. Here&#39;s an example of how we might use it in a blog post&#39;s markdown to render the Gravatar for an email address.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;posts/example-markdown.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;![A Gravatar Image]({% gravatar &quot;email@fakedomain.com&quot; %})&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;creating-a-gravatar-url&quot;&gt;Creating a Gravatar URL&lt;/h2&gt;&lt;p&gt;A &lt;a href=&quot;https://en.gravatar.com/site/implement/images/&quot;&gt;Gravatar image URL&lt;/a&gt; is created by calculating an &lt;a href=&quot;https://en.wikipedia.org/wiki/MD5&quot;&gt;MD5 hash&lt;/a&gt; of the email you want to use. We can use the Node.js &lt;code&gt;crypto&lt;/code&gt; module to easily generate this.&lt;/p&gt;
&lt;p&gt;We should ensure that the email address we pass to the hashing function is cleaned up. We can remove any leading or trailing spaces and make it lowercase. That will ensure it creates an identical hash if someone accidently calls it with a different case or some extra whitespace.&lt;/p&gt;
&lt;p&gt;Finally, we need to return a Gravatar URL with the hash appended to it.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;.eleventy.js&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; crypto &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;crypto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;gravatarShortcode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Clean up the email address&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// - Remove any leading or trailing spaces&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// - Make it lowercase&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cleanEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Create an MD5 hash from the cleaned email address&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; emailHash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;md5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cleanEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Return a URL image with the hash appended&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;emailHash&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In our template we&#39;ll now have an image URL for the email if a Gravatar exists for it.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;posts/example-markdown.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;![A Gravatar Image]({% gravatar &quot;email@fakedomain.com&quot; %})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above &lt;code&gt;gravatar&lt;/code&gt; shortcode becomes this image URL:&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;posts/example-markdown.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;[&lt;span class=&quot;token content&quot;&gt;A Gravatar Image&lt;/span&gt;](&lt;span class=&quot;token url&quot;&gt;https://www.gravatar.com/avatar/4cfeb4eed871ce152cfef83b542ad62f&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you wanted to use the shortcode in some HTML as an image tag you can do that too:&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;example.html&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{% gravatar &lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;email@fakedomain.com&quot;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;%}&quot;&lt;/span&gt; 
        &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Jane Doe&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Avatar of user&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;150&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;150&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is a Gravatar of me embedded into this Markdown file:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.gravatar.com/avatar/4b0d41741f2ffec347794d31f02daa56?d=mp&amp;amp;s=80&quot; alt=&quot;Gravatar&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;adding-a-size&quot;&gt;Adding a size&lt;/h2&gt;&lt;p&gt;The Gravatar URL allows you to append some additional query parameters to change the size of the image that the URL returns. We can update our shortcode to allow us to pass in a size so that the Gravatar URL renders it at the resolution we need.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;.eleventy.js&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; crypto &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;crypto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Add a size parameter which defaults to 80&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;gravatarShortcode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Clean up the email address&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// - Remove any leading or trailing spaces&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// - Make it lowercase&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cleanEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Create an MD5 hash from the cleaned email address&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; emailHash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;md5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cleanEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Return a URL image with the hash appended&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Append a size query parameter&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;emailHash&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;?s=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;size&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s try it out and render a larger Gravatar of me!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.gravatar.com/avatar/4b0d41741f2ffec347794d31f02daa56?d=mp&amp;amp;s=200&quot; alt=&quot;Gravatar&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;adding-a-default-image&quot;&gt;Adding a default image&lt;/h2&gt;&lt;p&gt;Finally, the Gravatar URL allows us to &lt;a href=&quot;https://en.gravatar.com/site/implement/images#default-image&quot;&gt;use a default image&lt;/a&gt; which will be rendered if no Gravatar is found for the email hash. We can add that as another query parameter in the generated URL.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;.eleventy.js&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; crypto &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;crypto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Add a defaultImage parameter which defaults to &#39;mp&#39; (mystery person)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; gravatarShortcode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; defaultImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;mp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Clean up the email address&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// - Remove any leading or trailing spaces&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// - Make it lowercase&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cleanEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Create an MD5 hash from the cleaned email address&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; emailHash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;md5&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cleanEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Return a URL image with the hash appended&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Append a default image query parameter&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://www.gravatar.com/avatar/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;emailHash&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;?s=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;size&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;amp;d=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;defaultImage&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&#39;s a default image for an unknown email address which uses the &amp;quot;mystery person&amp;quot; image.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;posts/example-markdown.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;![Gravatar]({% gravatar &quot;unknown@email.com&quot; 150 %})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://www.gravatar.com/avatar/6b08a0d645c12fdaf167c5b54436338b?d=mp&amp;amp;s=150&quot; alt=&quot;Gravatar&quot;&gt;&lt;/p&gt;
&lt;p&gt;You could also render a robot as the default image instead.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Filename&lt;/span&gt; &lt;span class=&quot;&quot;&gt;posts/example-markdown.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;![Gravatar]({% gravatar &quot;unknown@email.com&quot; 150 &quot;robohash&quot; %})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://www.gravatar.com/avatar/6b08a0d645c12fdaf167c5b54436338b?d=robohash&amp;amp;s=150&quot; alt=&quot;Gravatar&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;See how easy it is to add a Gravatar with Eleventy? Eleventy&#39;s awesome shortcode system gives us the ability to easily extend it and expose Gravatars in our templates. Try it out if you&#39;re moving your Wordpress site over to Eleventy.&lt;/p&gt;
&lt;p&gt;I hope you found it helpful but &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;let me know&lt;/a&gt; if you have any questions or spot any errors in the code.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Improving Your Workflow with JavaScript Testing: Best Practices</title>
    
    <link href="https://www.marclittlemore.com/javascript-testing/getting-started-with-javascript-testing/"/>
    <published>2021-07-24T00:00:00Z</published>
    
    <updated>2021-07-24T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/javascript-testing/getting-started-with-javascript-testing/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;One of the questions people ask when starting their journey into testing their JavaScript code is where to start. What functions should the team test? Do we need to test every function, or should we look at testing the whole API end-to-end? Do we have mock out all of our backend services like databases or cloud storage?&lt;/p&gt;
&lt;p&gt;It can be overwhelming to know where to start.&lt;/p&gt;
&lt;p&gt;This paralysis often stops people from adding tests at all.&lt;/p&gt;
&lt;h2 id=&quot;starting-to-test-your-code&quot;&gt;Starting to test your code&lt;/h2&gt;&lt;p&gt;Testing is often an area that&#39;s overlooked when starting to writing code, especially if your team is unfamiliar with the process. You start with with a proof of concept demo project and before you know it, it has become the production code and deployed without any tests.&lt;/p&gt;
&lt;p&gt;There&#39;s a common misconception that tests are there to find bugs in your code. This is a great side effect, and you can write additional tests to confirm bugs are fixed when you find them, but its not the main reason. The primary goal is to make sure that your code is working as expected and to give you and the whole team confidence in your codebase.&lt;/p&gt;
&lt;p&gt;When you write tests, they should be testing one thing: given an input into a function you are checking that you always receive the same output. Tests are there to give you confidence in your code so that you can make changes and ensure that everything still produces the same results as it did before.&lt;/p&gt;
&lt;p&gt;Another common worry is that testing will take too much time and effort.&lt;/p&gt;
&lt;p&gt;Teams often think it&#39;s not worth the time to add tests to your code. It&#39;s true that testing takes some upfront thinking about your code, but this often makes your design better. Not being able to test your code well is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_smell&quot;&gt;&amp;quot;code smell&amp;quot;&lt;/a&gt; - something which doesn&#39;t feel right and suggests that there are other problems.&lt;/p&gt;
&lt;p&gt;While testing your code takes some time and effort, the benefits pay for themselves later when we deploy the code to your production environment. Being able to run all of your tests on your continuous integration server, and confirm the tests all pass, gives you confidence that your end users will see fewer or no issues.&lt;/p&gt;
&lt;p&gt;The types of tests you will want to add to your code may differ depending on how much code has already been written.&lt;/p&gt;
&lt;h2 id=&quot;adding-tests-to-an-existing-codebase&quot;&gt;Adding tests to an existing codebase&lt;/h2&gt;&lt;p&gt;If you&#39;ve already got an existing project with lots of code, a good approach is to start writing &lt;a href=&quot;https://www.marclittlemore.com/different-types-of-software-tests/&quot;&gt;end-to-end tests&lt;/a&gt;. These test the functionality that the user will see and don&#39;t attempt to mock out any of your dependencies.&lt;/p&gt;
&lt;p&gt;For end-to-end tests, you can use a fresh development environment that you create to run the tests. This can match your production environment as closely as it can but it&#39;s created and intialised when you start running your end-to-end tests. Alternatively, you can initialise your data before your tests run and remove this data at the end of the test cycle. This is often known as the setup and tear-down process in a lot of test frameworks.&lt;/p&gt;
&lt;p&gt;Your database could be intialised via fixture files, data which is loaded from a file, and contain the initial state of your application that you want to test. Your tests can assert that the data changes correctly as you run your test cases. It&#39;s good practice to ensure that your tests can run independently. You want to avoid sharing state between tests. You don&#39;t want a test to fail because a previous test has run incorrectly.&lt;/p&gt;
&lt;p&gt;End-to-end tests are great because they mimic what a real user would do when they use your application. However, they can take longer to run and can take development time to get the data and environment set up correctly.&lt;/p&gt;
&lt;p&gt;If you want to start writing some end-to-end tests for your code then take a look at &lt;a href=&quot;https://www.cypress.io/&quot;&gt;Cypress&lt;/a&gt;, &lt;a href=&quot;https://testcafe.io/&quot;&gt;Test Cafe&lt;/a&gt;, or &lt;a href=&quot;https://codecept.io/&quot;&gt;Codecept&lt;/a&gt;. These are all fantastic testing frameworks which will help you to get started.&lt;/p&gt;
&lt;p&gt;A good compromise, and a complement to end-to-end tests, are &lt;a href=&quot;https://www.marclittlemore.com/different-types-of-software-tests/&quot;&gt;integration tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Integration tests can be used to test your code but for these tests you can mock or stub your dependencies with fake data. This means that things like upstream APIs or databases can have mocked data that you control. This allows you to avoid having to depend on parts of your system that you might not easily control. You are able to provide your application with both the happy path, when the code works as expected, and the unhappy paths, when there are errors or upstream APIs don&#39;t work as expected.&lt;/p&gt;
&lt;p&gt;A good rule of thumb is to mock or stub dependecies which aren&#39;t under your control. For example, if you have a database or API that you don&#39;t control, you can mock it out to provide fake data.&lt;/p&gt;
&lt;p&gt;Integration tests can be harder to get started with as you have to understand how to provide fake data for your systems. You must match the data structure that is returned from your database call or API endpoints and ensure that it is consistent with the real services. How this is done will depend on how your code is architected. A common JavaScript or Node.js pattern is to use mocking libraries such as &lt;a href=&quot;https://sinonjs.org/&quot;&gt;Sinon&lt;/a&gt;, &lt;a href=&quot;https://github.com/thlorenz/proxyquire&quot;&gt;Proxyquite&lt;/a&gt; or &lt;a href=&quot;https://jestjs.io/&quot;&gt;Jest&lt;/a&gt; mock functions to provide test doubles for modules. You can also mock HTTP requests with &lt;a href=&quot;https://github.com/nock/nock&quot;&gt;Nock&lt;/a&gt; and spin up your API locally using &lt;a href=&quot;https://github.com/visionmedia/supertest&quot;&gt;Supertest&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-many-tests-to-write&quot;&gt;How many tests to write&lt;/h2&gt;&lt;p&gt;Don&#39;t get hung up on your code&#39;s test coverage. This is a metric to determine what percentage of your code modules, functions, and logic branches are covered by tests. The goal is to write tests that are easy to understand and maintain, not to test everything. If you&#39;re not sure how many tests you need to write, start small and add more as you go.&lt;/p&gt;
&lt;p&gt;Start by testing the primary flows of your code and then add more as you continue to add new features. It&#39;s more import to get started with small test cases than to have an exhaustive test suite.&lt;/p&gt;
&lt;p&gt;Decide on which parts of the flows could fail and how important that would be to the team or your users. If it&#39;s important part of your code, then you should write tests for that. For example, if you&#39;re developing an e-commerce application, consider testing the code to add and delete items from the shopping cart and ensure that your payment flows work. These would be the key business areas that should rarely fail as you&#39;d lose money if they did.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;It is easier than you think to get started with testing your code. If you&#39;re new to the process, it can be a little overwhelming. Don&#39;t worry, it&#39;s not that hard and you can get started really quickly.&lt;/p&gt;
&lt;p&gt;Let me know if you have any questions or comments and I&#39;d be happy to help.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;Want to learn more about testing your JavaScript code? Check out my &lt;a href=&quot;https://www.marclittlemore.com/javascript-testing/&quot;&gt;other JavaScript testing articles&lt;/a&gt; for more tips and techniques!&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;</content>
    
  </entry>
  
  <entry>
    
    <title>Streamline Your Workflow: Automate GitLab Releases with Semantic-Release</title>
    
    <link href="https://www.marclittlemore.com/automating-your-releases-with-semantic-release-and-gitlab/"/>
    <published>2021-07-10T00:00:00Z</published>
    
    <updated>2021-07-10T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/automating-your-releases-with-semantic-release-and-gitlab/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Each week one of my new team makes a release and deployment of our API service from our GitLab repository. It is a very manual process that involves working out what has changed in the previous commits, determining what sort of version it should be, tagging it in GitLab, and running a GitLab pipeline to deploy it to the correct server. It&#39;s a tedious process that can be prone to human error.&lt;/p&gt;
&lt;p&gt;On a previous project, I&#39;d used a tool called &lt;code&gt;semantic-release&lt;/code&gt; in combination with conventional commit messages to automate the process. This gave a quick and easy release process without any human interaction. I love automation so it felt like the perfect tool for the job.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-release&quot;&gt;What is a release?&lt;/h2&gt;&lt;p&gt;A release will depend on your stack of choice and platform. For a desktop application, you could be taking the latest code, compiling the code, and creating an executable file. For web applications, this is often an update of a release number and re-deploying the application to your servers.&lt;/p&gt;
&lt;p&gt;Our team creates a Node.js API that is consumed by multiple desktop and mobile clients. A release for us is a &lt;code&gt;git&lt;/code&gt; tagged release which is then deployed to our development, QA, or production environments. We also bump our Node &lt;code&gt;package.json&lt;/code&gt; version number, although as we&#39;re not versioning a library, this isn&#39;t strictly necessary.&lt;/p&gt;
&lt;p&gt;Version numbers in the Node.js ecosystem use the standard &lt;a href=&quot;https://semver.org/&quot;&gt;semantic versioning&lt;/a&gt; (also known as &lt;strong&gt;SemVer&lt;/strong&gt;). This gives us a release number in the format of &lt;code&gt;major.minor.patch&lt;/code&gt;. A &lt;code&gt;major&lt;/code&gt; version is determined by a breaking change, for example, the API makes incompatible changes. A &lt;code&gt;minor&lt;/code&gt; version is generated if new functionality is added but the functionality is backwards compatible with previous releases. Finally, a &lt;code&gt;patch&lt;/code&gt; version is changed if any bug fixes are made.&lt;/p&gt;
&lt;p&gt;So how do we determine when to change the version number? This is up to the development team to think about the changes that are made. However, we can infer it automatically if the team uses consistent &lt;code&gt;git&lt;/code&gt; commit messages.&lt;/p&gt;
&lt;h2 id=&quot;conventional-commits&quot;&gt;Conventional Commits&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://www.conventionalcommits.org/&quot;&gt;conventional commits&lt;/a&gt; specification is a lightweight convention that can be adopted for commit messages. It provides you with an easy set of rules to follow to create consistent commit messages. This allows automated tools to parse the &lt;code&gt;git&lt;/code&gt; messages to determine a semantic version number.&lt;/p&gt;
&lt;p&gt;A commit message is structured in the following way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type(optional scope): description of commit

[optional body]

[optional footer]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;feat(user): add validation schema on user create endpoint
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have a consistent format for our commit messages, how do we ensure that the team sticks to it?&lt;/p&gt;
&lt;p&gt;For this, we can look at a tool to lint the commit messages before anyone is allowed to commit their code. A tool called &lt;a href=&quot;https://github.com/conventional-changelog/commitlint&quot;&gt;commitlint&lt;/a&gt; can be used to validate commit messages. We can also use the &lt;a href=&quot;https://github.com/typicode/husky&quot;&gt;husky&lt;/a&gt; package to add automated &lt;code&gt;git&lt;/code&gt; hooks to the project. With this, a &lt;code&gt;git&lt;/code&gt; hook can be added which will lint the commit message to our standardised format before the code is committed to our project.&lt;/p&gt;
&lt;p&gt;Assuming you have a Node.js project set up, &lt;code&gt;commitlint&lt;/code&gt; can be installed like this:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install commitlint CLI&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev @commitlint/cli&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although &lt;code&gt;commitlint&lt;/code&gt; allows you to validate against a variety of commit message formats, including your configuration, we chose to use the &lt;code&gt;config-conventional&lt;/code&gt; configuration which adheres to the conventional commit message format. You can install it as follows:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install conventional config for commitlint&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev @commitlint/config-conventional&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can set up the &lt;code&gt;commitlint&lt;/code&gt; configuration file called &lt;code&gt;commitlint.config.js&lt;/code&gt; so it uses the &lt;code&gt;config-conventional&lt;/code&gt; message format:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;module.exports = { extends: [&#39;@commitlint/config-conventional&#39;] };&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; commitlint.config.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&#39;ve set up &lt;code&gt;commitlint&lt;/code&gt;, we can ensure that it gets called when we attempt to commit code into &lt;code&gt;git&lt;/code&gt;. We can install &lt;code&gt;husky&lt;/code&gt; and add a &lt;code&gt;commit-msg&lt;/code&gt; hook:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Install Husky&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; husky --save-dev

&lt;span class=&quot;token comment&quot;&gt;# Activate Husky git hooks&lt;/span&gt;
npx husky &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Add the commit-msg hook&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# It runs commitlint with the given commit message&lt;/span&gt;
npx husky &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; .husky/commit-msg &lt;span class=&quot;token string&quot;&gt;&#39;npx --no-install commitlint --edit $1&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now all commit messages must adhere to the &lt;code&gt;config-conventional&lt;/code&gt; commit format and &lt;code&gt;git&lt;/code&gt; will fail to commit any code that has messages that don&#39;t validate.&lt;/p&gt;
&lt;p&gt;The team should now all be using the same commit message format so we can use that with &lt;code&gt;semantic-release&lt;/code&gt; to determine how we want to release.&lt;/p&gt;
&lt;h2 id=&quot;semantic-release&quot;&gt;Semantic Release&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://github.com/semantic-release/semantic-release&quot;&gt;semantic-release&lt;/a&gt; package automates your release workflow. It analyses the &lt;code&gt;git&lt;/code&gt; commit messages, determines the next version number, generates release notes and a changelog file, and publishes the package for you.&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;semantic-release&lt;/code&gt; has several plugins which are part of the main package. These allow you to automatically determine the release, update the version in &lt;code&gt;package.json&lt;/code&gt;, deploy to the &lt;code&gt;npm&lt;/code&gt; registry for public packages, and tag a release in GitHub. As my team has its code stored in a private GitLab repository, we need some additional configuration.&lt;/p&gt;
&lt;p&gt;First, let&#39;s install &lt;code&gt;semantic-release&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev semantic-release&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to create a &lt;code&gt;CHANGELOG.md&lt;/code&gt; file that can be shared outside of the team, commit that file and the updated &lt;code&gt;package.json&lt;/code&gt; to our &lt;code&gt;git&lt;/code&gt; repository, and tag a release in GitLab. For this we&#39;ll need to add the following plugins:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev @semantic-release/changelog @semantic-release/git @semantic-release/gitlab&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To override the normal &lt;code&gt;semantic-release&lt;/code&gt; configuration, we can add a &lt;code&gt;releases&lt;/code&gt; key to our &lt;code&gt;package.json&lt;/code&gt; and configure the additional plugins:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;release&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;plugins&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;@semantic-release/commit-analyzer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;@semantic-release/release-notes-generator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;@semantic-release/changelog&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;@semantic-release/npm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;@semantic-release/git&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;@semantic-release/gitlab&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ...
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;determine-when-a-release-should-be-created&quot;&gt;Determine when a release should be created&lt;/h2&gt;&lt;p&gt;Once &lt;code&gt;semantic-release&lt;/code&gt; is set up correctly, it will use its &lt;code&gt;commit-analyzer&lt;/code&gt; plugin to parse your commit messages and determine whether a new release should be created. By default, it creates a release when the following commit types are present in the commit message:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fix&lt;/code&gt; or &lt;code&gt;perf&lt;/code&gt; - A fix or performance commit message will trigger a new patch release&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feat&lt;/code&gt; - A feature commit message will trigger a new minor release&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;BREAKING CHANGE&lt;/code&gt; is written in the commit message, it will trigger a major release.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These defaults can be changed by adding additional rules to &lt;code&gt;commit-analyzer&lt;/code&gt; using the plugin configuration in your &lt;code&gt;package.json&lt;/code&gt;. We found some issues with it not creating releases when we expected it to, so see the &lt;a href=&quot;https://www.marclittlemore.com/automating-your-releases-with-semantic-release-and-gitlab/#gotchas&quot;&gt;gotchas&lt;/a&gt; section for rules that we added.&lt;/p&gt;
&lt;h2 id=&quot;gitlab-pipelines&quot;&gt;GitLab pipelines&lt;/h2&gt;&lt;p&gt;Now that the script and plugins are ready to determine when a new release should be tagged, it&#39;s time to set up a &lt;a href=&quot;https://docs.gitlab.com/ee/ci/pipelines/&quot;&gt;GitLab pipeline&lt;/a&gt; to do so.&lt;/p&gt;
&lt;p&gt;How you do this will vary depending on your project&#39;s need. For our project, we first want to run our tests and linting jobs to ensure they all pass. After that, we can determine if we should create a release by running &lt;code&gt;semantic-release&lt;/code&gt;. We only want to check this on our &lt;code&gt;main&lt;/code&gt; branch so that we don&#39;t attempt to create a release for all branches pushed to GitLab which are used for merge requests. Finally, we have a deployment job that will run only if a new release has been tagged. This is our continuous deployment step.&lt;/p&gt;
&lt;p&gt;As we want to use GitLab&#39;s API to tag a release, we need to create an environment variable called &lt;code&gt;GITLAB_TOKEN&lt;/code&gt; which is exposed to the pipeline. This will allow us to have write access to the API.&lt;/p&gt;
&lt;p&gt;To do this you need to create a new access token in your project in GitLab by going to &lt;code&gt;Settings -&amp;gt; Access Tokens&lt;/code&gt;. The access token will need read and write access to the API and also the ability to read and write to the repository. It should look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/github-access-token-setup.png&quot; alt=&quot;GitLab access token setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;For security, your access token will only be shown once so copy the token ready to add it as an environment variable into your GitLab project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/gitlab-environment-variables-setup.png&quot; alt=&quot;GitLab access token setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once the environment variable has been added to the pipeline, we can build the GitLab pipeline to use the access token with &lt;code&gt;semantic-release&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example GitLab YAML pipeline:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# semantic-release requires optional chaining which&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# appears in Node 14.5+&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14.15&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Assuming we&#39;re:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# - running tests&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# - creating a new release if applicable&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# - deploying the release if applicable&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; test
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; release
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; deploy

&lt;span class=&quot;token comment&quot;&gt;# An example job to run the unit tests&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;run-unit-tests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; test
  &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; npm run test&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;unit
  &lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &amp;lt;your&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# An example job to run integration tests&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;run-integration-tests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; test
  &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; npm run test&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;integration
  &lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &amp;lt;your&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# A job to cut a new release&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# This will run before our deployment&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;create-release&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# This is part of the release stage&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; release

  &lt;span class=&quot;token comment&quot;&gt;# Only attempt to run this job when we merge to the main branch&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;only&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;refs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main
  
  &lt;span class=&quot;token comment&quot;&gt;# Expose the GITLAB_TOKEN environment variable to this job&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# as a GitLab variable to be used by semantic-release&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;GITLAB_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $GITLAB_TOKEN
  
  &lt;span class=&quot;token comment&quot;&gt;# Execute semantic-release to create a new release&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; npx semantic&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;release
  
  &lt;span class=&quot;token comment&quot;&gt;# Add the runner that will execute this job&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &amp;lt;your&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# An example deployment script&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;development-deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;stage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; deploy

  &lt;span class=&quot;token comment&quot;&gt;# Only run this job when a release has been tagged&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $COMMIT_TAG
      &lt;span class=&quot;token key atrule&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; always
  &lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &amp;lt;your&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deployment&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &amp;lt;your&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the above example GitLab pipeline, a new release will be created if your commit messages should update the version number when you merge your code to your &lt;code&gt;main&lt;/code&gt; branch from a merge request.&lt;/p&gt;
&lt;h2 id=&quot;advantages-of-automatic-releases&quot;&gt;Advantages of automatic releases&lt;/h2&gt;&lt;p&gt;Using &lt;code&gt;semantic-release&lt;/code&gt; to automatically generate your releases gives many advantages.&lt;/p&gt;
&lt;p&gt;Moving towards consistent commit messages using &lt;code&gt;commitlint&lt;/code&gt; allows the team to move towards smaller, more atomic pull or merge requests. This makes code reviews much easier as your team will gravitate towards less code for each branch.&lt;/p&gt;
&lt;p&gt;The automatic generation of release notes and a changelog file allows you to automatically inform stakeholders by sharing a link to the latest release. It avoids the team having to perform &lt;code&gt;git&lt;/code&gt; comparisons between commits to determine what has changed.&lt;/p&gt;
&lt;p&gt;If you&#39;re publishing a library, the &lt;code&gt;npm&lt;/code&gt; plugin allows you to automatically publish to the &lt;code&gt;npm&lt;/code&gt; registry. No more manual steps to publish your latest and greatest work.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; If your project is marked as private in your &lt;code&gt;package.json&lt;/code&gt; file, there will be no attempt to publish the application if you use the &lt;code&gt;npm&lt;/code&gt; plugin. It will just be used to update your &lt;code&gt;package.json&lt;/code&gt; version number.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It avoids the team having to think about releasing manually. This is often a job that happens daily or weekly and it now happens automatically. Ultimately, you&#39;ll start to see a faster release cadence with these smaller, atomic changes.&lt;/p&gt;
&lt;h2 id=&quot;gotchas&quot;&gt;Gotchas&lt;/h2&gt;&lt;p&gt;While using &lt;code&gt;semantic-release&lt;/code&gt; has made life easier, there are some potential issues you might have to deal with.&lt;/p&gt;
&lt;p&gt;Firstly, &lt;code&gt;semantic-release&lt;/code&gt; claims to work with Node v10+. However, when I tried it in the GitLab pipeline, one of the plugins depends on a new JavaScript operator called the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot;&gt;optional chaining operator&lt;/a&gt;. This only appears in Node 14.5 and beyond so make sure that any Docker containers, or the GitLab Node image you use, use Node 14.5 or later.&lt;/p&gt;
&lt;p&gt;If you use the &lt;code&gt;@semantic-release/git&lt;/code&gt; plugin, it will commit the changes in your &lt;code&gt;CHANGELOG.md&lt;/code&gt; or &lt;code&gt;package.json&lt;/code&gt; files to your repository. This &lt;code&gt;git&lt;/code&gt; commit will trigger a new pipeline on your &lt;code&gt;main&lt;/code&gt; branch, and hence this can start another pipeline to re-check the release.&lt;/p&gt;
&lt;p&gt;Tagging a new version in &lt;code&gt;git&lt;/code&gt; also causes an additional pipeline to trigger. Again, you may have to play with your pipeline jobs to avoid unnecessarily triggering another pipeline.&lt;/p&gt;
&lt;p&gt;If you use the &lt;code&gt;revert&lt;/code&gt; type commit message, it won&#39;t trigger a new release unless you use a &lt;code&gt;git revert&lt;/code&gt; command or the message has &lt;code&gt;This reverts commit &amp;lt;your-commit-sha&amp;gt;&lt;/code&gt;. If reverting code, you&#39;d expect the version to be changed as functionality will have changed. Using the &lt;code&gt;refactor&lt;/code&gt; type also doesn&#39;t trigger a new release. In theory, a refactor shouldn&#39;t change the functionality so it&#39;s understandable that it might not trigger the release. In practice, a refactor often needs to be deployed to sanity check that nothing has broken.&lt;/p&gt;
&lt;p&gt;To fix both of these issues, you can add additional config to the &lt;code&gt;commit-analyzer&lt;/code&gt; plugin to trigger release off the &lt;code&gt;revert&lt;/code&gt; or &lt;code&gt;refactor&lt;/code&gt; commit types.&lt;/p&gt;
&lt;p&gt;In the example below, a &lt;code&gt;revert&lt;/code&gt; type will trigger a new patch release and a &lt;code&gt;refactor&lt;/code&gt; gives a new minor release.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;release&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;plugins&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;@semantic-release/commit-analyzer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;&quot;releaseRules&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;revert&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token property&quot;&gt;&quot;release&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;patch&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;refactor&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token property&quot;&gt;&quot;release&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;minor&quot;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      ...
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ...
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Using &lt;code&gt;sematic-release&lt;/code&gt; is a great way to take the stress out of your release process and help your team to move towards a continuous deployment process. Although this post mainly concentrates on a Node.js automatic release pipeline, the tools suggested could be used to create automated releases with other languages too.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;Let me know&lt;/a&gt; if you have any questions or spot any errors.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Lessons learned from 30 days of writing</title>
    
    <link href="https://www.marclittlemore.com/lessons-learned-from-30-days-of-writing/"/>
    <published>2021-04-22T00:00:00Z</published>
    
    <updated>2021-04-22T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/lessons-learned-from-30-days-of-writing/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;In the middle of January 2021, I decided to start a &lt;a href=&quot;https://www.marclittlemore.com/creating-a-writing-habit/&quot;&gt;habit of writing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was inspired by the &lt;a href=&quot;https://ship30for30.com/&quot;&gt;#ship30for30&lt;/a&gt; community I&#39;d seen on Twitter. The hashtags were appearing daily and I thought a writing habit would be good to start and could be a useful tool to clarify my thinking.&lt;/p&gt;
&lt;p&gt;Here are some of the lessons I learned after my &lt;a href=&quot;https://www.marclittlemore.com/writing/&quot;&gt;30 days of writing&lt;/a&gt; experiment was over.&lt;/p&gt;
&lt;h2 id=&quot;it-took-more-than-30-days&quot;&gt;It took more than 30 days&lt;/h2&gt;&lt;p&gt;My original plan was take the #ship30for30 hashtag literally and write 30 articles in 30 days. The Ship 30 community revolves around making much smaller articles than I ended up writing. Their idea is to write an &amp;quot;atomic essay&amp;quot; of approximately 250 to 500 words to summarise a topic.&lt;/p&gt;
&lt;p&gt;I initially attempted to keep the posts small but I struggled to articulate what I wanted to say in so few words. Anyone that knows me will tell you that I can talk a lot and my writing style matches this. I tried to get better at editing but even then, most articles ended up being between 500 and 1500 words.&lt;/p&gt;
&lt;p&gt;In the end, my 30-day writing challenge took 55 days. I&#39;m still incredibly proud of what I achieved, but it didn&#39;t happen as quickly as I initially thought it would.&lt;/p&gt;
&lt;h2 id=&quot;i-enjoy-writing&quot;&gt;I enjoy writing&lt;/h2&gt;&lt;p&gt;I&#39;ve always struggled with thinking that I was more mathematically-minded and &amp;quot;wasn&#39;t a great writer&amp;quot;. Part of this experiment was me trying to dispel the myth that I&#39;d built up in my own head.&lt;/p&gt;
&lt;p&gt;I knew that I wasn&#39;t bad at technical writing. In my day job, I wrote a lot and I could be clear and succinct when I needed to be. Writing a new article every couple of days really helped me to exercise my writing muscle and I found out that I &lt;strong&gt;actually enjoyed writing regularly&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;people-enjoyed-reading-the-articles&quot;&gt;People enjoyed reading the articles&lt;/h2&gt;&lt;p&gt;Having a website where you share personal views is a double-edged sword. On the one hand, you worry that nobody is going to read what you write. On the other hand, you worry that everyone will see what you write, especially if you make it more personal.&lt;/p&gt;
&lt;p&gt;Every time I wrote a new article, I shared it on Twitter and on Linked In. While the aim was never to attempt to go viral, I found that people enjoyed reading the articles. Twitter wasn&#39;t the biggest driver of traffic but as I ended up writing more articles around team cultures, Linked In became a great place for a discussion. I found that my writing resonated with people and they commented on each article I shared.&lt;/p&gt;
&lt;p&gt;I talked about &lt;a href=&quot;https://www.marclittlemore.com/share-your-network/&quot;&gt;sharing your network&lt;/a&gt; and &lt;a href=&quot;https://www.marclittlemore.com/learn-in-public/&quot;&gt;learning in public&lt;/a&gt; in one of my articles and I found that sharing my work expanded my own network. I talked to a few recruiters who had &amp;quot;heard of me&amp;quot; from my articles. It also allowed me to have great conversations with new people. I especially enjoyed a Zoom chat I had with &lt;a href=&quot;https://www.linkedin.com/in/duncanskelton/&quot;&gt;Duncan Skelton&lt;/a&gt; about leadership and coaching. Writing has really opened up my network to more opportunities.&lt;/p&gt;
&lt;h2 id=&quot;finding-my-cadence&quot;&gt;Finding my cadence&lt;/h2&gt;&lt;p&gt;I mentioned that I didn&#39;t manage to write 30 articles in 30 days but I did manage to find a cadence that worked for me. My wife is currently a teacher and also tutors some students for a couple of hours on Monday to Wednesday from around 6.30 pm. I found that attempting to write at this time gave me a chance to focus for 2 hours on writing. Sometimes I&#39;d be helping my children with homework, but most of the time, this routine worked well.&lt;/p&gt;
&lt;p&gt;Trying to write on Thursday and Friday evenings was much harder as my wife wasn&#39;t tutoring and I was involved more with the family. I&#39;m an early riser and often wake up early at the weekends. I found that trying to write for an hour on Saturday or Sunday morning worked well too. I&#39;d often finished a new article before the rest of the family were out of bed.&lt;/p&gt;
&lt;h2 id=&quot;ideas-didnt-always-flow&quot;&gt;Ideas didn&#39;t always flow&lt;/h2&gt;&lt;p&gt;Some evenings I really struggled with what to write about. I wrote an article about &lt;a href=&quot;https://www.marclittlemore.com/creating-an-idea-habit/&quot;&gt;creating an idea habit&lt;/a&gt; and even then I sometimes couldn&#39;t focus on a single idea. I hadn&#39;t prepared a list of ideas in advance and so this is why it took much longer than 30 days.&lt;/p&gt;
&lt;p&gt;Ideas often came from great conversations I had with my team in their 1:1 meetings, or when I caught up with colleagues. I wrote down these ideas as soon as I could and with them, I was able to more easily outline the article. In future I need to ensure I keep a list of article ideas so that I can just pick from them and start writing.&lt;/p&gt;
&lt;h2 id=&quot;taking-notes-makes-writing-easy&quot;&gt;Taking notes makes writing easy&lt;/h2&gt;&lt;p&gt;While sometimes I lacked ideas, the articles I found the most easy to write were those where I&#39;d previously written a bunch of notes in &lt;a href=&quot;https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/&quot;&gt;Obsidian&lt;/a&gt;. It wasn&#39;t just a single idea. It was a collection of ideas around a single topic which I&#39;d often outlined into key points. Being able to pick one of these notes made it easy to write an article and I could often finish a 1000 word article within a couple of hours.&lt;/p&gt;
&lt;h2 id=&quot;technical-articles-take-a-long-time&quot;&gt;Technical articles take a long time&lt;/h2&gt;&lt;p&gt;I wasn&#39;t sure of the intial direction that my 30-day writing challenge was going to take. As an Engineering Manager, I tend to write less code in my day job. However, that means I&#39;m often spending my evenings reading about new languages or frameworks and wanting to try them out.&lt;/p&gt;
&lt;p&gt;I decided to write a few technical articles as part of my writing challenge but I found they were much harder. When I write a technical post, I care that the reader is getting a learning experience. That means I spent a lot of time ensuring that I&#39;d captured every step in writing the code for the article. This slowed down the writing process and meant that a technical article often took 4 or 5 hours to write.&lt;/p&gt;
&lt;p&gt;Whilst I really enjoy writing technical articles, the time taken meant I wrote fewer of those. Writing one per week would be much easier and I&#39;ll look to do that in the future.&lt;/p&gt;
&lt;h2 id=&quot;would-i-do-it-again&quot;&gt;Would I do it again?&lt;/h2&gt;&lt;p&gt;I very much enjoyed the challenge and I think its made me a better writer. My website readership has increased and I&#39;m starting to rank for various keywords now too. I don&#39;t think I&#39;d attempt to write every day again as this was difficult at times. However, writing a new article weekly is what I want to do from now on.&lt;/p&gt;
&lt;p&gt;You should try a 30-day writing challenge if you haven&#39;t.&lt;/p&gt;
&lt;p&gt;I promise that it&#39;ll change the way you think.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>You've got this: become a confident developer</title>
    
    <link href="https://www.marclittlemore.com/youve-got-this-become-a-confident-developer/"/>
    <published>2021-03-09T00:00:00Z</published>
    
    <updated>2021-03-09T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/youve-got-this-become-a-confident-developer/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I&#39;ve never liked the title Junior Developer. I much prefer Less-Experienced Developer. It doesn&#39;t imply age. It just says &amp;quot;you don&#39;t have the knowledge yet&amp;quot;.&lt;/p&gt;
&lt;p&gt;Being a Less-Experienced Developer is where we all start.&lt;/p&gt;
&lt;p&gt;We might be less-experienced in software development as a whole. Or we might be less-experienced in a specific programming languange. Or a specific domain. Or testing. Or in front-end skills. Or databases. Or infrastructure. Or tooling. And so on...&lt;/p&gt;
&lt;p&gt;It can be overwhelming to think of all of the software development concepts that you don&#39;t know. I&#39;ve been a professional software developer since 1994 and I have a list as long as my arm of all of the things I don&#39;t yet know. And I like that. I&#39;m &lt;a href=&quot;https://www.marclittlemore.com/always-be-learning/&quot;&gt;always learning&lt;/a&gt; so it gives me something else to look at when I feel I&#39;ve gained enough experience in the current technology I&#39;m investing my time in.&lt;/p&gt;
&lt;p&gt;But it can feel too much at times. You might think that being a software developer is too formidable and you don&#39;t know where to start. As I say to the less-experienced developers on my team:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“Be confident. You&#39;ve got this!”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You are eventually going to be the biggest asset to your team and company. It&#39;ll just take a bit of time.&lt;/p&gt;
&lt;p&gt;So how do you help to keep up that confidence?&lt;/p&gt;
&lt;h2 id=&quot;ask-all-the-questions&quot;&gt;Ask all the questions&lt;/h2&gt;&lt;p&gt;We have a phrase in my team: &amp;quot;there&#39;s no such thing as a silly question&amp;quot;. I love it when the team ask questions. It&#39;s a chance for more-experienced team members to step up and share what they know. Or for another less-experienced person to share what they&#39;ve just learnt in that area. Asking questions is the key to learning. You&#39;re not expected to know everything so ask away!&lt;/p&gt;
&lt;p&gt;Hopefully you&#39;re working in a team with a positive culture where senior engineers or managers don&#39;t disparage others for not knowing things. If you&#39;re a team lead, it should be your job to enable a culture of openness. If a less-experienced engineer is under confident, and perhaps isn&#39;t understanding something, why not try to open the discussion for them by asking someone to explain the concept. Be their advocate. Not everyone is comfortable in asking for help.&lt;/p&gt;
&lt;h2 id=&quot;understand-the-teams-applications&quot;&gt;Understand the team&#39;s applications&lt;/h2&gt;&lt;p&gt;Every team will build their applications in a different way. There will be technology decisions which have been made many years ago. There will be specific tools that the team use. Their will be team understanding around code design patterns. The folder structures and testing methods will have been chosen for a variety of reasons. It&#39;s time to dig in and start to understand why they&#39;ve been built in this way.&lt;/p&gt;
&lt;p&gt;Try and get a high level overview of the applications that the team owns, what they do, and how they work. You won&#39;t be expected to know everything, especially in a large codebase, but having a basic understand will really help to build your confidence. Once you&#39;ve learnt a little bit about the systems, why not share it with others?&lt;/p&gt;
&lt;h2 id=&quot;communicate-with-your-team-and-find-their-superpowers&quot;&gt;Communicate with your team and find their superpowers&lt;/h2&gt;&lt;p&gt;You should get to know your teammates. Each of them will be different and have expertise in various technical topics. There will often be someone on the team who ends up being the most knowledgeable about specific applications or technologies. Another might be the expert in your tooling and continuous integration servers. If you&#39;re feeling stuck, then work out who has the superpower in that area and chat to them.&lt;/p&gt;
&lt;p&gt;In my experience, the best developers are born out of great communicators. Building a great working relationship with the right person, and asking for their help at the right time, can really help you to grow as a developer. Pay attention to what they&#39;re helping you with and how they&#39;re explaining things. Reiterate the information back to them to clarify your understanding. They are there to help you. A good senior engineer should be a good mentor to those with less experience.&lt;/p&gt;
&lt;h2 id=&quot;document-all-the-things&quot;&gt;Document all the things&lt;/h2&gt;&lt;p&gt;Documentation is the thing that most developers are the worst at. It should be part of the product that we&#39;re developing but we often leave it as an afterthought. When you&#39;re learning from others, always make your own documentation. Some of the less-experienced developers on my team keep a developer diary of what they&#39;ve learnt each day. They then use that to confirm their understanding at the end of the week. Suggest to the team that you document what you&#39;re learning for everyone. Update the project&#39;s README file with what you&#39;ve learnt and raise a pull request. Doing so will really help everyone but especially you.&lt;/p&gt;
&lt;p&gt;Your notes can be a list of the concepts you&#39;ve learnt during the day. It could be those pesky &lt;code&gt;git&lt;/code&gt; commands that you need to learn. It could be links to online resources that you&#39;ve found helpful. I&#39;ve always liked to keep my developer resources in a GitHub repository which I can add to. By using version control, you can keep a history of what you&#39;ve added and why. It also helps you to learn how a tool like &lt;code&gt;git&lt;/code&gt; and GitHub works. If you don&#39;t want to use version control, then you can keep your notes in your favourite note-taking application. I love &lt;a href=&quot;https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/&quot;&gt;Obsidian&lt;/a&gt; but it could be Evernote or another application.&lt;/p&gt;
&lt;h2 id=&quot;set-solid-learning-objective&quot;&gt;Set solid learning objective&lt;/h2&gt;&lt;p&gt;One of my team used to be a primary school teacher and is one of the fantastic developers on my team. She&#39;s taught me that having a good learning objective is the key to learning a topic. Understand what you want to learn and why. This could be learning how to create a unit test for your code. Or how to store some state in a database.&lt;/p&gt;
&lt;p&gt;Decide on what you want to learn and focus on that for a period of time. Don&#39;t jump from one language to another or from one new library to the next. This is very easy to do and won&#39;t help to build your confidence in your ability. You don&#39;t have to become an expert in everything overnight so having a single, focused learning objective will really help.&lt;/p&gt;
&lt;h2 id=&quot;help-others-once-you-know&quot;&gt;Help others once you know&lt;/h2&gt;&lt;p&gt;Once you&#39;ve learnt a technological topic, teach it to someone else. Being one step ahead of someone else, and teaching them what you know, will really help to solidify your learning. I always like to build a culture of &lt;a href=&quot;https://www.marclittlemore.com/lift-your-team-up/&quot;&gt;lifting the team up&lt;/a&gt; and this should apply to everyone and not just the team lead.&lt;/p&gt;
&lt;p&gt;Help to lift your colleagues up by running your own learning sessions for the team.&lt;/p&gt;
&lt;h2 id=&quot;be-excellent-to-yourself-and-each-other&quot;&gt;Be excellent to yourself and each other&lt;/h2&gt;&lt;p&gt;Finally, please remember that you deserve to be a software engineer. You might be early in your career, and feel like there&#39;s too much to learn. Just remember that you&#39;ll definitely get there but it takes time. You probably don&#39;t remember how much you have already learnt. Look back at your notes and rediscover what you learnt last month, 3 months ago, or even a year ago.&lt;/p&gt;
&lt;p&gt;Celebrate your wins, however small they are! You&#39;ll have come much further than you think. 🎉&lt;/p&gt;
&lt;p&gt;Don&#39;t forget to be excellent to yourself. You&#39;re doing great!&lt;/p&gt;
&lt;p&gt;And in the words of Bill and Ted, &amp;quot;be excellent to each other!&amp;quot;&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Never get too comfortable</title>
    
    <link href="https://www.marclittlemore.com/never-get-too-comfortable/"/>
    <published>2021-03-07T00:00:00Z</published>
    
    <updated>2021-03-07T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/never-get-too-comfortable/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Being comfortable is a good thing, especially when you start a new job.&lt;/p&gt;
&lt;p&gt;You&#39;ve got to know your colleagues, you&#39;ve found your way around the office or if you&#39;re working remote, the company intranet. You can quickly find documentation on Confluence and know how to find Jira tickets, and you&#39;ve finally found your stride with your work. Every day you make a significant impact in your role.&lt;/p&gt;
&lt;p&gt;After a while you can easily drift towards the familiar daily tasks. The exciting can quickly become mundane. Maybe a restructure has left you drifting away from what you loved about your role. Perhaps there are few opportunities to grow within your team or department. Often you end up feeling like you&#39;re working hard each day but without making any impact by the end of the week. You feel like you&#39;ve stagnated.&lt;/p&gt;
&lt;p&gt;Sometimes it&#39;s easy to do nothing as you&#39;ve become too comfortable.&lt;/p&gt;
&lt;h2 id=&quot;feeling-stuck&quot;&gt;Feeling stuck&lt;/h2&gt;&lt;p&gt;If you&#39;ve been doing the same role for the past few years, it&#39;s easy to feel like you&#39;re stuck. You might have reached your career goals, but you&#39;ve become comfortable with the status quo and don&#39;t feel that you can reach outside of your comfort zone.&lt;/p&gt;
&lt;p&gt;Feeling stuck like this can often lead to a sense of dissatisfaction. Everything that you do can seem too easy and you&#39;re no longer learning anything new. You can do one of two things if this happens. You can sit back and stay comfortable, or you can challenge yourself by expanding your role or asking for more.&lt;/p&gt;
&lt;p&gt;Try to kick-start a change in what you do. Reach out to people outside of your immediate network and see if you can help other teams. Embrace a change in your day-to-day work to find a new challenge.&lt;/p&gt;
&lt;h2 id=&quot;find-opportunties-for-growth&quot;&gt;Find opportunties for growth&lt;/h2&gt;&lt;p&gt;Doing the same thing every week means that you&#39;re missing out on your personal and professional growth. Doing the same work repeatedly means you&#39;ll potentially miss other opportunities to learn something new.&lt;/p&gt;
&lt;p&gt;If you&#39;re feeling unchallenged in your work, it could be time to look at making changes to your routine and finding other areas in which you can grow. If you&#39;re a software developer who mainly writes code each day, now could be the time to look at mentoring less experienced developers. At my current job, I looked to grow my career and decided to &lt;a href=&quot;https://www.marclittlemore.com/gain-confidence-at-public-speaking/&quot;&gt;gain confidence in public speaking&lt;/a&gt;. I knew this was one of the keys to unlocking a wider network of people I could help and who could potentially help me. This definitely helped to challenge myself and pushed me further in my career. It felt uncomfortable at the time, but that was a good thing.&lt;/p&gt;
&lt;h2 id=&quot;work-out-your-goals&quot;&gt;Work out your goals&lt;/h2&gt;&lt;p&gt;If you&#39;re not feeling challenged each day, you need to determine what would challenge you. Is it a need to take more responsibility? Do you want to learn a different skill or aspect to your current role? Only you can define what will make you feel uncomfortably challenged.&lt;/p&gt;
&lt;p&gt;Reach out to your peers or manager and ask for more responsibility. If you&#39;re a developer then maybe you could look at learning a new framework or programming language. If you&#39;re a team lead or engineering manager then look for opportunities to be a &lt;a href=&quot;https://www.marclittlemore.com/be-a-force-multiplier/&quot;&gt;force multiplier&lt;/a&gt; for your team or external teams.&lt;/p&gt;
&lt;h2 id=&quot;time-to-move-on&quot;&gt;Time to move on&lt;/h2&gt;&lt;p&gt;Ultimately, if you&#39;re feeling too comfortable in your current role, and you can&#39;t find the challenges within your company, it may be time to look elsewhere.&lt;/p&gt;
&lt;p&gt;It&#39;s a difficult decision to look for a new role but sometimes a fresh start can help to recharge your batteries. A new role is always a new challenge and it can often help to redefine your role and responsibilities. Making impactful change in a new role is always a great way to learn more and to grow your career.&lt;/p&gt;
&lt;h2 id=&quot;challenge-yourself&quot;&gt;Challenge yourself&lt;/h2&gt;&lt;p&gt;So stop and think about where you are now. Are you too comfortable in what you&#39;re doing? Is it time to make a change?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.marclittlemore.com/dont-repeat-your-years/&quot;&gt;Don&#39;t repeat your years&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Don&#39;t play your job on easy mode.&lt;/p&gt;
&lt;p&gt;Find chances to change what you do and help yourself to grow.&lt;/p&gt;
&lt;p&gt;Avoid becoming too comfortable.&lt;/p&gt;
&lt;p&gt;Now might be the time to make the change that you need.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Be a force multiplier</title>
    
    <link href="https://www.marclittlemore.com/be-a-force-multiplier/"/>
    <published>2021-03-02T00:00:00Z</published>
    
    <updated>2021-03-02T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/be-a-force-multiplier/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;In software development, there&#39;s always been the idea of the &lt;strong&gt;10x or rockstar developer&lt;/strong&gt;. A programmer that&#39;s so talented, that they can write software in double quick time, with their eyes closed, and also in their sleep. I&#39;ve worked with developers like this. With a few exceptions, I&#39;d never want to work with them again.&lt;/p&gt;
&lt;p&gt;In my experience, the 10x developer is a maverick who likes to solve issues alone and not share how. Who doesn&#39;t document their complex algorithms. Who breaks code and often doesn&#39;t take responsibility for fixing up other related interfaces. Who likes the challenge of initially solving the hard problems, but doesn&#39;t want to write production ready code. Who wants to take the glory and isn&#39;t concerned with the rest of the team.&lt;/p&gt;
&lt;p&gt;I don&#39;t want to work with 10x developers.&lt;/p&gt;
&lt;p&gt;I much prefer &lt;strong&gt;force multipliers&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;w-full h-0 pb-72 relative&quot;&gt;
    &lt;iframe src=&quot;https://giphy.com/embed/26wBbcfILlypQ5a7K&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen=&quot;&quot;&gt;
    &lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;force-multiplier&quot;&gt;Force multiplier&lt;/h2&gt;&lt;p&gt;Much like the Jedi Knights, you can use the force for good and improve life for you and your team. You can become a force multiplier in multiple areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technical:&lt;/strong&gt; Use your software engineering skills beyond your own role. Look at mentoring and coaching your team and share your knowledge with them. Or maybe look outside your own team and become an advisor for other teams and projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cultural:&lt;/strong&gt; Focus on &lt;a href=&quot;https://www.marclittlemore.com/lift-your-team-up/&quot;&gt;lifting your team up&lt;/a&gt; and set out to improve the team culture. Make your team the best place to be such that others want to join you too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Process:&lt;/strong&gt; Use your skills to focus on improving the team or department&#39;s processes. Can you improve the continuous integration or deployment servers? Or improve inter-team communication?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these are force multipliers. Working in these areas will help to set an example of the change you want to see. You can work alone or also work with others to achieve the changes. Using force multipliers will help to lift the whole team up.&lt;/p&gt;
&lt;h2 id=&quot;technical-force-multipliers&quot;&gt;Technical force multipliers&lt;/h2&gt;&lt;p&gt;If you&#39;re like me, you might be a manager of a software engineering team and have a technical background. You can use your this knowledge with any less experienced members of your team with technical mentoring. I run regular &amp;quot;team learning&amp;quot; sessions with my team where we look at programming concepts and patterns, and software development ideas like test-driven development (TDD).&lt;/p&gt;
&lt;p&gt;Carving out the time each week to help your team means you&#39;ll have less time for any hands-on coding yourself, but you&#39;re helping to increase the knowledge of others. Spend some time pair programming with your team and be available as a technical advisor when people need it. Get involved in code reviews and share different solutions if you think they might help.&lt;/p&gt;
&lt;p&gt;Being a technical force multiplier doesn&#39;t just have to be within your team. If you have the time, you can offer technical help to other teams. Share your experience of what has worked for your team and what hasn&#39;t. &lt;a href=&quot;https://www.marclittlemore.com/share-your-network/&quot;&gt;Share your network&lt;/a&gt; and introduce your engineers to engineers on other teams. Helping others that are missing domain knowledge that your team has will help both of your teams to thrive.&lt;/p&gt;
&lt;p&gt;By being a technical force multiplier, you are encouraging others to become technical mentors too.&lt;/p&gt;
&lt;h2 id=&quot;cultural-force-multipliers&quot;&gt;Cultural force multipliers&lt;/h2&gt;&lt;p&gt;Culture can be difficult to define. It&#39;s a combination of the values, beliefs, attitudes, and behaviours shared by the team as a whole. It&#39;s how people work well together and how they treat each other.&lt;/p&gt;
&lt;p&gt;How can you improve the team culture? Think about what a culture of diversity, positivity, and inclusivity would do to team morale. Encourage constant feedback from yourself as the team manager, and peer feedback from others. Be honest, open, and transparent in all that you do.&lt;/p&gt;
&lt;p&gt;In my own team I&#39;ve tried to build a culture where every voice is heard. I&#39;ve encouraged people to share their concerns and for everyone to be honest about what they know and don&#39;t. Asking questions is encouraged. We all know that there&#39;s no such thing as a stupid questions. It&#39;s just something somebody hasn&#39;t yet learnt.&lt;/p&gt;
&lt;p&gt;Don&#39;t forget to encourage taking time out from the day-to-day of technical work and build a culture of sharing parts of your lives that you&#39;re comfortable with others knowing. Informal chats and &amp;quot;water cooler&amp;quot; breaks are important, especially as more of us are now remote. Digital tea breaks where people can chat about life can help the team to bond. Don&#39;t wait for company mandated activities. Build them yourself with your team.&lt;/p&gt;
&lt;p&gt;By being a cultural force multipler, you make your team, and hopefully the company, a fantastic place to work.&lt;/p&gt;
&lt;h2 id=&quot;process-force-multipliers&quot;&gt;Process force multipliers&lt;/h2&gt;&lt;p&gt;What slows your team down? Are code reviews taking too long? Does deploying to production take an eternity? Is your documentation left undocumented or hard to find? Look at how you can work with the team to enable making these things easier.&lt;/p&gt;
&lt;p&gt;These problems are often best looked at from the bottom up rather than being mandated from the management team but you can help to kickstart the initiative. Look to make your day-to-day processes slightly easier each day. Once you get the ball rolling, you can chip away at the bigger problems.&lt;/p&gt;
&lt;p&gt;By being a process force multiplier you can make the team more efficient.&lt;/p&gt;
&lt;h2 id=&quot;use-the-force&quot;&gt;Use the force&lt;/h2&gt;&lt;p&gt;As your journey develops into more senior roles, your job might become less technical and more managerial. Use your force multipliers and expand your impact both within your team and in the wider department or company. It&#39;ll help you to grow and help all of your team on the way.&lt;/p&gt;
&lt;p&gt;May the force multiplier be with you...always.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Become a producer not a consumer</title>
    
    <link href="https://www.marclittlemore.com/become-a-producer-not-a-consumer/"/>
    <published>2021-02-28T00:00:00Z</published>
    
    <updated>2021-02-28T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/become-a-producer-not-a-consumer/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;When I was young, I used to love watching TV when I got in from school. I was a child of the 70s and 80s here in the UK so I only had about 1.5 to 2 hours of children&#39;s TV until the soap operas and news started. I&#39;d eat my dinner and then jump on to my VIC-20 or C64. They had no internet connection. We had no games without spending 30 minutes waiting for a game to load from a cassette tape. Instead, I&#39;d write my own programs. I was a content producer.&lt;/p&gt;
&lt;p&gt;My kids love YouTube and could spend hours on it watching videos of their favourite gamers. I love YouTube too. It&#39;s easy to watch one video and then get sucked down a rabbit hole of related videos. Before you know it you&#39;ve spent 4 hours watching videos on food gadgets for your kitchen, how to light Zoom videos, or how to take better notes. Or is that just my feed?&lt;/p&gt;
&lt;p&gt;I guesstimate that 90%+ of the people who consume content stay in that phase.&lt;/p&gt;
&lt;p&gt;And that&#39;s fine.&lt;/p&gt;
&lt;p&gt;But by starting to produce content you&#39;ll start to find what you enjoy. You&#39;ll discover what you&#39;re good at and want you&#39;re not. It can set a direction for your hobbies or maybe your work life. Repeat this regularly and you&#39;ll become a content producer and a creator.&lt;/p&gt;
&lt;h2 id=&quot;getting-started-with-content-production&quot;&gt;Getting started with content production&lt;/h2&gt;&lt;p&gt;Set the bar low to begin with. I&#39;m almost at the end of a &lt;a href=&quot;https://www.marclittlemore.com/creating-a-writing-habit/&quot;&gt;30-day writing habit&lt;/a&gt;. I&#39;ve sometimes struggled with attempting to write too much in each blog post, but if you aim for 200 to 500 words then it shouldn&#39;t take a long. It could be creating a quick drawing. Or a 5-minute piece of audio recorded on your phone. Just get started.&lt;/p&gt;
&lt;p&gt;Document what you&#39;ve made and share it with others. Post a link, a picture or a short video on Twitter, Instagram, TikTok or YouTube. It&#39;s a small amount of extra effort to document your work, but sharing with others will get them excited about what you do. Having a network, and then &lt;a href=&quot;https://www.marclittlemore.com/share-your-network/&quot;&gt;sharing your network with others&lt;/a&gt;, will help you to grow.&lt;/p&gt;
&lt;p&gt;Schedule some time to create something. I&#39;ve found that setting a specific time each evening has really helped me to write more. I&#39;ve not always achieved it every night but I&#39;ve definitely written much more in the past 30 days than I have in the previous 5 years. Taking control of your schedule will change you into a producer of interesting content and not just a passive consumer.&lt;/p&gt;
&lt;p&gt;Turn off notifications and block your applications. I&#39;ve found this hard at times. While writing this I had WhatsApp enabled on my laptop. A friend messaged me about a Twitch DJ stream I wanted to hear. It delayed me from starting to write this post for over an hour as we exchanged messages about it. I should have turned off all none essential applications and blocked any websites except this text editor I&#39;m using to write this post. Create a quiet space away from the noise of the internet and start to create.&lt;/p&gt;
&lt;p&gt;Build a streak and form a habit. &lt;a href=&quot;https://jamesclear.com/stop-procrastinating-seinfeld-strategy&quot;&gt;Jerry Seinfeld wrote every day&lt;/a&gt; and created a habit of writing. He wasn&#39;t looking for results immediately. He was looking for consistency which helped to hone his craft and over time, it would bring the results. That&#39;s what you need to do to become a creator. Just get started.&lt;/p&gt;
&lt;p&gt;And finally, look to repurpose your content in multiple ways. A video can be cut up for Instagram or TikTok. A blog post can be linked to on Twitter or cut up and shared as a Twitter tweet thread. Use quotes from your blog post to answer questions on Quora. There are so many ways to share your content and increase your reach.&lt;/p&gt;
&lt;p&gt;So do you want to just sit there and watch another YouTube video? Or do you want to make something?&lt;/p&gt;
&lt;p&gt;Get creating!&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Asking for feedback as a leader</title>
    
    <link href="https://www.marclittlemore.com/asking-for-feedback-as-a-leader/"/>
    <published>2021-02-24T00:00:00Z</published>
    
    <updated>2021-02-24T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/asking-for-feedback-as-a-leader/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I had an interesting catch up with a wonderful colleague, &lt;a href=&quot;https://twitter.com/blanquish&quot;&gt;Blanca Garcia Gil&lt;/a&gt;, today. We talked about the role of a software engineering team lead and also about giving and receiving feedback. After giving me a compliment which I didn&#39;t expect, she noticed that I was taken aback by it. The thing that came to light from our conversation was that &lt;strong&gt;&lt;em&gt;managers don&#39;t receive as much feedback as their team do&lt;/em&gt;&lt;/strong&gt; and they probably should.&lt;/p&gt;
&lt;p&gt;As a leader, your job is to guide your team and part of that is to offer constructive feedback on a regular basis. Feedback is a process that needs regular attention. If it needs to be given then it should be around that time that any situation has happened for both good and not-so-good feedback. It&#39;s not a process that should wait for a yearly review otherwise it comes as a shock. Frequent, informal feedback should feel normal and shouldn&#39;t be difficult to deliver, nor unexpected for your team.&lt;/p&gt;
&lt;p&gt;I realised that I need to improve at asking for feedback so it made me wonder what I should do. Asking for feedback demonstrates a &lt;a href=&quot;https://www.mindsetworks.com/science/&quot;&gt;growth mindset&lt;/a&gt; and a need for continuous improvement. I know that I should be taking the initiative to get better at asking for feedback as well as giving it.&lt;/p&gt;
&lt;h2 id=&quot;who-should-i-be-asking-for-feedback-from&quot;&gt;Who should I be asking for feedback from?&lt;/h2&gt;&lt;h3 id=&quot;my-manager&quot;&gt;My manager&lt;/h3&gt;&lt;p&gt;First and foremost, I need to be checking in with my own manager in our weekly 1:1s and asking for feedback on my own work. Sometimes 1:1s can head into a discussion of the day-to-day work of the team rather than my own development. I need to ensure that we talk about my own development and how I can improve on a regular basis.&lt;/p&gt;
&lt;h3 id=&quot;my-team&quot;&gt;My team&lt;/h3&gt;&lt;p&gt;Asking for feedback from my team in their 1:1s is something I try to do but sometimes without realising it. If you&#39;ve built a relationship of trust with your team, this should be a natural conversation. As our team transitioned into a new way of working in early 2020, I often asked what I could do better to support my team. If you&#39;ve built a healthy team cullture then you can get some good feedback in this way and not just from the most senior or confident people in your team.&lt;/p&gt;
&lt;h3 id=&quot;my-peers&quot;&gt;My peers&lt;/h3&gt;&lt;p&gt;I&#39;m currently a Software Engineering Team Lead (but this will change soon!) and I need to engage with other team leads to gain more peer feedback. We&#39;ll all be going through similar challenges so to compare how we address them is helpful to build a community around the technical leadership team. It&#39;ll help me to understand how I can improve and any changes I need to make.&lt;/p&gt;
&lt;h2 id=&quot;asking-for-feedback&quot;&gt;Asking for feedback&lt;/h2&gt;&lt;p&gt;Asking for feedback is key to improving as a person and leader but how should you do it? You should prepare both yourself and your colleague or manager to ask for it to ensure you&#39;re both comfortable.&lt;/p&gt;
&lt;h3 id=&quot;find-an-appropriate-time&quot;&gt;Find an appropriate time&lt;/h3&gt;&lt;p&gt;While it&#39;s easy to approach your manager and ask them for feedback, it&#39;s probably better to set aside some time when you can both discuss things in more detail. This gives both of you some time to prepare for the conversation. It can be a good idea to forewarn them of your expectations.&lt;/p&gt;
&lt;h3 id=&quot;prepare-questions-to-ask&quot;&gt;Prepare questions to ask&lt;/h3&gt;&lt;p&gt;Asking &amp;quot;can you give me some feedback&amp;quot; without context is too general and a hard question to answer. Instead, preparing some questions in advance, and potentially sharing them with your manager, could be helpful.&lt;/p&gt;
&lt;p&gt;Perhaps you want to ask about specific strengths and weaknesses or other areas you&#39;d like to concentrate on. Some examples could be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can I get better at supporting the mission of our team?&lt;/li&gt;
&lt;li&gt;What advice would you give me to help me improve my communication?&lt;/li&gt;
&lt;li&gt;How can I prepare for our upcoming project X?&lt;/li&gt;
&lt;li&gt;Which parts of my presentation could have been better?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Asking for specific advice makes it easier for you to understand the areas you need to concentrate on in order to grow.&lt;/p&gt;
&lt;h3 id=&quot;take-notes&quot;&gt;Take notes&lt;/h3&gt;&lt;p&gt;Anyone that knows me, knows that I take a lot of notes. I&#39;m currently a big fan of &lt;a href=&quot;https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/&quot;&gt;Obsidian&lt;/a&gt;, which allows me to link concepts in my notes together really easily, but a pen and notebook will do just as well. Ensure you capture any ideas from your manager or peers as to how you can improve.&lt;/p&gt;
&lt;h3 id=&quot;learn-from-the-feedback&quot;&gt;Learn from the feedback&lt;/h3&gt;&lt;p&gt;There&#39;s little point in asking for feedback if you don&#39;t then use it. Take some time to reflect on what&#39;s been said, and the notes you&#39;ve taken, especially if there is room for improvement. Consider how you could apply any suggestions to how you work and make a development plan for yourself. Perhaps creating a list of goals would be useful here.&lt;/p&gt;
&lt;h3 id=&quot;say-thank-you&quot;&gt;Say thank you&lt;/h3&gt;&lt;p&gt;Finally, don&#39;t forget to thank people for taking the time to give you feedback. It&#39;s not easy to give feedback and we should all be doing it more regularly than we probably do. Make sure you thank people for their time and thoughts and use it to help your personal development and growth.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>How do you measure success?</title>
    
    <link href="https://www.marclittlemore.com/how-do-you-measure-success/"/>
    <published>2021-02-18T00:00:00Z</published>
    
    <updated>2021-02-18T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/how-do-you-measure-success/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;The dictionary defines success as:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“The accomplishment of an aim or purpose”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Everyone wants to feel successful. But what does it mean to you? What is the aim or purpose of your life?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Peter_Drucker&quot;&gt;Peter Drucker&lt;/a&gt;, one of the great theorists on management techniques, has been attributed with the saying:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“You can&#39;t manage what you can&#39;t measure”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;But how do you measure success?&lt;/p&gt;
&lt;h2 id=&quot;create-your-own-definition-of-success&quot;&gt;Create your own definition of success&lt;/h2&gt;&lt;p&gt;I&#39;ve got a friend who often compares themselves to others. How their own life is not as good as mine or one of our mutual friends. They tell me how successful I&#39;ve been and how they&#39;ve not. I live my own life optimistically. I&#39;ve always felt that this was the wrong way to consider success by looking at what others do. Success should be what&#39;s truly important to you and to nobody else.&lt;/p&gt;
&lt;p&gt;Everyone&#39;s definition of success is different.&lt;/p&gt;
&lt;p&gt;Your definition of success will be different to mine. Many people look successful from the outside but only they can define what it means. They may not feel as successful as you think they are.&lt;/p&gt;
&lt;h2 id=&quot;success-changes-over-time&quot;&gt;Success changes over time&lt;/h2&gt;&lt;p&gt;When I was in my late teens, I started DJing as my &lt;a href=&quot;https://djcruze.co.uk/&quot;&gt;DJ Cruze&lt;/a&gt; alter ego. I came to Manchester for university in 1990 and slowly started DJing at the student nights. I met my good friend Mark and sent him DJ mixtapes over to Germany when he lived out there. Before I knew it, I was DJing every other weekend in Germany and in Manchester and I became a pretty successful DJ. It felt great to spin new house music tracks to adoring clubbers every weekend. This continued for almost 18 years but towards the end, it felt less and less like success. Sometimes it felt like going through the motions and playing the music because I felt I had to rather than I wanted to.&lt;/p&gt;
&lt;p&gt;It&#39;s never easy to admit, but sometimes your idea of what success looks like changes. We should embrace this but we often don&#39;t.&lt;/p&gt;
&lt;p&gt;I got married in 2006 and we had our children in 2007 and 2009. These key moments in my life changed my measure of success. It was no longer about partying every weekend. Having a happy marriage and a healthy family meant much more to me than anything else.&lt;/p&gt;
&lt;h2 id=&quot;how-i-measure-success-now&quot;&gt;How I measure success now&lt;/h2&gt;&lt;p&gt;I was incredibly ill in 2014 and &lt;a href=&quot;https://www.marclittlemore.com/how-i-almost-died/&quot;&gt;almost died&lt;/a&gt;. I was training for a half marathon at the time and one of the consultants commented that having exercised as much as I did, and therefore having a healthy heart and lungs, was one of the key factors in keeping me alive. Being healthy is one of the key success criteria for me in the past few years and will continue to be so in 2021. I don&#39;t think I&#39;m necessarily as healthy as I could be, but I&#39;m definitely walking and cycling much more than I ever have. I want to ensure that I keep active and eat well and I encourage my family to do the same. Being out in the fresh air and enjoying life after near-death feels like a big success for me.&lt;/p&gt;
&lt;p&gt;I&#39;m lucky that I found a career that I enjoy. I got my first home computer, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Commodore_VIC-20&quot;&gt;Commodore VIC-20&lt;/a&gt;, when I was about 11 or 12 and I&#39;ve continued to love computers and software development since those early years. Getting to work with people who also love writing software and to be able to help them to develop their own careers is another key success measurement for me. It&#39;s helped me to earn a good salary for what I do, and it&#39;s enough to keep my family and I well cared for.&lt;/p&gt;
&lt;p&gt;In my earlier career, I worked in the video games industry. While I loved games, and still do, the industry suffered from a tendency to push the developers to work beyond the normal working week. The so-called &lt;a href=&quot;https://en.wikipedia.org/wiki/Video_game_developer#%22Crunch_time%22&quot;&gt;&amp;quot;crunch time&amp;quot;&lt;/a&gt; was one of the main reasons why I decided to move out of games and into the world of web-based applications. One of my other success indicators is to ensure I have a good work-life balance. There are other ways I could look to earn more money in the software industry but I&#39;d never want it to come at the expense of my family. A healthy work-life balance will always been one of my key success measures nowadays.&lt;/p&gt;
&lt;p&gt;While 2020 and 2021 have been years like no others, I see my own success as something I love to share with others. Sharing my money, time, experience, or help with other people is something I cherish. Spending time with friends and family, or helping others I perhaps don&#39;t know as well, is something I love. A successful life is a happy life and being able to share it with others really makes me happy too.&lt;/p&gt;
&lt;p&gt;Don&#39;t compare yourself to other people&#39;s definition of success. Only you can define your own values and what it means to be successful. Define your own succesful life and strive to make it happen.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Share your network</title>
    
    <link href="https://www.marclittlemore.com/share-your-network/"/>
    <published>2021-02-16T00:00:00Z</published>
    
    <updated>2021-02-16T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/share-your-network/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I went to the &lt;a href=&quot;https://theleaddeveloper.com/&quot;&gt;Lead Developer Conference&lt;/a&gt; back in 2019. &lt;a href=&quot;https://www.patkua.com/&quot;&gt;Pat Kau&lt;/a&gt; gave a fantastic talk called &amp;quot;Flavours of technical leadership&amp;quot;. In the talk, he shared different ways that engineering leaders develop and make a difference to their teams. One of his thoughts really resonated with me:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“Share your network”&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;different-types-of-networks&quot;&gt;Different types of networks&lt;/h2&gt;&lt;p&gt;Anyone who has been in an idustry for a while has a network of people that they know and trust. This builds up over time and expands the impact and opportunities you have.&lt;/p&gt;
&lt;p&gt;There are differing networks you can be a part of. The Harvard Business Review&#39;s &amp;quot;How leaders create and use networks&amp;quot;, Herminia Ibarra and Mark Lee Hunter suggest that there are three main types of networks:&lt;/p&gt;
&lt;h3 id=&quot;1-operation-network&quot;&gt;1. Operation network&lt;/h3&gt;&lt;p&gt;An operation network consists of people that help to fulfil your current responsiblities and day-to-day tasks. They may be people in your immediate team and direct managers or stakeholders.&lt;/p&gt;
&lt;h3 id=&quot;2-personal-network&quot;&gt;2. Personal network&lt;/h3&gt;&lt;p&gt;You can find people outside of your team, department, or company who can help you or you can help them. This is your personal network. They can be people with similar interests or thinking and you can leverage their help for your own success. Always remember that you should be helping them too.&lt;/p&gt;
&lt;h3 id=&quot;3-strategic-network&quot;&gt;3. Strategic network&lt;/h3&gt;&lt;p&gt;A strategic network are people within or external to your organisation which will help you to find new directions for your team. They may be external stakeholders that you can work with or find support from. They&#39;ll help to open up interesting opportunities for you and your team.&lt;/p&gt;
&lt;p&gt;These three networks will consist of different people who will be valuable to both you and others that you introduce them too. To help to build a high-performing team, you can make introductions to others in these networks and share your network.&lt;/p&gt;
&lt;h2 id=&quot;why-leaders-should-share-their-network&quot;&gt;Why leaders should share their network&lt;/h2&gt;&lt;p&gt;I wrote a little bit about sharing your network in my article on &lt;a href=&quot;https://www.marclittlemore.com/lift-your-team-up/&quot;&gt;lifting your team up&lt;/a&gt;. Most strong leaders use networking to develop relationships in their team and beyond. Talking to people is a good way to forge personal and professional relationships and enable you and your team to do your best work.&lt;/p&gt;
&lt;p&gt;By introducing your team to others, you&#39;re creating new opportunities. These can help your team to grow and learn from people with different experiences than your own. You&#39;ll find the potential for mentoring and coaching outside of your own knowledge and this will help your own team to thrive. Networking with others is important for your own career so by sharing the network you have it helps to grow the careers of others. You will understand your team&#39;s needs and can help them to connect with relevant people.&lt;/p&gt;
&lt;p&gt;An introduction to a senior leader or engineer is an endorsement or sponsorship from someone trusted. Hopefully you&#39;ve gained a reputation within the company as being someone reliable. By encouraging your team to talk to people outside of the department, you&#39;re subconciously telling others how much you trust and believe in your team member. This goes much further than you think in helping to grow their careers.&lt;/p&gt;
&lt;p&gt;What differentiates a leader from a manager, is the ability to understand who to talk to enlist the help that is needed. By sharing this with your team, you&#39;re unlocking their ability to become independent and grow their potential both with and without you.&lt;/p&gt;
&lt;p&gt;Take a look at your own network today and see who you can share it with. Empower your team to be amazing.&lt;/p&gt;
&lt;h2 id=&quot;watch-the-talk&quot;&gt;Watch the talk&lt;/h2&gt;&lt;p&gt;https://www.youtube.com/watch?v=YbGwoU2Ow4A&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hbr.org/2007/01/how-leaders-create-and-use-networks&quot;&gt;How leaders create and use networks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Remote working is the future</title>
    
    <link href="https://www.marclittlemore.com/remote-working-is-the-future/"/>
    <published>2021-02-13T00:00:00Z</published>
    
    <updated>2021-02-13T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/remote-working-is-the-future/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;People have talked about remote working in the technology industry for many years. Some companies, such as &lt;a href=&quot;https://basecamp.com/&quot;&gt;Basecamp&lt;/a&gt;, &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt;, and &lt;a href=&quot;https://about.gitlab.com/&quot;&gt;GitLab&lt;/a&gt; have embraced it for years whilst others have pushed back, expecting most teams to co-locate in office spaces. Some have had a mix of remote-friendly teams who may have some split of the two.&lt;/p&gt;
&lt;p&gt;And then 2020 and Coronavirus COVID-19 changed everything.&lt;/p&gt;
&lt;p&gt;Many countries introduced lockdowns to stop the movement of people and the spread of the disease. Companies had to quickly switch their working practices to allow people to work from away from their offices. For those that had already embraced remote-first working practices, the transition was easier. For those that had not started to look at remote working, I&#39;m sure it was much more difficult.&lt;/p&gt;
&lt;p&gt;After almost a  year of remote working, I hope that most technology companies will move towards remote-first thinking. I believe it&#39;s the future of work.&lt;/p&gt;
&lt;h2 id=&quot;it-wont-be-easy-but-itll-be-worth-it&quot;&gt;It won&#39;t be easy but it&#39;ll be worth it&lt;/h2&gt;&lt;p&gt;I became very ill in 2014 and &lt;a href=&quot;https://www.marclittlemore.com/how-i-almost-died/&quot;&gt;almost died&lt;/a&gt;. It took 5 months for me to return to work and thankfully the BBC were great in allowing me to work remotely to a team which was mostly based in London.&lt;/p&gt;
&lt;p&gt;However, being the only remote working to a co-located team wasn&#39;t easy.&lt;/p&gt;
&lt;p&gt;Being an individual on the end of a Skype or Zoom call was tough. A room full of people all talking to each other with a single laptop in front of one person often meant that I couldn&#39;t communicate effectively. Trying to be heard over a meeting room of people who can easily interject and take visual cues from each other was tough. It took some conversations with my team, and a long time, to help to drive a change towards &amp;quot;remote-first&amp;quot;. This is when you build your team&#39;s culture around the idea that everyone should be considered remote, even if co-located in the same office. I helped to drive this change in culture in our team and for the past 3 or 4 years, we&#39;ve definitely become remote-first.&lt;/p&gt;
&lt;h2 id=&quot;constantly-iterate&quot;&gt;Constantly iterate&lt;/h2&gt;&lt;p&gt;Remote working doesn&#39;t mean attempting to replicate what you do in the office but from home. It shouldn&#39;t mean a day of Zoom calls which mirror office meetings.&lt;/p&gt;
&lt;p&gt;Companies and teams need to get better at changing what remote working looks like. Companies like GitLab have been running 100% remotely and have built their practices around their teams and how they collaborate. Communication and the discoverability and visibility of that information can become more difficult in the remote world. Defining what information is required and where it should live will make your team&#39;s life easier.&lt;/p&gt;
&lt;p&gt;As technology teams, we have to become better at writing instead of communicating face-to-face. Starting to think about remote ways of working like &lt;a href=&quot;https://blog.trello.com/how-to-run-an-asynchronous-meeting&quot;&gt;asynchronous meetings&lt;/a&gt; will help us embrace remote-friendly ways of working. Being clear and concise in our written communication will allow us to work together but apart.&lt;/p&gt;
&lt;p&gt;We&#39;ll have to get better at building software remotely too.&lt;/p&gt;
&lt;p&gt;I&#39;ve always liked &lt;a href=&quot;https://en.wikipedia.org/wiki/Pair_programming&quot;&gt;pair programming&lt;/a&gt; as a process which helps to write better software. While it&#39;s a great way to share code, enable a mentoring relationship, and spot issues earlier, it can be more challenging in the remote environment. The expectations of having the same schedule and for pairing over Zoom for 5 hours idea can be tiring. We potentially need to look at new ways of working collaborating more effectively. If we look to the open source communities, who have been building great software in public but by distributed software engineers, we can think about new ways to build applications.&lt;/p&gt;
&lt;p&gt;Thinking about the way we work will be necessary to avoid bringing old office ways of working to the remote world. Being more communicative, organised, and intentional in what we do will be great for everyone.&lt;/p&gt;
&lt;h2 id=&quot;companies-will-adapt&quot;&gt;Companies will adapt&lt;/h2&gt;&lt;p&gt;There will have to be big changes to the way that companies build their teams.&lt;/p&gt;
&lt;p&gt;Having remote-first teams will mean we can start to hire people from anywhere. Having flexibility around how we work will allow us to look more deeply at diversity and inclusion in our teams. Being able to work asynchronously can help us to encourage people who wouldn&#39;t normally feel that technology is for them to join our teams. Talented people no longer have to be within commuting distance of an office space. We&#39;ll have to ensure that we can support new members of the team with better remote on-boarding, mentoring, and coaching. People like me, in engineering management, need to make this better.&lt;/p&gt;
&lt;p&gt;If we no longer need teams to be in offices, companies might consider cutting their commercial office spaces. These may become more like tech hubs where teams can come together and collaborate from time-to-time instead of all of the time. Some companies may decide to get rid of the office spaces all together. I hope that companies will look to spend some of the money they save by not needing office spaces on enabling great remote setups for their employees. For example, we all need good chairs if we&#39;re not in an office space. Or allowing people to work from shared spaces should they wish too and perhaps don&#39;t have the ability to work from home as easily.&lt;/p&gt;
&lt;p&gt;Companies need to adapt to the new normal of many people not wanting to return to the 9 am to 5 pm, 5-days per week in an office. They want to save their commuting time for time with their families, to exercise, or to just get up later!&lt;/p&gt;
&lt;p&gt;Remote working is the future for those companies that can embrace it. While there may be a mix of teams coming together in real life, and also working remotely, I can&#39;t see a future where companies will bring teams back into the office for 5-days a week. I think we&#39;re being naive if we believe that their teams want to do that even after the pandemic. Most of the larger technology companies are now publicly stating that they will be building remote-friendly teams. And this is a great step forward from the past few years.&lt;/p&gt;
&lt;p&gt;The future is remote-first working. The pendulum won&#39;t swing back now.&lt;/p&gt;
&lt;h2 id=&quot;references-and-further-reading&quot;&gt;References and further reading&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mondaynote.com/tech-will-return-to-work-but-habits-will-be-changed-forever-de30ce4a9190&quot;&gt;MondayNote&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://om.co/2020/05/03/the-inevitable-has-happened/&quot;&gt;Om&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.timcasasola.com/blog/writing&quot;&gt;Tim Casasola&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.salesforce.com/news/stories/creating-a-best-workplace-from-anywhere/&quot;&gt;Salesforce&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://newsroom.spotify.com/2021-02-12/distributed-first-is-the-future-of-work-at-spotify/&quot;&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://threadreaderapp.com/thread/1359135080753614854.html&quot;&gt;Chris Herd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://about.gitlab.com/handbook/&quot;&gt;GitLab Remote Handbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=26139863&quot;&gt;Microsoft - &amp;quot;It&#39;s pushed our culture to be a lot more communicative, intentional, and organized, which is good for everyone&amp;quot;
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Writing great pull requests</title>
    
    <link href="https://www.marclittlemore.com/writing-great-pull-requests/"/>
    <published>2021-02-10T00:00:00Z</published>
    
    <updated>2021-02-10T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/writing-great-pull-requests/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Teams want to write great code. They want to communicate and collaborate on their software. Sharing code via a &lt;a href=&quot;https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests&quot;&gt;pull request&lt;/a&gt; is a great way to present your code for review by your peers.&lt;/p&gt;
&lt;p&gt;A pull request is a way to create a new feature branch which you&#39;re asking to be merged into your main branch. It&#39;s also a way to document and share your thinking around the changes and allow others to review the code and suggest changes.&lt;/p&gt;
&lt;p&gt;Here are some ideas as to what you can do to help to write a great pull request.&lt;/p&gt;
&lt;h2 id=&quot;why-does-it-matter&quot;&gt;Why does it matter?&lt;/h2&gt;&lt;p&gt;Keeping your codebase simple and easy to read is hard. Ensuring code consistency isn&#39;t easy. Reviewing code can be difficult and defects can easily occur without the whole team helping to validate any potential changes.&lt;/p&gt;
&lt;p&gt;Each change can be neatly captured in a pull request. Keeping these pull requests descriptive, easy to read, and consistent allows your team to use their mental energy on reviewing the code. It avoids having to jump through hoops to understand why the change is being made.&lt;/p&gt;
&lt;h2 id=&quot;make-small-pull-requests&quot;&gt;Make small pull requests&lt;/h2&gt;&lt;p&gt;It&#39;s difficult to review large pull requests. If you have too many changes then reviewers tend to skim read the code and often merge it without a thorough review. &lt;a href=&quot;https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/&quot;&gt;A study by SmartBear&lt;/a&gt; showed that developers should review no more than 200 to 400 lines of code at a time. The brain can only process so much information and beyond 400 lines of code, it was shown that the ability to spot issues was greatly reduced.&lt;/p&gt;
&lt;p&gt;Look to make your smaller pull requests and then you can raise them more often. Try to have a single context for each new request. Sometimes you think &amp;quot;I&#39;ll just fix this other thing while I&#39;m here&amp;quot;. It&#39;s a great idea to &lt;a href=&quot;https://www.marclittlemore.com/refactoring-code-broken-windows-theory/&quot;&gt;refactor your code&lt;/a&gt; while in the codebase, but maybe save it for a separate pull request to avoid losing the context around the current changes. Having atomic contextual changes makes it easy to review.&lt;/p&gt;
&lt;h2 id=&quot;a-useful-title&quot;&gt;A useful title&lt;/h2&gt;&lt;p&gt;We often go back through the code and look at closed pull requests. Writing descriptive titles allows you to see what happened for each set of merged changes.&lt;/p&gt;
&lt;p&gt;Add a summary of what changes have been applied for this pull request. This should be a short description of the changes at a high level. If you use a feature or defect tracking application such as Jira, you can use a pattern of adding a ticket number to the title as my team currently does. This helps to have a quick reference to a potentially more descriptive document with more context around the changes.&lt;/p&gt;
&lt;p&gt;You can also add a prefix to your title to define the type of pull request. For example, adding a &lt;strong&gt;[WIP]&lt;/strong&gt; (work in progress) would suggest that the pull request is unfinished. Or adding &lt;strong&gt;[RFC]&lt;/strong&gt; (request for comments) could suggest that it&#39;s not yet a mergeable pull request and that you&#39;d like people to comment on the suggested architectural pattern.&lt;/p&gt;
&lt;p&gt;GitHub also allows you to create a draft pull request which can be reviewed and commented upon but not merged into the main branch. This is a useful way to review an idea before embarking on the full implementation of the code.&lt;/p&gt;
&lt;h2 id=&quot;tell-them-what-changed&quot;&gt;Tell them what changed&lt;/h2&gt;&lt;p&gt;Next up is the description for the pull request.&lt;/p&gt;
&lt;p&gt;Firstly, we should tell the reviewers what has changed. Your job here is to make it easier for the reviewer to see what this pull request achieves.&lt;/p&gt;
&lt;p&gt;Write a high level description which explains the changes being made and what the expected results are. You can also add a list of the key changes that have been made so the reviewer can see them at a glance. Don&#39;t just make this the list of commits to the request. Those can be easily seen as the commits to this pull request. Instead, list the key changes which matter.&lt;/p&gt;
&lt;h2 id=&quot;tell-them-why-its-changed&quot;&gt;Tell them why its changed&lt;/h2&gt;&lt;p&gt;In addition to telling the reviewer what has changed, it&#39;s good to give a context as to why this change is happening. If it&#39;s related to other pull requests or architectural changes, add links to these documents so the reviewer can read them. Often the developer reading the code won&#39;t have been involved in the changes and may not have the full picture of why this change is necessary. Given them a solid context as to the reason for the pull request will definitely help with their understanding.&lt;/p&gt;
&lt;p&gt;Adding screenshots or videos to your pull request makes it much easier to review. For visual changes, the screenshots could be a before and after shot to show what changes the pull request provides. For more complicated changes, a short video is a fantastic way to share information about user flows and expectations.&lt;/p&gt;
&lt;h2 id=&quot;tags-and-labels&quot;&gt;Tags and labels&lt;/h2&gt;&lt;p&gt;If your pull request references previous changes, make sure to link them in your description. You can tag other team members if you think that they can provide additional context around the changes or you want them involved in the discussion. In GitHub you can also tag teams if it&#39;s relevant to a whole team and not just individuals.&lt;/p&gt;
&lt;p&gt;Use a pull requests labels to give the reviewer some additional information around the changes. This could be a &lt;code&gt;bug&lt;/code&gt; label for a quick fix or a &lt;code&gt;do not merge&lt;/code&gt; label if the code is not yet ready for prime time. You can add your own team&#39;s custom labels if relevant too.&lt;/p&gt;
&lt;h2 id=&quot;keep-consistent&quot;&gt;Keep consistent&lt;/h2&gt;&lt;p&gt;The best thing for a team to do is to keep a consistent pull request format. Context switching between pull requests with different information in them can be challenging and slow down the review process.&lt;/p&gt;
&lt;p&gt;GitHub provides the ability to add a standard &lt;a href=&quot;https://docs.github.com/en/github/building-a-strong-community/creating-a-pull-request-template-for-your-repository&quot;&gt;pull request Markdown template&lt;/a&gt; which gets added to the description when a pull request gets raised. This is a useful way to capture the information above into a consistent format. For GitHub, this involves creating a file named &lt;code&gt;pull_request_template.md&lt;/code&gt; in the &lt;code&gt;.github&lt;/code&gt; directory of your project.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;&lt;p&gt;These are just some of the ways you can help to write great pull requests. They are not mandatory rules but they&#39;ve definitely helped my team to write better pull requests over the years. As with all processes, they should adapt and change over time. Your teams needs might be different from mine. Take the best ideas and add your own.&lt;/p&gt;
&lt;p&gt;Let me know if you have any other fantastic ideas for writing great pull requests by &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;sending me a message&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/marclittlemore&quot;&gt;tweeting at me&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful resources&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@hugooodias/the-anatomy-of-a-perfect-pull-request-567382bb6067&quot;&gt;The anatomy of a perfect pull request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pullrequest.com/blog/writing-a-great-pull-request-description/&quot;&gt;Writing a great pull request descriptipn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.blog/2015-01-21-how-to-write-the-perfect-pull-request/&quot;&gt;How to write the perfect pull request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.atlassian.com/blog/git/written-unwritten-guide-pull-requests&quot;&gt;The (written) unwritten guide to pull requests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Don't repeat your years</title>
    
    <link href="https://www.marclittlemore.com/dont-repeat-your-years/"/>
    <published>2021-02-09T00:00:00Z</published>
    
    <updated>2021-02-09T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/dont-repeat-your-years/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I recently attended an &amp;quot;away day&amp;quot; for the leadership team of our department. In early 2021, an &amp;quot;away day&amp;quot; obviously means another set of Zoom meetings. It was a great day of digging into the challenges of the team and there were a couple of guest speakers with amazingly inspiring talks.&lt;/p&gt;
&lt;p&gt;One of the speakers talked about her experience in moving from being a managing director at a newspaper to becoming a business manager at the BBC. She had to work her way up again after having senior role. And then she said one thing that really resonated with me:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“Don&#39;t repeat your years.”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;She&#39;d stayed in a role for over a year because she didn&#39;t believe she was ready to take the next step up even though she had the experience. She taken the easier route because she didn&#39;t yet believe in herself and the skills she had. She did the same thing she&#39;d done the previous year for the next 12 months.&lt;/p&gt;
&lt;p&gt;She&#39;d repeated her year.&lt;/p&gt;
&lt;h2 id=&quot;complacency-kills-time&quot;&gt;Complacency kills time&lt;/h2&gt;&lt;p&gt;Time is scarce. Life is short. I know this as &lt;a href=&quot;https://www.marclittlemore.com/how-i-almost-died/&quot;&gt;I almost lost it&lt;/a&gt;. If you don&#39;t use your time wisely, it slips away like the sands in an hourglass. You never get that time back.&lt;/p&gt;
&lt;p&gt;As a software engineer, and now as a team lead and manager of software engineers, I enjoy a challenge. I enjoy helping to creating a new application. I love digging through a codebase to understand how it works. I can&#39;t wait for my 1:1s with my team and to help them on to achieve their career aspirations. I love making an impact in the code or by helping my team.&lt;/p&gt;
&lt;p&gt;But sometimes you go through the motions and find yourself no longer learning new things.&lt;/p&gt;
&lt;p&gt;If the work lacks challenges, or even worse, you do the same things that you&#39;ve done in the past month or two, it&#39;s easy to become complacent. Work shifts towards boredom and you lack the motivation to rise to new challenges. You can easily do the same thing without feeling a sense of purpose.&lt;/p&gt;
&lt;p&gt;It&#39;s easy to repeat your years.&lt;/p&gt;
&lt;h2 id=&quot;dont-waste-your-career&quot;&gt;Don&#39;t waste your career&lt;/h2&gt;&lt;p&gt;I read a fantastic article called &lt;a href=&quot;https://apoorvagovind.substack.com/p/how-to-waste-your-career-one-comfortable&quot;&gt;&amp;quot;How to waste your career, one comfortable year at a time&amp;quot;&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/appyg99&quot;&gt;Apoorva Govind&lt;/a&gt;. It&#39;s a great post so please take the time out to read it. Apoorva lays out a framework for deciding when it&#39;s time for a change. It&#39;s an incredibly useful way to determine if you&#39;re wasting your years. She suggests looking at the following every quarter and giving yourself a score in the following areas.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Accomplishment&lt;/strong&gt; - Have you achieved anything significant in the past three months?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Impact&lt;/strong&gt; - Could you write a new line in your resume about the things you&#39;ve worked on in the past three months? If you were hiring for a role, would you value this experience?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Growth or future alignment&lt;/strong&gt; - Have you acquired valuable insights or skills? Do these align with how you want to grow into your career?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Challenge&lt;/strong&gt; - Do you spend your days thinking of the challenges at work? Do you try and solve these problems in the shower? (I love it when you get to do this!)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community&lt;/strong&gt; - Are you happy to go to work every day? Even if this is on Zoom? Do you believe in the mission, vision, and leadership of the team or company?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you mark each of these five areas together as a percentage, and you&#39;re scoring less than 50%, you have to question if you&#39;re still feeling useful in your role. If you&#39;re not then it&#39;s time to change and avoid becoming stuck in your role.&lt;/p&gt;
&lt;h2 id=&quot;time-for-a-change&quot;&gt;Time for a change&lt;/h2&gt;&lt;p&gt;If you feel you&#39;re repeating your years, it doesn&#39;t necessarily mean a new job.&lt;/p&gt;
&lt;p&gt;It can be moving teams.&lt;/p&gt;
&lt;p&gt;It could be working on a project you&#39;re less familiar with.&lt;/p&gt;
&lt;p&gt;If you have the option to do this then ask to change and see if this changes your framework scores.&lt;/p&gt;
&lt;p&gt;If not, it might be time to think about looking for a new role elsewhere.&lt;/p&gt;
&lt;h2 id=&quot;dont-live-the-life-of-others&quot;&gt;Don&#39;t live the life of others&lt;/h2&gt;&lt;p&gt;You have to shape your own life. It&#39;s easy to plod along and feel a lack of achievement. It&#39;s especially easy to do this in a larger organisation. But it&#39;s up to you. You have to shape your career yourself, and maybe with the help of a supportive team lead like myself.&lt;/p&gt;
&lt;p&gt;I read &lt;a href=&quot;https://www.theguardian.com/lifeandstyle/2012/feb/01/top-five-regrets-of-the-dying&quot;&gt;an article from a palliative nurse&lt;/a&gt; who recorded the most common regrets of people on their deathbeds. This one stood out to me.&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“I wish I&#39;d had the courage to live a life true to myself, not the life others expected of me.”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Don&#39;t spend your own life waiting for life to happen to you.&lt;/p&gt;
&lt;p&gt;Be proactive in doing what you want to do.&lt;/p&gt;
&lt;p&gt;Don&#39;t repeat your years.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Create an Eleventy podcast feed</title>
    
    <link href="https://www.marclittlemore.com/create-an-eleventy-podcast-feed/"/>
    <published>2021-02-08T00:00:00Z</published>
    
    <updated>2021-02-08T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/create-an-eleventy-podcast-feed/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;At the end of last year I converted my &lt;a href=&quot;https://djcruze.co.uk/&quot;&gt;DJ Cruze website&lt;/a&gt; from Wordpress to &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt;. Much like this website, it&#39;s easy to deploy using &lt;a href=&quot;https://netlify.com/&quot;&gt;Netlify&lt;/a&gt;. Moving to a statically generated site made it much easier to update and avoided me having to maintain a server, PHP, Wordpress versions, and so on.&lt;/p&gt;
&lt;p&gt;For many years I created the DJ Cruze podcast show so I wanted to move over the podcast RSS feed from Wordpress to Eleventy. Creating a custom RSS feed based on my podcast episodes took a bit of work so I thought I&#39;d share how I did it.&lt;/p&gt;
&lt;h2 id=&quot;create-the-podcast-data-file&quot;&gt;Create the podcast data file&lt;/h2&gt;&lt;p&gt;Eleventy supports a wide range of &lt;a href=&quot;https://www.11ty.dev/docs/data/&quot;&gt;data sources&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For my podcast I define a JSON file which contains the metadata needed for the podcast. All of this data will be used to populate the RSS feed. You may have this information in other data files in your Eleventy site but I wanted to keep most of the data for the podcast together.&lt;/p&gt;
&lt;p&gt;It&#39;s stored in the data directory like this: &lt;code&gt;/data/podcast.json&lt;/code&gt;. This will expose a global data object called &lt;code&gt;podcast&lt;/code&gt; which matches the filename of the JSON file and can be used in your template files.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DJ Cruze House Music Podcast&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DJ Cruze is in the house! Spinning funky and chunky house music since 1988. Manchester is in the house!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;category&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Music&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DJ Cruze&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;channelImage&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/podcasts/dj-cruze-podcast-logo-1400x1400.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;owner&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Marc Littlemore&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;info@djcruze.co.uk&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;feedPath&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/podcasts/feed.xml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;episode&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;defaultDescription&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The latest episode of the DJ Cruze podcast features new and old funky and chunky house music.&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the metadata properties should be obvious.&lt;/p&gt;
&lt;p&gt;Both iTunes and Google Podcasts support the standard RSS schema but with additional tags. Google Podcasts is essentially the same as an iTunes feeds and uses the iTunes specific tags in its feed too. Make sure that your &lt;code&gt;category&lt;/code&gt; property matches the expected &lt;a href=&quot;https://help.apple.com/itc/podcasts_connect/#/itc9267a2f12&quot;&gt;iTunes categories&lt;/a&gt; and be careful that you also match the correct case. If you don&#39;t, it may well be rejected. In my case I&#39;m using the &amp;quot;Music&amp;quot; category. Note that you can use sub-categories too but I&#39;m not in my example.&lt;/p&gt;
&lt;h2 id=&quot;set-up-a-podcast-post&quot;&gt;Set up a podcast post&lt;/h2&gt;&lt;p&gt;In Wordpress, each of my podcasts was a new post with a specific tag. I exported these as Markdown files and added some custom frontmatter to mark it up with per-podcast metadata.&lt;/p&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;media:
    # The episode number
    episode: 57

    # The image file for this episode
    # You can ignore this if you don&#39;t want per-episode artwork
    image: &#39;/images/podcasts/dj-cruze-podcast-episode-57-june-2011.jpg&#39;

    # Your MP3 file 
    content: &#39;/podcasts/dj-cruze-podcast-episode-57-june-2011.mp3&#39;

    # The duration of the episode in seconds
    duration: &#39;4230&#39;

    # The filesize of the MP3 file in bytes
    fileSize: &#39;67973718&#39;

    # A per-episode description if you want it
    description: &#39;Another DJ set of funky house music from DJ Cruze!&#39;

# Additional layout data here...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use Eleventy&#39;s &lt;a href=&quot;https://www.11ty.dev/docs/data-template-dir/&quot;&gt;directory specific data files&lt;/a&gt; to mark up all podcasts with the tag &lt;code&gt;podcast&lt;/code&gt;. The enables us to create an Eleventy collection of podcasts to iterate through.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;podcast&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;custom-filters&quot;&gt;Custom filters&lt;/h2&gt;&lt;p&gt;Our podcast feed needs some custom filters adding to the Eleventy configuration.&lt;/p&gt;
&lt;p&gt;RSS feeds expect some dates using the &lt;a href=&quot;https://www.w3.org/Protocols/rfc822/#z28&quot;&gt;RFC822 date format&lt;/a&gt;. There&#39;s an &lt;code&gt;npm&lt;/code&gt; package we can install to do the conversion for us. Install it as a development dependency like this:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev rfc822-date&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also need to escape some of our text to ensure any special characters are encoded correctly. I use the &lt;a href=&quot;https://lodash.com/&quot;&gt;Lodash&lt;/a&gt; &lt;a href=&quot;https://lodash.com/docs/4.17.15#escape&quot;&gt;escape&lt;/a&gt; method to do this. Again, lets install this a development dependency as follows:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev lodash.escape&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly, we need to know what the date of the last episode was so we can add a special &lt;code&gt;LastBuildDate&lt;/code&gt; tag. This tells any clients when the podcast was last modified. By adding this, any new podcasts will update to the latest date and allow new episodes to be downloaded.&lt;/p&gt;
&lt;p&gt;Here are the additional filters we add to our &lt;code&gt;.eleventy.js&lt;/code&gt; configuration file. I&#39;ve only added the filters we&#39;ve created for the podcast feed and intentionally left out any other Eleventy configuration.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; escape &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;lodash.escape&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rfc822Date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;rfc822-date&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// RSS&lt;/span&gt;
    eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addLiquidFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;rfc822Date&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;dateValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rfc822Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dateValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Escape characters for XML feed&lt;/span&gt;
    eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addLiquidFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;xmlEscape&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;escape&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Newest date in the collection&lt;/span&gt;
    eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;collectionLastUpdatedDate&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;collection &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;collection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;Collection is empty in collectionLastUpdatedDate filter.&#39;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rfc822Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;collection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Rest of Eleventy config goes here...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;template-file&quot;&gt;Template file&lt;/h2&gt;&lt;p&gt;Now we can create a post with a custom template in it to define our podcast URL. In this template we can iterate over our &lt;code&gt;podcast&lt;/code&gt; collection and render each podcast episode correctly into the expected RSS XML format.&lt;/p&gt;
&lt;p&gt;I created a podcast Liquid template file with a &lt;code&gt;/podcast/feed.xml&lt;/code&gt; permalink. I&#39;m excluding this file from other collections and from my sitemap. You might want to do this too.&lt;/p&gt;
&lt;p&gt;The podcast feed is split up into the intial channel metadata which is mostly created from our &lt;code&gt;podcast.json&lt;/code&gt; metadata. Ensure that any text is escaped correctly by passing it through the &lt;code&gt;escape&lt;/code&gt; filter.&lt;/p&gt;
&lt;p&gt;As I&#39;m attempting to redirect my old Wordpress theme from an old URL to a new URL, I&#39;ve also added in the &lt;code&gt;itunes:new-feed-url&lt;/code&gt; tag as follows. You won&#39;t need this unless you are migration your podcast feed too.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;new-feed-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{site.url}}{{podcast.feedPath}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;new-feed-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second part of the podcast feed iterates through our &lt;code&gt;collections.podcast&lt;/code&gt; feed in reverse order. In that way, we always get the latest episode first in the RSS feed. The metadata used for each episode comes from the Markdown frontmatter that we defined earlier.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;enclosure&lt;/code&gt; tag defines the media file needed to play the podcast. As I host my podcasts on a different URL to the DJ Cruze site itself, I have an additional URL in my &lt;code&gt;site.json&lt;/code&gt; data file which defines the external server to load them from. This explains the &lt;code&gt;site.mediaFilesUrl&lt;/code&gt; URL which is prepended before the content path.&lt;/p&gt;
&lt;p&gt;Here is the full RSS template.&lt;/p&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;permalink: &quot;/podcasts/feed.xml&quot;
eleventyExcludeFromCollections: true
sitemap:
  exclude: yes&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;rss&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;2.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;xmlns:&lt;/span&gt;itunes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.itunes.com/dtds/podcast-1.0.dtd&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;xmlns:&lt;/span&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://purl.org/rss/1.0/modules/content/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;channel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{podcast.title | xmlEscape}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{site.url}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;en-us&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;copyright&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#169;&quot;&gt;&amp;amp;#169;&lt;/span&gt; 2005 - {{&quot;now&quot; | date: &quot;%Y&quot;}} {{podcast.author}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;copyright&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;lastBuildDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{collections.podcast | collectionLastUpdatedDate}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;lastBuildDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;generator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Eleventy&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;generator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{podcast.description | xmlEscape}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{podcast.author}} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;explicit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;false&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;explicit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;episodic&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;image&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{podcast.channelImage | prepend: site.url}}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;owner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{podcast.owner.name}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{podcast.owner.email}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;owner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;category&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{podcast.category}}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;new-feed-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{site.url}}{{podcast.feedPath}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;itunes:&lt;/span&gt;new-feed-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    
&lt;span class=&quot;token code keyword&quot;&gt;    {%- for podcastEpisode in collections.podcast reversed -%}
      &amp;lt;item&gt;
        &amp;lt;pubDate&gt;{{ podcastEpisode.date | rfc822Date }}&amp;lt;/pubDate&gt;
        &amp;lt;link&gt;{{ podcastEpisode.url | prepend: site.url }}&amp;lt;/link&gt;&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.guid %}
          &amp;lt;guid&gt;{{ podcastEpisode.data.media.guid }}&amp;lt;/guid&gt;
        {% else %}
          &amp;lt;guid&gt;{{ podcastEpisode.url | prepend: site.url }}&amp;lt;/guid&gt;
        {% endif %}&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.title %}
          &amp;lt;title&gt;{{podcastEpisode.data.media.title | xmlEscape}}&amp;lt;/title&gt;
        {% else %}
          &amp;lt;title&gt;{{podcastEpisode.data.title}}&amp;lt;/title&gt;
        {% endif %}&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.description %}
          &amp;lt;description&gt;{{ podcastEpisode.data.media.description | xmlEscape }}&amp;lt;/description&gt;
        {% else %}
          &amp;lt;description&gt;{{ podcast.episode.defaultDescription | xmlEscape }}&amp;lt;/description&gt;
        {% endif %}
        
        &amp;lt;enclosure
          url=&quot;{{podcastEpisode.data.media.content | prepend: site.mediaFilesUrl}}&quot;
          length=&quot;{{podcastEpisode.data.media.fileSize}}&quot;
          type=&quot;audio/mpeg&quot;
        /&gt;&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% comment %} iTunes specific tags {% endcomment %}&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.episodeType %}
            &amp;lt;itunes:episodeType&gt;{{podcastEpisode.data.media.episodeType}}&amp;lt;/itunes:episodeType&gt;
        {% else %}
            &amp;lt;itunes:episodeType&gt;full&amp;lt;/itunes:episodeType&gt;
        {% endif %}&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.image %}
            &amp;lt;itunes:image href=&quot;{{podcastEpisode.data.media.image | prepend: site.url}}&quot; /&gt;
        {% endif %}&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.episode %}
            &amp;lt;itunes:episode&gt;{{podcastEpisode.data.media.episode}}&amp;lt;/itunes:episode&gt;
        {% endif %}&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.season %}
            &amp;lt;itunes:season&gt;{{podcastEpisode.data.media.season}}&amp;lt;/itunes:season&gt;
        {% endif %}&lt;/span&gt;

&lt;span class=&quot;token code keyword&quot;&gt;        {% if podcastEpisode.data.media.duration %}
            &amp;lt;itunes:duration&gt;{{podcastEpisode.data.media.duration}}&amp;lt;/itunes:duration&gt;
        {% endif %}        
      &amp;lt;/item&gt;
    {%- endfor -%}&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;channel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;rss&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you head to my &lt;a href=&quot;https://www.djcruze.co.uk/podcasts/feed.xml&quot;&gt;DJ Cruze podcast feed&lt;/a&gt;, you can see all of the episodes rendered correctly.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;&lt;p&gt;There is some metadata which could be automatically generated from the media files. I add to the duration and file size properties to my Markdown frontmatter for each podcast episode. I do this by hand but it could easily be generated by reading the file when Eleventy builds the feed.&lt;/p&gt;
&lt;p&gt;Here is the &lt;a href=&quot;https://github.com/MarcL/djcruze.co.uk/&quot;&gt;DJ Cruze website&lt;/a&gt; on GitHub. Feel free to take a look through the code and adapt it for your own use.&lt;/p&gt;
&lt;p&gt;I hope you find this useful. &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;Let me know&lt;/a&gt; if you have any questions or comments.&lt;/p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful resources&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://itunespartner.apple.com/podcasts/articles/podcast-requirements-3058&quot;&gt;Apple iTunes podcast requirements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.google.com/googleplay/podcasts/answer/6260341?hl=en&quot;&gt;Google Podcasts requirements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Always be learning</title>
    
    <link href="https://www.marclittlemore.com/always-be-learning/"/>
    <published>2021-02-07T00:00:00Z</published>
    
    <updated>2021-02-07T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/always-be-learning/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;We often think of learning as something we do at school, college, or university. You learn new skills as you start your career but often, people stop thinking they need to continue as they get older.&lt;/p&gt;
&lt;p&gt;It often feels like learning is something that we encourage our children to do but we forgot how fun it can be as adults. Learning something new encourages those dopamine hits and makes you feel motivated. If you challenge yourself and solve small problems by learning something new, you&#39;ll soon find that childlike wonder and love of learning all over again.&lt;/p&gt;
&lt;p&gt;Learning is natural - embrace it.&lt;/p&gt;
&lt;p&gt;Most &lt;a href=&quot;https://en.wikipedia.org/wiki/Knowledge_worker&quot;&gt;knowledge workers&lt;/a&gt; like me know that our industries change quickly. I&#39;ve learnt so much in the past 25+ years as a professional sofware developer. Computer languages change. Software development environments are constantly evolving. Every week in the world of JavaScript there&#39;s a new framework or library. Software development changes at an incredible pace but that doesn&#39;t mean you have to learn everything that the software industry throws at you. Think about &lt;a href=&quot;https://en.wikipedia.org/wiki/Just-in-time_learning&quot;&gt;just-in-time learning&lt;/a&gt; and learn as you need them.&lt;/p&gt;
&lt;p&gt;Companies look for people who can learn new skills quickly and embrace change. If you can constantly adapt to new challenges, you&#39;ll never be out of a job.&lt;/p&gt;
&lt;p&gt;What stops people from learning?&lt;/p&gt;
&lt;p&gt;It&#39;s hard work. You have to put some effort in to learn something new. But the challenge of the unknown is worth it for the rewards you&#39;ll find.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/&quot;&gt;Taking notes&lt;/a&gt; and learning a new skill helps to clarify your thoughts. If you expose yourself to new ways of thinking, you&#39;ll know if your current thinking is effective and worth continuing iwht. You can find different, and often simpler, ways of achieving the same goal with your new found knowledge.&lt;/p&gt;
&lt;p&gt;There are so many places to learn new skills now.&lt;/p&gt;
&lt;p&gt;Lots of people are sharing their knowledge online so you can find online courses for most things. Take a look on &lt;a href=&quot;https://www.udemy.com/&quot;&gt;Udemy&lt;/a&gt;, &lt;a href=&quot;https://www.skillshare.com/&quot;&gt;Skillshare&lt;/a&gt;, or just use Google, to learn anything from writing, machine learning, or photography, to beer brewing and watercolour painting. You name it and there will be a course for it.&lt;/p&gt;
&lt;p&gt;YouTube is filled with people teaching and this is all free. There are fantastic tutorials from talented people so dive in and take a look.&lt;/p&gt;
&lt;p&gt;If you prefer reading then Google for tutorials on the topic you want to learn or browse through Amazon to find a book on the subject.&lt;/p&gt;
&lt;p&gt;Make learning a priority. If you&#39;re not learning then you can quickly become stagnant. &lt;a href=&quot;https://www.marclittlemore.com/learning-to-learn-software-development/&quot;&gt;Learn how to learn&lt;/a&gt;. And don&#39;t forget to share your notes and &lt;a href=&quot;https://www.marclittlemore.com/learn-in-public/&quot;&gt;learning in public&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Always be learning.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Creating an idea habit</title>
    
    <link href="https://www.marclittlemore.com/creating-an-idea-habit/"/>
    <published>2021-02-06T00:00:00Z</published>
    
    <updated>2021-02-06T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/creating-an-idea-habit/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;If you&#39;re playing along at home, you&#39;ll know that I embarked on a &lt;a href=&quot;https://www.marclittlemore.com/creating-a-writing-habit/&quot;&gt;30-day writing habit&lt;/a&gt; challenge to myself.&lt;/p&gt;
&lt;p&gt;I want to attempt to &lt;a href=&quot;https://charlesduhigg.com/how-habits-work/&quot;&gt;kickstart a habit&lt;/a&gt;, and feel like I was getting better at putting pen to paper, or fingers to keyboard. I don&#39;t think I&#39;m a bad writer, but I&#39;ve always struggled to get started with writing.&lt;/p&gt;
&lt;p&gt;Once I start writing, I love it. But some days I hit a roadblock.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What should I write about?&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;a-habit-of-ideas&quot;&gt;A habit of ideas&lt;/h2&gt;&lt;p&gt;I found a great &lt;a href=&quot;https://twitter.com/MarkShpuntov/status/1357834827748302855&quot;&gt;Twitter thread&lt;/a&gt; from &lt;a href=&quot;https://twitter.com/MarkShpuntov/&quot;&gt;Mark Shpuntov&lt;/a&gt; which summarised a workshop where &lt;a href=&quot;https://twitter.com/AliAbdaal/&quot;&gt;Ali Abdaal&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/david_perell&quot;&gt;David Perell&lt;/a&gt; discussed &amp;quot;building an idea factory&amp;quot;. I thought this was a fantastic description of what your brain could become.&lt;/p&gt;
&lt;p&gt;Very few people have 100% original ideas. You don&#39;t have to constantly come up with unique ideas to create good content. When you combine an idea with your voice and your knowledge and experience of somthing, it becomes original.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aliabdaal.com/&quot;&gt;Ali Abdaal&lt;/a&gt; is a doctor and &lt;a href=&quot;https://www.youtube.com/user/Sepharoth64&quot;&gt;top YouTube creator&lt;/a&gt; in the productivity space. He suggests a some simple ways to generate new ideas.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find content creators that you watch regularly and consider them to be great in their area.&lt;/li&gt;
&lt;li&gt;Be inspired from their content and watch or read what they create.&lt;/li&gt;
&lt;li&gt;Create your own ideas and content based upon their work but give it a unique angle.&lt;/li&gt;
&lt;li&gt;Make sure you reference the original idea and creator.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can ensure that you don&#39;t feel like you&#39;re just stealing their ideas by using more than just a single source to create your own. Read multiple articles or watch videos from different creators to gain the initial inspiration but apply your own thinking and experience, to enable you to make it different.&lt;/p&gt;
&lt;p&gt;To be more creative you can diversify the content you consume. As &lt;a href=&quot;https://en.wikipedia.org/wiki/Steve_Jobs&quot;&gt;Steve Jobs&lt;/a&gt; once said:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“&lt;a href=&quot;https://www.wired.com/1996/02/jobs-2/&quot;&gt;Creativity is just connecting things&lt;/a&gt;”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Take inspiration from different places, different fields of study, and different places. The more things you can connect, the better your ideas will become. Try to consume quality content and look for inspiration and ideas in unique books, documentaries, YouTube videos, podcasts, and blogs.&lt;/p&gt;
&lt;p&gt;One thing that I&#39;m trying to get better at is capturing the things I learn using &lt;a href=&quot;https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/&quot;&gt;Obsidian&lt;/a&gt;. Good ideas are everywhere but our brains can struggle to notice them. As with most things, I believe that this can be improved with practice. Having a reliable process for capturing ideas is a great way to let your brain think of the ideas and then dump them into a place that you can curate them. Pay attention to any ideas you may have and attempt to write each one down.&lt;/p&gt;
&lt;p&gt;Telling others your ideas can help you to clarify your thinking and maybe allow you to see it in a new way. This could be writing it down in a blog post like this, tweeting it out on Twitter, or having a chat with others. Having a discussion allows people to ask more questions which will lead to new ideas and thinking.&lt;/p&gt;
&lt;p&gt;The last thing I&#39;m planning on doing before my next blog post is to brainstorm ideas. I have a lot of diverse interests and sometimes it&#39;s hard to slim down all of my thoughts into simple ideas. A 30-minute session where I concentrate on coming up with as many thoughts as possible will help me to consider options and ideas I&#39;d not previously considered.&lt;/p&gt;
&lt;p&gt;It&#39;s time to break out the &lt;a href=&quot;https://en.wikipedia.org/wiki/Mind_map&quot;&gt;mind mapping&lt;/a&gt; software before tomorrow&#39;s post.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Building an audience</title>
    
    <link href="https://www.marclittlemore.com/building-an-audience/"/>
    <published>2021-02-03T00:00:00Z</published>
    
    <updated>2021-02-03T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/building-an-audience/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Building an online audience is a great way to build your career.&lt;/p&gt;
&lt;p&gt;The more people who see your writing or videos, the more your network can expand. This can help you to connect with more people and create more opportunities for your career.&lt;/p&gt;
&lt;p&gt;This video by the writer and entrepreneur &lt;a href=&quot;https://www.nateliason.com/&quot;&gt;Nate Eliason&lt;/a&gt; shows us how he builds an audience for his top-selling online course - &lt;a href=&quot;https://www.effortlessoutput.com/&quot;&gt;Effortless Output in Roam&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here are my notes from watching it and learning from an online expert.&lt;/p&gt;
&lt;h2 id=&quot;the-video&quot;&gt;The video&lt;/h2&gt;&lt;p&gt;https://www.youtube.com/watch?v=LSrcELpdgX8&lt;/p&gt;
&lt;h2 id=&quot;lessons-to-learn&quot;&gt;Lessons to learn&lt;/h2&gt;&lt;h3 id=&quot;start-an-email-list&quot;&gt;Start an email list&lt;/h3&gt;&lt;p&gt;Most people start posting their messages on social media. A new tweet, an Instagram photo, a post on Facebook, or a YouTube video only gets you so far. Twitter, Facebook, or Google own their platforms and they control how many of your followers see your content. They decide what the people see. And they decide if you&#39;re allowed on the platform.&lt;/p&gt;
&lt;p&gt;By building an email list, you control the distribution of your content and message. It&#39;s not uncommon to see 30 to 50% open rates of emails if the audience is engaged and wants to hear from you. With an email service provider, you control the emails in your list. You have permission from the users to email them. You can export the email addresses to a spreadsheet and take them to another email provider with ease.&lt;/p&gt;
&lt;p&gt;Nates likes &lt;a href=&quot;https://convertkit.com/&quot;&gt;ConvertKit&lt;/a&gt; and I&#39;ve had a good experience with &lt;a href=&quot;https://mailchimp.com/&quot;&gt;Mailchimp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But making an email list isn&#39;t enough...&lt;/p&gt;
&lt;h3 id=&quot;how-to-get-people-to-join-your-list&quot;&gt;How to get people to join your list&lt;/h3&gt;&lt;p&gt;There are a lot of emails lists and people aren&#39;t going to sign up to yours without a compelling reason to do so. Just asking people to sign up to your newsletter isn&#39;t enough. You have to create a &amp;quot;super freebie&amp;quot;.&lt;/p&gt;
&lt;p&gt;A &amp;quot;super freebie&amp;quot; is something so good that they don&#39;t even think twice about signing up to your email list. For example, Nate and his friend &lt;a href=&quot;http://justinmares.com/&quot;&gt;Justin Mares&lt;/a&gt; created a 7 day series of free lesons for their technical marketing course. Every lesson was at least 1500 to 2000 words long with screenshots, videos, and free code examples. They made sure that each daily lesson had a really high value. The subscribers were so blown away by the free content, that they were asking for the paid course before finishing all of the lessons!&lt;/p&gt;
&lt;p&gt;Some other examples of a high value &amp;quot;super freebie&amp;quot; are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A valuable digital download&lt;/li&gt;
&lt;li&gt;A video course&lt;/li&gt;
&lt;li&gt;Free templates for a design tool&lt;/li&gt;
&lt;li&gt;Free code snippets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It has to be so good that people don&#39;t hesitate to share their email address to get it.&lt;/p&gt;
&lt;p&gt;The easiest way to start collecting email addresses, and starting to build your audience, is to set up a landing page. On the page you should explain to them what they&#39;re getting when they sign up. On all of your future videos, articles, or social media posts, you can send users to this landing page. It will have a clear call to action and it&#39;ll be understood what they&#39;ll receive when they join your mailing list.&lt;/p&gt;
&lt;p&gt;But how do you get people to show up on the landing page in the first place?&lt;/p&gt;
&lt;h3 id=&quot;involve-others&quot;&gt;Involve others&lt;/h3&gt;&lt;p&gt;When you&#39;re first starting out, you probably don&#39;t have much of an audience. While you wait to build up your own followers, you can leverage other people&#39;s credibility by involving them in what you&#39;re doing.&lt;/p&gt;
&lt;p&gt;Interview a dozen experts in the field of what you&#39;re trying to build a product around. Share those interviews on your blog, in videos, or on a podcast. Point the audience at the &amp;quot;super freebie&amp;quot; that they should look at if they found the interview useful or interesting. If you do a good job with the interviews, and all of your guests feel appreciated, then they&#39;ll be happy to share the article with their audience. This will help to build your credibility and drive traffic to your landing page.&lt;/p&gt;
&lt;p&gt;This isn&#39;t just about you though.&lt;/p&gt;
&lt;p&gt;It should be a win-win deal where you&#39;re helping them. If you give them more value than they&#39;re giving you, then they&#39;ll be happy to help you boost what you&#39;re doing. People know when you&#39;re being selfish and just out for yourself.&lt;/p&gt;
&lt;p&gt;People can tell if you&#39;re just wanting to promote your own work. If you&#39;re reaching out to people to help them, don&#39;t ask them to write anything. Writing is an intensive and time consuming process. Asking for a guest post takes a lot more energy than asking for an interview. It&#39;s much easier to show up and talk about their life for 30 minutes than it is to write 1000 words. If you do want an article, ask them for some old content. This could be something that they&#39;d previously shared with their own audience.&lt;/p&gt;
&lt;p&gt;Once you&#39;ve created your content, offer it out to them and their audience to share as part of their products too. Give them full permission to use it as their own. This makes it really easy to build a mutually advantageous relationship which helps you both.&lt;/p&gt;
&lt;p&gt;The more you help others, the easier it is to promote your &amp;quot;super freebie&amp;quot;.&lt;/p&gt;
&lt;h3 id=&quot;promote-the-super-freebie&quot;&gt;Promote the super freebie&lt;/h3&gt;&lt;p&gt;Once you have an email list and an audience, this will be the main way you promote your work. However, this is the chicken-and-egg situation. You need an audience to build an audience. This is where online communities can help.&lt;/p&gt;
&lt;p&gt;Look for Reddit subreddits or Facebook groups that focus around the subject you want to build your product around. You should be able to find some of these communities online which care about your product. Actively participate in the conversations in these groups and bring value to them. Over time you&#39;ll be able to introduce your &amp;quot;super freebie&amp;quot; or product that you&#39;re working on. Don&#39;t you try and sell to them immediately or it becomes annoying. It&#39;s all about building relationships with like-minded people so you can kickstart your own audience.&lt;/p&gt;
&lt;h3 id=&quot;using-seo-for-passively-growing-the-list&quot;&gt;Using SEO for passively growing the list&lt;/h3&gt;&lt;p&gt;For long-term audience growth you should be considering SEO for your video or blog content as you create it. For Nate, he put up the technical marketer lessons on the blog and over time they started ranking for keywords like &amp;quot;automate your twitter activity&amp;quot; and &amp;quot;automatically reply to tweets&amp;quot;. People would find the content, read it, and sign up to the email list to get all of the other lessons in the course. This drove between 10 and 30 people per day onto the email list without them intending for this to happen.&lt;/p&gt;
&lt;p&gt;With Nate&#39;s &lt;a href=&quot;https://roamresearch.com/&quot;&gt;Roam Research&lt;/a&gt; &lt;a href=&quot;https://www.nateliason.com/blog/roam&quot;&gt;blog post&lt;/a&gt;, he managed to get the article on the first page of Google for the keywords &amp;quot;Roam research&amp;quot;. Once someone has found the article and read it, there&#39;s a call to action at the bottom to sign up for the email list to learn more about how to use Roam. This is another great way to build up the audience over time.&lt;/p&gt;
&lt;h3 id=&quot;encourage-referrals&quot;&gt;Encourage referrals&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://www.morningbrew.com/&quot;&gt;Morning Brew&lt;/a&gt; is a huge email newsletter that has &lt;a href=&quot;https://www.vox.com/recode/2020/10/13/21515418/morning-brew-business-insider-deal-acquisition&quot;&gt;recently been acquired&lt;/a&gt; by Business Insider. They grew successfully by encouraging people to invite their friends to join up in exchange for Morning Brew products. This baked virality into their newsletter so that every sign up brought in more people until they had millions of subscribers. But you don&#39;t have to use any fancy referral software to do this.&lt;/p&gt;
&lt;p&gt;When someone signs up to your newsletter, send them an email asking them to share it on Twitter. In your email you can include some simple share links for Twitter or Facebook or wherever you want them to share it. You can then use your email tool, like ConvertKit to track anyone who clicks on one of the share links and send the orginator another email with a link to an additional freebie. This gives them another great reason to share our content.&lt;/p&gt;
&lt;p&gt;For example, Nate and Justin had an additional PDF for their technical marketer course which shared some common automation tasks with Zapier that people would get if they shared the course with their friends. They found that 20-30% of the people would share the course on Twitter to get the new valuable freebie. This brought in a few thousand subscribes.&lt;/p&gt;
&lt;h3 id=&quot;put-your-content-on-a-blog&quot;&gt;Put your content on a blog&lt;/h3&gt;&lt;p&gt;Finally, you should always share the content and link to your freebies on your blog. Starting a blog is a great way to start building that audience.&lt;/p&gt;
&lt;p&gt;Hopefully you enjoyed these notes on Nate&#39;s video and found them useful.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Ship your code with confidence</title>
    
    <link href="https://www.marclittlemore.com/ship-your-code-with-confidence/"/>
    <published>2021-02-02T00:00:00Z</published>
    
    <updated>2021-02-02T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/ship-your-code-with-confidence/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;All developers want to ship code on a daily basis. But how can we ensure that the code we&#39;re pushing out to our users won&#39;t break?&lt;/p&gt;
&lt;p&gt;There are plenty of good practices we can use to ensure we can confidently release new versions of our applications. Here are a few that I think are really valuable.&lt;/p&gt;
&lt;h2 id=&quot;version-control&quot;&gt;Version control&lt;/h2&gt;&lt;p&gt;I&#39;m always surprised when I have to mention this, but I&#39;ve still met some teams and individuals who still don&#39;t use &lt;a href=&quot;https://en.wikipedia.org/wiki/Version_control&quot;&gt;version control&lt;/a&gt; for their source code. Even if you&#39;re a solo developer, make sure you use a source control system. Having revisions of your code is a must for everyone&#39;s sanity. Having previous versions of your code makes it easier to spot any differences should you have errors when you deploy to production. You can also use this revision history to roll back to a previous version should you need to.&lt;/p&gt;
&lt;h2 id=&quot;write-tests-and-run-locally&quot;&gt;Write tests and run locally&lt;/h2&gt;&lt;p&gt;When you write your first computer program, you test it manually as it often doesn&#39;t do a lot. Once things get more complicated, manually verifying all of your changes can take a long time. And how do you know that you haven&#39;t broken anything else with your new code?&lt;/p&gt;
&lt;p&gt;This is why you should start writing automated tests for your code. Investing the time into learning about unit, integration, and end-to-end tests will help you to feel confident that your new code works and that it hasn&#39;t broken any other code.&lt;/p&gt;
&lt;p&gt;You can run your tests on your local computer but it&#39;s also great to add in hooks to run tests automatically when you attempt to commit or push code. I use &lt;code&gt;git&lt;/code&gt; for my version control and I add &lt;a href=&quot;https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks&quot;&gt;git hooks&lt;/a&gt; so that the tests have to run and complete successfully before I can push any code to a remote repository like GitHub.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;If you&#39;re writing JavaScript code, and are new to testing, then download my &lt;strong&gt;FREE&lt;/strong&gt; course on &lt;a href=&quot;https://www.marclittlemore.com/javascript-testing/&quot;&gt;JavaScript testing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;pair-programming-and-code-reviews&quot;&gt;Pair programming and code reviews&lt;/h2&gt;&lt;p&gt;I place a lot of value in having more than one person looking at a code change. &lt;a href=&quot;https://en.wikipedia.org/wiki/Pair_programming&quot;&gt;Pair programming&lt;/a&gt; is the practice of two people working together on code. This is conventionally done by two people sitting next to each other but in these times of remote working, it&#39;s just as easy to do this remotely with something like &lt;a href=&quot;https://visualstudio.microsoft.com/services/live-share/&quot;&gt;Visual Studio Live Share&lt;/a&gt;. Working together on a problem can help to spot errors early and helps to create consistent patterns and strategies for the code.&lt;/p&gt;
&lt;h2 id=&quot;code-reviews&quot;&gt;Code reviews&lt;/h2&gt;&lt;p&gt;Once the code is complete, I use a &lt;a href=&quot;https://guides.github.com/introduction/flow/&quot;&gt;branch-based workflow in GitHub&lt;/a&gt;. With this method I create pull requests to allow feature branch code reviews. This discussion around the code again gives the team another opportunity to spot any errors.&lt;/p&gt;
&lt;p&gt;Before the code can be merged to a main branch, we can also run our tests on our &lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_integration&quot;&gt;continuous integration&lt;/a&gt; server. This allows us to check that all of our tests pass in a completely new environment and not just on a developer&#39;s local machine. Try and avoid &amp;quot;it works on my machine&amp;quot; syndrome in your teams.&lt;/p&gt;
&lt;h2 id=&quot;small-iterations-and-often&quot;&gt;Small iterations and often&lt;/h2&gt;&lt;p&gt;A few years ago, it was commonplace to spend a month or two adding features and fixing bugs for an application and then have a big bang release of thousands of lines of code. This isn&#39;t the way to build code confidence!&lt;/p&gt;
&lt;p&gt;Breaking a problem into small iterations that you can release quickly and often is the way to combat this. It helps to speed up delivery and gives the team confidence in your code deployments. If there is a problem once you&#39;ve released to production, it&#39;s such a small change that you can more easily roll it back or understand how to fix it.&lt;/p&gt;
&lt;h2 id=&quot;feature-flags&quot;&gt;Feature flags&lt;/h2&gt;&lt;p&gt;One of the best techniques we implemented to build confidence in our code has been &lt;a href=&quot;https://launchdarkly.com/blog/what-are-feature-flags/&quot;&gt;feature flags&lt;/a&gt;. These are variables which we can use to determine whether a logical path should be followed or not. They are often booleans but can just as easily have multiple values. These flags allows us to release code quickly and often but not yet enable a feature until we decide that the time is right.&lt;/p&gt;
&lt;p&gt;There are many ways to implement feature flags but in our current system, we have a user interface which allows us to change the value for the feature flag and this gets propagated to any running instances of our applications. This allows a feature flag&#39;s value to be altered and it&#39;s an almost instant change for the end users. They are also great if any issues are seen in the production environment because the new feature can almost immediately be turned off too.&lt;/p&gt;
&lt;p&gt;Note that it does incur some technical debt when you add feature flags. The code can end up littered with feature flag logic and branching paths in both the code and tests. Make sure you remove the feature flag code as soon as you&#39;re happy that the new code works as expected.&lt;/p&gt;
&lt;h2 id=&quot;development-environments&quot;&gt;Development environments&lt;/h2&gt;&lt;p&gt;In order to ensure you can perform some testing prior to the production environment, it&#39;s a good idea to consider multiple development environments. This can vary based upon your own needs. We use multiple environments for testing our integration with other products prior to the code going live.&lt;/p&gt;
&lt;p&gt;Depending on the scale of your application, you might want to consider performance testing on one of your environments. This tends to be a staging environment where you attempt to make it as close to the production environment as possible to mimic real-world conditions.&lt;/p&gt;
&lt;h2 id=&quot;live-testing&quot;&gt;Live testing&lt;/h2&gt;&lt;p&gt;Finally, once your code has been deployed to the production environment, you can test it here. These can be &lt;a href=&quot;https://en.wikipedia.org/wiki/Smoke_testing_(software)&quot;&gt;smoke tests&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Regression_testing&quot;&gt;regression tests&lt;/a&gt; to ensure that no features have been broken. You can also use any monitoring you have to ensure that alarms trigger if performance degrades or tests fail on the live environment.&lt;/p&gt;
&lt;p&gt;I hope this has given you some ideas for giving you more confidence in your development processes. Let me know on &lt;a href=&quot;https://twitter.com/marclittlemore&quot;&gt;Twitter&lt;/a&gt; or via the &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;contact form&lt;/a&gt; if you have any others.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Create animated gifs using Camtasia</title>
    
    <link href="https://www.marclittlemore.com/create-animated-gifs-camtasia/"/>
    <published>2021-01-31T00:00:00Z</published>
    
    <updated>2021-01-31T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/create-animated-gifs-camtasia/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I love gifs!&lt;/p&gt;
&lt;div class=&quot;w-full h-0 pb-72 relative&quot;&gt;
    &lt;iframe src=&quot;https://giphy.com/embed/3oriO6qJiXajN0TyDu&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen=&quot;&quot;&gt;
    &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I used to create them in the 90s when I was learning how to build my early websites. It was probably mostly the animated &amp;quot;under construction&amp;quot; gif. They went out of fashion in the late 90s due to &lt;a href=&quot;https://www.smithsonianmag.com/history/brief-history-gif-early-internet-innovation-ubiquitous-relic-180963543/&quot;&gt;better file formats and a patent on the gif format&lt;/a&gt;. When Slack was launched in 2013, and other messaging apps became commonplace, they made a reappearance as an animated response to messages. I think I probably send at least 10 gifs a day but I&#39;d only made a few of my own. As I use &lt;a href=&quot;https://www.techsmith.com/video-editor.html&quot;&gt;Camtasia&lt;/a&gt; for my &lt;a href=&quot;https://www.youtube.com/c/MarcLittlemore&quot;&gt;own videos on YouTube&lt;/a&gt;, let&#39;s look at how we can make our own using that.&lt;/p&gt;
&lt;p&gt;Oh and of course, I pronounce it &amp;quot;gif&amp;quot; with a hard G like &amp;quot;gift&amp;quot;. That&#39;s the correct way right? 😉&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;&lt;p&gt;To start off you need to record some video footage. This can be a video of yourself, your family, or some other fun situation.&lt;/p&gt;
&lt;p&gt;You can record this easily using your mobile phone and then upload it to your computer. If you have a webcam, you can also record it straight into Camtasia instead.&lt;/p&gt;
&lt;h2 id=&quot;a-basic-camtasia-gif-project&quot;&gt;A basic Camtasia gif project&lt;/h2&gt;&lt;p&gt;The main thing to remember with a gif is that it needs to be small. You&#39;re not looking for ultra-HD footage here. If you&#39;re planning on uploading the gif to a site like &lt;a href=&quot;http://giphy.com/&quot;&gt;Giphy&lt;/a&gt; or &lt;a href=&quot;https://tenor.com/&quot;&gt;Tenor&lt;/a&gt; then, although they support 720p images, its best to stick to a 480p resolution of 852 x 480 pixels.&lt;/p&gt;
&lt;p&gt;Set up the project in Camtasia by clicking on &lt;code&gt;Edit &amp;gt; Project Settings&lt;/code&gt; and set the resolution to a width of 852 and height of 480 pixels. Leave the frame rate as 30 fps.&lt;/p&gt;
&lt;p&gt;Drag the footage that you&#39;ve created already into the Camtasia Media Bin. If you&#39;re recording footage in Camtasia, ensure your webcam is enabled and record your footage directly into the application instead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/camtasia-video-footage.jpg&quot; alt=&quot;Camtasia video footage&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, drag the video you&#39;ve added or created onto the timeline. You&#39;ll probably have recorded more footage than you need so use Camtasia&#39;s trimming tools to resize it to only the footage you need. Ideally this will be under 6 seconds long. For my gif, this was around 2 seconds long.&lt;/p&gt;
&lt;p&gt;A gif needs to be short and sweet.&lt;/p&gt;
&lt;div class=&quot;w-full h-0 pb-72 relative&quot;&gt;
    &lt;iframe src=&quot;https://giphy.com/embed/5wWf7GR2nhgamhRnEuA&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen=&quot;&quot;&gt;
    &lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;add-text&quot;&gt;Add text&lt;/h2&gt;&lt;p&gt;If you want to add some text to your gif, you can do this by using Camtasia&#39;s &lt;code&gt;annotations&lt;/code&gt; tools in the left menu bar. Choose the annotation type that you&#39;d like and drag it to the timeline. Make sure that you place it above the video on the timeline so that you can see it overlaid on top of the video footage.&lt;/p&gt;
&lt;p&gt;If you want to use the classic meme font, it&#39;s &lt;code&gt;Impact&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Move the text to the correct place for your video footage as shown in the screenshot below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/camtasia-video-with-text.jpg&quot; alt=&quot;Camtasia video with text&quot;&gt;&lt;/p&gt;
&lt;p&gt;For my footage, I added a behaviour to the text so that it grows into the screen. Drag the behaviour of your choice on top of the text to apply it.&lt;/p&gt;
&lt;p&gt;Depending on the video footage colour, you might need to add an additional background shape behind the text. If you do, make sure it sits underneath the text but above the video in the Camtasia timeline.&lt;/p&gt;
&lt;p&gt;You can add any additional transitions to the text that you want such as fade in or out.&lt;/p&gt;
&lt;h2 id=&quot;exporting&quot;&gt;Exporting&lt;/h2&gt;&lt;p&gt;Now you&#39;ve created your masterpiece, it&#39;s time to export it. If you&#39;re planning on uploading the gif to Giphy or Tenor, you can export the timeline as an MP4.&lt;/p&gt;
&lt;p&gt;Choose &lt;code&gt;Share &amp;gt; Local File&lt;/code&gt; from the menu and export it to the folder of your choice as an MP4.&lt;/p&gt;
&lt;p&gt;Alternatively, if you want the gif file, choose the same &lt;code&gt;Share &amp;gt; Local File&lt;/code&gt; option from the menu but change the output format to animated gif and hit export.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/camtasia-export.jpg&quot; alt=&quot;Camtasia export&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you&#39;re exporting it as a gif file, it often has quite a large file size because we can&#39;t go lower than 30 frame per second for a Camtasia project. You can optimise the gif file using a tool like &lt;a href=&quot;https://ezgif.com/optimize/&quot;&gt;EZGif&lt;/a&gt;. Experiment with the different optimisation formats. I used the lossy gif encoder at 35% which had a good reduction in file size without making the gif look too bad.&lt;/p&gt;
&lt;h2 id=&quot;upload-to-giphy-or-tenor&quot;&gt;Upload to Giphy or Tenor&lt;/h2&gt;&lt;p&gt;Now you can upload it to the gif platform of your choice. I use Giphy a lot so I&#39;ve created an account and &lt;a href=&quot;https://giphy.com/channel/marclittlemore&quot;&gt;channel&lt;/a&gt; there. Upload your MP4 or gif file to your channel. You can then add tags so that it&#39;s searchable via their search engine. This will make it appear in apps like Slack too. Make sure you add relevant tags to the gif. In Giphy you can add a maximum of 20 tags so make sure they count.&lt;/p&gt;
&lt;h2 id=&quot;animated-gif-best-practices&quot;&gt;Animated gif best practices&lt;/h2&gt;&lt;p&gt;Gifs are not high quality videos so think about the following for optimising them for the gif platforms.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Try and make your gif short. Under 6 seconds is best.&lt;/li&gt;
&lt;li&gt;Make your source video resolution should be 720p max, but we recommend you keep it at 480p.&lt;/li&gt;
&lt;li&gt;Most gifs appear on a phone or in messaging apps like Slack or WhatsApp. You don&#39;t need huge files at high resolution.&lt;/li&gt;
&lt;li&gt;Try and keep the number of frames in the animation low. Try to keep it at 200 frames or less. At 30 frames per second, that&#39;s just over 6.5 seconds in length.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Enjoy and keep on creating great gifs!&lt;/p&gt;
&lt;div class=&quot;w-full h-0 pb-72 relative&quot;&gt;
    &lt;iframe src=&quot;https://giphy.com/embed/l4HodBpDmoMA5p9bG&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen=&quot;&quot;&gt;
    &lt;/iframe&gt;
&lt;/div&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Rubber duck debugging</title>
    
    <link href="https://www.marclittlemore.com/rubber-duck-debugging/"/>
    <published>2021-01-30T00:00:00Z</published>
    
    <updated>2021-01-30T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/rubber-duck-debugging/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I wrote &lt;a href=&quot;https://www.marclittlemore.com/games/&quot;&gt;video games&lt;/a&gt; for about 20 years and for a long time I made games for the Sony PlayStation consoles.&lt;/p&gt;
&lt;p&gt;Sony were famous for using rubber ducks in their console announcement demos. Here&#39;s some old footage of the PlayStation 3 announcement from the wonderful &lt;a href=&quot;https://twitter.com/mrphilharrison&quot;&gt;Phil Harrison&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=7UaDvIPpQnQ&lt;/p&gt;
&lt;p&gt;At Sony&#39;s &lt;a href=&quot;https://www.eurogamer.net/articles/2013-03-22-wipeout-the-rise-and-fall-of-sony-studio-liverpool&quot;&gt;Studio Liverpool&lt;/a&gt;, we had hundreds of rubber ducks in the offices. They became part of our debugging process.&lt;/p&gt;
&lt;p&gt;Yes, I used to talk to them when I had coding problems. 🦆&lt;/p&gt;
&lt;h2 id=&quot;what-is-rubber-duck-debugging&quot;&gt;What is rubber duck debugging?&lt;/h2&gt;&lt;p&gt;Writing computer software is a very precise skill. You have to be explicit in your code and clear in the logic to get the results you want. Humans are often not so good at this. Requirements can seem ambigiously or perhaps we don&#39;t have enough details before we start coding a solution. It&#39;s easy to become blind to errors that we&#39;ve created. Our code can be incorrect but it can be difficult to understand why.&lt;/p&gt;
&lt;p&gt;The idea of &lt;strong&gt;rubber duck debugging&lt;/strong&gt; is that when you encounter a problem in your code, you talk through your code to a rubber duck. It doesn&#39;t have to be a duck. It can be another inanimate object, your children, another member of your team, your partner, or especially in these times of working from home, it could be your cat, dog, or hamster. 🐹&lt;/p&gt;
&lt;p&gt;So what do you need to do?&lt;/p&gt;
&lt;h3 id=&quot;1-read-through-your-code-out-loud&quot;&gt;1. Read through your code out loud&lt;/h3&gt;&lt;p&gt;Explain your code at a high level to your duck friend. Tell it what the code is meant to do if it was working. This helps you to set the understanding for the problem at hand.&lt;/p&gt;
&lt;h3 id=&quot;2-explain-each-line-and-functions&quot;&gt;2. Explain each line and functions&lt;/h3&gt;&lt;p&gt;Next you should try and explain each line on its own. Describe the flow of your function and what each line does. Say how it changes any variable values or state within the function. Don&#39;t skip any of the details. Ducks like details.&lt;/p&gt;
&lt;h3 id=&quot;3-discuss-any-state-or-global-variables&quot;&gt;3. Discuss any state or global variables&lt;/h3&gt;&lt;p&gt;If your code isn&#39;t purely &lt;a href=&quot;https://en.wikipedia.org/wiki/Functional_programming&quot;&gt;functional&lt;/a&gt;, you may rely on state or global variables from outside of this code. Describe what this is, where it is set, and what its values should be.&lt;/p&gt;
&lt;h3 id=&quot;4-find-your-problem&quot;&gt;4. Find your problem&lt;/h3&gt;&lt;div class=&quot;w-full h-0 pb-72 relative&quot;&gt;
    &lt;iframe src=&quot;https://giphy.com/embed/xT5LMzIK1AdZJ4cYW4&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen=&quot;&quot;&gt;
    &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;At this point you should have your &amp;quot;DOH!&amp;quot; moment. Talking about the code out loud will help you to realise the assumptions you&#39;ve made which might not be true. Maybe you&#39;ll spot the typo where you&#39;ve misnamed a variable. Or perhaps you&#39;ll see that your logic is incorrect.&lt;/p&gt;
&lt;p&gt;Your duck will have helped you to find your problem! Aren&#39;t ducks great? 🐤&lt;/p&gt;
&lt;h2 id=&quot;why-does-rubber-duck-debugging-work&quot;&gt;Why does rubber duck debugging work?&lt;/h2&gt;&lt;p&gt;You&#39;ll be amazed that this type of debugging actually works. I&#39;ve often explained a problem to one of my team and as I explain each line, I end up discovering the issue. While writing new code, you can easily enter a state of &lt;a href=&quot;https://en.wikipedia.org/wiki/Flow_(psychology)&quot;&gt;flow&lt;/a&gt;, also known as &amp;quot;being in the zone&amp;quot;. In this state, your brain is thinking quickly and your hands are busy typing. You think you understand what you need to do but you might miss the details.&lt;/p&gt;
&lt;p&gt;Once you start explaining a problem to your rubber duck, your brain slows down to read and understand what&#39;s going on. You think a lot faster than you talk. Talking out loud helps you to focus on what&#39;s gone wrong.&lt;/p&gt;
&lt;p&gt;Try out rubber duck debugging when you next have a problem. You&#39;ll find that you quickly gain clarity on what your code does rather than what you think it does. And don&#39;t forget to bring some bread to feed your duck. 🦆&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Lift your team up</title>
    
    <link href="https://www.marclittlemore.com/lift-your-team-up/"/>
    <published>2021-01-27T00:00:00Z</published>
    
    <updated>2021-01-27T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/lift-your-team-up/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I&#39;ve always loved the following saying:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“A rising tide lifts all boats”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Although John F Kennedy may have borrowed it from the chamber of commerce of the &lt;a href=&quot;https://en.wikipedia.org/wiki/A_rising_tide_lifts_all_boats&quot;&gt;New England Council&lt;/a&gt;, the idea behind it really resonates with me. The true meaning behind it was to suggest that an improved economy will benefit all participants. However, I like to apply it to how you build a positive team culture. By helping one person, you quickly help to lift the whole team up.&lt;/p&gt;
&lt;p&gt;As a manager of a software engineering team, I help to set the tone for team. It&#39;s my responsibility to ensure the team are happy, that they&#39;re constantly learning, and that they can grow their careers. By building an environment where everyone helps to bring their team mates up, we end up with a positive and inclusive culture.&lt;/p&gt;
&lt;p&gt;So how do you help to lift your whole team up?&lt;/p&gt;
&lt;h2 id=&quot;be-encouraging&quot;&gt;Be encouraging&lt;/h2&gt;&lt;p&gt;Believe in your team. You hired them for a reason. Don&#39;t let them join your team and then leave them to get on with the work in isolation. Give regular feedback. Help them to thrive. When they&#39;re doing a great job then tell them. Share their successes with others. We have a kudos Slack channel where I love posting my team&#39;s small and big wins. Encouragement and positivity is infectious.&lt;/p&gt;
&lt;h2 id=&quot;be-understanding&quot;&gt;Be understanding&lt;/h2&gt;&lt;p&gt;We&#39;re all human. Life is never easy and we all face challenges in our own lives, especially in 2020 and 2021. Have regular 1:1 meetings with your team and be an active listener. Accept what they&#39;re willing to share with you and share your own life, as much as you both feel comfortable with. It&#39;ll help you both to understand each other and build a positive relationship. Feeling understood will help them to feel accepted as part of the team.&lt;/p&gt;
&lt;h2 id=&quot;let-everyone-feel-heard&quot;&gt;Let everyone feel heard&lt;/h2&gt;&lt;p&gt;To make everyone feel included they must feel heard. Ensure you enable every to voice their opinion in your meetings. Don&#39;t only allow those with the most confidence or loudest voices to speak. Lift everyone&#39;s opinions so that they feel part of the team, even if you can&#39;t always act upon everyone&#39;s views. As &lt;a href=&quot;https://simonsinek.com/&quot;&gt;Simon Sinek&lt;/a&gt; quite nicely puts it, &lt;a href=&quot;https://www.youtube.com/watch?v=3EPLItTf-QU&quot;&gt;&amp;quot;be the last to speak&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;multiply-the-makers&quot;&gt;Multiply the makers&lt;/h2&gt;&lt;p&gt;As a team lead, your job is to multiply the makers on your team. As a technical leader, your job isn&#39;t to always know the right answer. It&#39;s to find the answer by allowing your team to be the experts that you know they are. See the potential in your team and help to amplify their voices. You&#39;ll have much more impact as a leader and it&#39;ll bring your whole team along on the journey with you.&lt;/p&gt;
&lt;h2 id=&quot;build-a-learning-environment&quot;&gt;Build a learning environment&lt;/h2&gt;&lt;p&gt;I love learning. It&#39;s something I actively try and encourage my children to understand. Every day should be a day of discovery in finding something new to learn about. Create learning opportunities for your team. I run a weekly team learning session where I mentor some of my less experienced team members to increase their knowledge and understanding of both programming and our team&#39;s domain expertise. I share my own knowledge and help to bring them up too. It&#39;s not a &lt;a href=&quot;https://en.wikipedia.org/wiki/Zero-sum_game&quot;&gt;zero-sum game&lt;/a&gt;. By sharing what I know, I&#39;m helping the team and in turn, they&#39;ll teach someone else. I&#39;ve learnt so much more by teaching my team and it&#39;s helped to lift me up too.&lt;/p&gt;
&lt;h2 id=&quot;share-your-network&quot;&gt;Share your network&lt;/h2&gt;&lt;p&gt;You can lift the whole team up by &lt;a href=&quot;https://www.marclittlemore.com/share-your-network/&quot;&gt;sharing your network&lt;/a&gt;, especially if you&#39;ve been at a company for a while. You&#39;ll have gained a reputation, and hopefully a good one. Use this to sponsor members of your team. Introduce them to other people they might not normally meet and create those opportunities for career growth. Lend your privilege to others who may not have it yet. A short introduction to another team might not mean much to you, but it&#39;ll do wonders for the confidence of your engineer who is being raised up to represent your team.&lt;/p&gt;
&lt;h2 id=&quot;inspire-the-team&quot;&gt;Inspire the team&lt;/h2&gt;&lt;p&gt;Inspiration is a funny term and I often struggle with it and the idea of being inspirational. You should strive to inspire your team to do their best work by aspiring to do some or all of the things I&#39;ve talked about above. You can share the history of why your team has created the application architecture that you have. It can inspire them that they can help to build similar too. You can also set the vision of the future and be an inspiration for what could be. By listening to your team, you can help them to build the team&#39;s vision for the future too.&lt;/p&gt;
&lt;h2 id=&quot;stay-humble&quot;&gt;Stay humble&lt;/h2&gt;&lt;p&gt;You might not realise how much you help the team by lifting them up. Your accomplishments will mean a lot to the team but remember to be happy to do so without a fuss. Sometimes you might not even realise you&#39;re doing a great job until somebody tells you so.&lt;/p&gt;
&lt;p&gt;Build that positive environment and keep on helping to lift the team&#39;s boats.&lt;/p&gt;
&lt;p&gt;The rewards will also come to you too.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>It's not too late to start</title>
    
    <link href="https://www.marclittlemore.com/its-not-too-late-to-start/"/>
    <published>2021-01-26T00:00:00Z</published>
    
    <updated>2021-01-26T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/its-not-too-late-to-start/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I once found a &lt;a href=&quot;https://www.smbc-comics.com/index.php?db=comics&amp;amp;id=2722&quot;&gt;wonderful comic&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/ZachWeiner&quot;&gt;Zach Weinersmith&lt;/a&gt; of Saturday Morning Breakfast Cereal (SMBC). I&#39;ve told a lot of people about it so please go and read it now.&lt;/p&gt;
&lt;p&gt;It states that most people think that they only live once and spend their life thinking they can only have one career. But if it takes around 7 years to master a skill, you&#39;ve actually got 11 opportunities to do something from age 11 to 88.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It&#39;s not too late to start.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I spent my early career as a programmer writing video games. That&#39;s the part of the technology industry I knew best for nearly 20 years. I never thought I&#39;d need to think about any other technology. And then I got made redundant in 2010.&lt;/p&gt;
&lt;p&gt;After a couple of years of contracting in the games industry, I started developing web-based technologies like API services. It was something I knew very little about, but I &lt;a href=&quot;https://www.marclittlemore.com/learning-to-learn-software-development/&quot;&gt;kept learning&lt;/a&gt;. I showed up every day and learnt a little bit more than I had the day before. This new knowledge helped me to get a job at the BBC.&lt;/p&gt;
&lt;p&gt;I was using programming languages I&#39;d never touched before, on platforms I knew nothing about. I watched graduate programmers talk about things I knew nothing about and had never previously needed to. But I carried on learning and while my job was still in technology, I started a new career in web services and cloud technologies.&lt;/p&gt;
&lt;p&gt;I had reinvented myself. I&#39;d started again.&lt;/p&gt;
&lt;p&gt;I see a lot of people online asking if it&#39;s too late to become a software developer. They&#39;re often in another career, or think they&#39;re too old. Just remember:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It&#39;s not too late to start.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The BBC ran a fantastic programme of work called &lt;a href=&quot;https://www.bbc.com/backstage/design-engineering/new-and-diverse-talent#stepintotechscheme&quot;&gt;Step Into Tech&lt;/a&gt; in 2018 and 2019. It encouraged women to sign up for a 12-week training course to have the opportunity to learn some fundamental skills to kick-start their career in the technology sector. All of the women were of differing ages and most had no background in software engineering. It was great to see them move from their varied careers such as working for the NHS, working in music production, and teaching, to reinvent themselves as software developers. They&#39;re all doing wonderfully well now and have yet again proved that:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It&#39;s not too late to start.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So what do you need to do to start?&lt;/p&gt;
&lt;p&gt;Turn up. Start doing. And be consistent.&lt;/p&gt;
&lt;p&gt;Am I a good writer? Probably not yet. But 12 days ago I decided to turn up and &lt;a href=&quot;https://www.marclittlemore.com/creating-a-writing-habit/&quot;&gt;start writing every day&lt;/a&gt;. By showing up every day, I&#39;ve written about 14,000 words. Not all of them are great I&#39;m sure. But I started.&lt;/p&gt;
&lt;p&gt;I appreciate that this advice is easy to give but hard to follow. Consistency is hard inbetween your work and family life. If you can set some time aside to work on your goals, then:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It&#39;s not too late to start.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And finally, I love this video from &lt;a href=&quot;https://twitter.com/seanwes&quot;&gt;Sean Wes&lt;/a&gt;. It&#39;s called &amp;quot;Show up every day for 2 years&amp;quot;. Follow his advice and you can&#39;t lose.&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=pUhnIWUTTts&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Learning to learn about software development</title>
    
    <link href="https://www.marclittlemore.com/learning-to-learn-software-development/"/>
    <published>2021-01-25T00:00:00Z</published>
    
    <updated>2021-01-25T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/learning-to-learn-software-development/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;One of my team asked me a fantastic question today.&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;“How do I stay on top of learning about software development technologies and best practices?”&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;There&#39;s an information overload in the software development world. Every day there&#39;s a new framework, a new library, a new technology stack, or a new programming language. It feels like you have to learn everything, especially if you&#39;re not an experienced developer yet.&lt;/p&gt;
&lt;p&gt;But you don&#39;t!&lt;/p&gt;
&lt;p&gt;So how do you manage to learn new skills when there&#39;s a massive influx of articles and videos competing for your attention?&lt;/p&gt;
&lt;p&gt;My day-to-day role is as a software engineering team lead. This means I&#39;m not as hands-on with development work as I used to be. I have to pick and choose what I have time to learn and, with the exception of our team&#39;s 10% time (&lt;a href=&quot;https://en.wikipedia.org/wiki/20%25_Project&quot;&gt;similar to Google&#39;s 20% time&lt;/a&gt;), most of my learning is done after work in the evenings or at weekends.&lt;/p&gt;
&lt;h2 id=&quot;knowing-what-to-learn&quot;&gt;Knowing what to learn&lt;/h2&gt;&lt;p&gt;I&#39;m somewhat addicted to Twitter, although I try my best not to be. This is my first port of call when it comes to ideas for new technologies to investigate. You can follow some interesting developers and technologists on Twitter but it&#39;s sometimes difficult to keep up with your Twitter timeline if you follow too many people. Alternatively, you can create and follow lists of people, or follow lists that others have made. This is a great way to expose yourself to new people and their ideas. Don&#39;t forget to keep your ideas diverse by following more than just people who look and think like you or who use the same technologies.&lt;/p&gt;
&lt;p&gt;Another great source of new technologies are technology aggregators such as &lt;a href=&quot;https://news.ycombinator.com/&quot;&gt;Hacker News&lt;/a&gt; (don&#39;t read the comments!), &lt;a href=&quot;https://dev.to/&quot;&gt;Dev.to&lt;/a&gt;, &lt;a href=&quot;https://www.reddit.com/&quot;&gt;Reddit&lt;/a&gt;, and &lt;a href=&quot;https://hashnode.com/explore&quot;&gt;Hashnode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also watch a lot of YouTube videos. YouTube is a fantastic search engine but by also following the right people, its algorithm will also suggest other technology videos you might be interested in. I enjoy watching &lt;a href=&quot;https://www.youtube.com/c/AngularFirebase&quot;&gt;Fireship&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/channel/UCy1H38XrN7hi7wHSClfXPqQ&quot;&gt;Adam Wathan&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/channel/UCP4bf6IHJJQehibu6ai__cg&quot;&gt;Firebase&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/channel/UCnUYZLuoy1rq1aVMwx4aTzw&quot;&gt;Google Chrome Developers&lt;/a&gt; and many more.&lt;/p&gt;
&lt;p&gt;You&#39;ll find plenty to read on Twitter, YouTube, and these sites and it&#39;ll help you to discover &lt;strong&gt;what&lt;/strong&gt; you&#39;re interested in learning.&lt;/p&gt;
&lt;h2 id=&quot;scratch-your-own-itch&quot;&gt;Scratch your own itch&lt;/h2&gt;&lt;p&gt;I always like to investigate new ideas and technologies by &amp;quot;scratching my own itch&amp;quot;. This means that I have an idea for something which solves a current problem I have and it allows me to learn whilst I&#39;m implementing a solution.&lt;/p&gt;
&lt;p&gt;For example, my wife was watching a series of online webinars that were only available for the next 5 days. She had no time to watch them so wondered if I could download the videos so she could watch them later. The videos were hosted by &lt;a href=&quot;https://wistia.com/&quot;&gt;Wistia&lt;/a&gt; so I investigated how I could find the source video files by digging through the websites source code and discovering how the video was embedded. As I needed to download multiple videos, I wondered if I could automate the process through a command line tool. This took me off on a voyage of discovery as I scraped a webpage, used regular expressions to parse script tags to pull out JSON data, and worked out which hidden binary file URL to download. This led me to Node.js streams, which I&#39;d not used before with any force, to stream the video file and write it to disk. All of this from a simple question from my wife!&lt;/p&gt;
&lt;p&gt;As you can see, working a project that you&#39;re interested in can open up a world of learning opportunities.&lt;/p&gt;
&lt;h2 id=&quot;open-source-github-repositories&quot;&gt;Open source GitHub repositories&lt;/h2&gt;&lt;p&gt;Once you&#39;ve discovered what you want to learn it&#39;s a good idea to dig into other people&#39;s code. When I&#39;m thinking about functionality I need for a project, I have to decide whether it&#39;s worth writing the code myself or exploring an open source library which might do what I need.&lt;/p&gt;
&lt;p&gt;If it&#39;s an open source library I&#39;m using, I quickly jump to their GitHub, GitLab, or BitBucket repository. A well-written README file can teach you a lot about the architecture and design of their library. Once I&#39;ve got an idea of how it works, I&#39;ll attempt to use the library in my project to understand their public API. If I struggle to get something working, now is a good time to jump into the code. The great thing about open source projects is that their code is available for you to read. Take a look at the code modules and work out the code path for the action that you&#39;re attempting. You&#39;ll fix your own bug and learn how their code is designed.&lt;/p&gt;
&lt;h2 id=&quot;take-notes-and-share-them&quot;&gt;Take notes and share them&lt;/h2&gt;&lt;p&gt;As I&#39;ve mentioned previously, I take a lot of notes using &lt;a href=&quot;https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/&quot;&gt;Obsidian&lt;/a&gt;. I use this to build up a list of technologies, languages or frameworks I&#39;d like to learn as I discover them. Every few weeks I revisit my notes to see what I want to learn next. As I start using something new, I take notes as I&#39;m learning and note down any problems I&#39;ve encountered, other libraries that they use, or how they&#39;re structured. This helps me to make my own technical decisions for my code and allows me to expand my area of knowledge in new directions.&lt;/p&gt;
&lt;p&gt;Once you&#39;ve done that, try and write up your notes so that you can &lt;a href=&quot;https://www.marclittlemore.com/learn-in-public/&quot;&gt;learn in public&lt;/a&gt;. You might not know all of the details yet but you&#39;re one step ahead of someone who&#39;s not yet discovered it. Why not write up some notes on Twitter or if you have a personal website, write a blog post about it.&lt;/p&gt;
&lt;h2 id=&quot;just-in-time-learning&quot;&gt;Just-in-time learning&lt;/h2&gt;&lt;p&gt;If you&#39;re anything like me, you&#39;ve probably now got hundreds of ideas of what you want to build. But you can&#39;t learn everything. You have to be ruthless with your time, especially if you have other commitments like a partner or family. I try and employ just-in-time learning. I don&#39;t go off and attempt to become an expert in a technology that I don&#39;t currently need. I learn it when I need it for my job or my side projects.&lt;/p&gt;
&lt;p&gt;You can&#39;t learn everything so gain your knowledge at the right time. Before you know it, you&#39;ve learnt an awful lot more than you thought.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Add YouTube playlists to your Eleventy site</title>
    
    <link href="https://www.marclittlemore.com/add-youtube-playlists-to-eleventy-site/"/>
    <published>2021-01-24T00:00:00Z</published>
    
    <updated>2021-01-24T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/add-youtube-playlists-to-eleventy-site/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Over the past couple of years I&#39;ve been moving my personal websites from using WordPress to simple static HTML sites. WordPress is fantastic but I spent too much time being concerned about security risks, updating to new versions, and worrying about managing PHP versions on a server. These are things I didn&#39;t want to have to concern myself with so I wanted to use a &lt;a href=&quot;https://www.netlify.com/blog/2020/04/14/what-is-a-static-site-generator-and-3-ways-to-find-the-best-one/&quot;&gt;static site generator&lt;/a&gt; to build plain HTML, CSS, and a little bit of client-side JavaScript where necessary. I discovered &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; back in early 2020 and this has quickly become my tool of choice.&lt;/p&gt;
&lt;p&gt;My &lt;a href=&quot;https://www.marclittlemore.com/bots/&quot;&gt;chatbots&lt;/a&gt; page has a list of YouTube videos on it. Initially this was a static list of YouTube video IDs that I iterated over to generate the markup. I realised that using Eleventy&#39;s great data flow, I could automate this using the YouTube API. This means that I only need to regenerate the site when I add new videos to the playlist and I don&#39;t have to cut and paste a YouTube video ID into a markdown file each time. This makes things much simpler.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at how to do this using Eleventy&#39;s data files.&lt;/p&gt;
&lt;h2 id=&quot;obtaining-youtube-api-credentials&quot;&gt;Obtaining YouTube API credentials&lt;/h2&gt;&lt;p&gt;I&#39;m assuming that you have a &lt;a href=&quot;https://www.google.com/accounts/NewAccount&quot;&gt;Google account&lt;/a&gt;. Without this you can&#39;t access the developer console to register an application or generate a new API key for the YouTube API.&lt;/p&gt;
&lt;p&gt;Sign into your account and head to the &lt;a href=&quot;https://console.developers.google.com/&quot;&gt;Google Developers Console&lt;/a&gt;. Create a new project for your website, or use an existing project if you have one.&lt;/p&gt;
&lt;p&gt;You must &lt;a href=&quot;https://developers.google.com/youtube/registering_an_application&quot;&gt;obtain authorisation credentials&lt;/a&gt; in order to generate an API key. Open the &lt;a href=&quot;https://console.developers.google.com/apis/credentials&quot;&gt;credentials page&lt;/a&gt; in the Google console and click &amp;quot;Create credentials&amp;quot; to create a new API key.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/google-api-key.jpg&quot; alt=&quot;API key&quot;&gt;&lt;/p&gt;
&lt;p&gt;After creating the key you must enable the YouTube Data API (v3) for this API key. Go to the &lt;a href=&quot;https://console.developers.google.com/apis/library&quot;&gt;developer console here&lt;/a&gt; and enable it. Ensure that you save your API key somewhere safe so we can use it to access the YouTube API later.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;The playlist we will attempt to read is public and so we can use an API key to retrieve this data. If you require a user&#39;s private data, such as a private YouTube playlist, then you need to use an &lt;a href=&quot;https://developers.google.com/identity/protocols/oauth2&quot;&gt;OAuth 2.0&lt;/a&gt; token with your request.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;add-your-api-key-locally&quot;&gt;Add your API key locally&lt;/h2&gt;&lt;p&gt;API keys should always be kept safe. If you expose it publicly then others can use it to make requests against your Google account. To avoid this, we&#39;re going to store the API key as an &lt;a href=&quot;https://en.wikipedia.org/wiki/Environment_variable&quot;&gt;environment variable&lt;/a&gt;. We can then use this in our YouTube playlist fetcher without exposing the key to anyone.&lt;/p&gt;
&lt;p&gt;In Node.js, a great way to expose environment variables to your application is using the &lt;a href=&quot;https://www.npmjs.com/package/dotenv&quot;&gt;dotenv&lt;/a&gt; &lt;code&gt;npm&lt;/code&gt; package. This allows us to create a &lt;code&gt;.env&lt;/code&gt; file which contains all of our secret keys.&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;dotenv&lt;/code&gt; as a development dependency for your Eleventy project:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt; dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should attempt to require &lt;code&gt;dotenv&lt;/code&gt; as early as you can in your JavaScript project. For Eleventy, this means you can add to the top of your &lt;code&gt;.eleventy.js&lt;/code&gt; coniguration file.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;dotenv&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The rest of your Eleventy configuration goes below&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Your Eleventy plugins and so on&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in the root of your project and add in your YouTube API key.&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Add your secret API key&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;YOUTUBE_API_KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;my&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;youtube&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;api&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;key&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When your Eleventy build is executed, you&#39;ll have access to the API key using the Node process environment variables like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myApiKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;YOUTUBE_API_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now use this exposed API key inside our data files to read a YouTube playlist.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-red-100 border-red-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-red-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm32 224c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;Make sure you add the &lt;code&gt;.env&lt;/code&gt; file to your &lt;code&gt;.gitignore&lt;/code&gt; file so that you don&#39;t accidentally add secret keys to your git repository.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;eleventy-data-files&quot;&gt;Eleventy data files&lt;/h2&gt;&lt;p&gt;One of the super powers of Eleventy is that it allows you to retrieve data in multiple ways. This can be data in your markdown frontmatter, data in a layout directory, template data, data directories, and &lt;a href=&quot;https://www.11ty.dev/docs/data/&quot;&gt;more&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I often use static global data as JSON files in my &lt;code&gt;_data&lt;/code&gt; directory but you can also use &lt;a href=&quot;https://www.11ty.dev/docs/data-js/&quot;&gt;JavaScript files&lt;/a&gt; to generate it programatically. Using this method, we export a single function which gets called by Eleventy as it builds are site. One of the great features of using this method is that we can use asynchronous functions to retrieve data from external sources. Let&#39;s look at how we can use this to load a public playlist using the YouTube API.&lt;/p&gt;
&lt;p&gt;Install the Google APIs Node.js package as a development dependency. We&#39;ll use this to call the YouTube API without having to write our own HTTP requests and authentication.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt; googleapis&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a file in your &lt;code&gt;_data&lt;/code&gt; directory called &lt;code&gt;youTubeVideos.js&lt;/code&gt;. The name of the file will match the name of the global object available to your Eleventy build.&lt;/p&gt;
&lt;p&gt;We&#39;ll require the Google APIs package and create a YouTube client to use to retrieve the data we want from the YouTube API. It can be used for any public API call and not just for playlists.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Require the Google API package&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;google&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;googleapis&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Create a YouTube service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; youTubeClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;youtube&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Use the v3 API&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;v3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Don&#39;t hardcode your API key&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Read it from an environment variable&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;YOUTUBE_API_KEY&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have the YouTube client, let&#39;s create a function which retrieves the data we want from our playlist. We can then expose it to Eleventy using &lt;code&gt;module.exports&lt;/code&gt;. We will use the YouTube client we created earlier to calll the YouTube &lt;a href=&quot;https://developers.google.com/youtube/v3/docs/playlistItems/list&quot;&gt;playlistItems endpoint&lt;/a&gt; and list all of the videos for a specified playlist. The API expects you to tell it what fields you want to retrieve. For this example we&#39;ll retrieve the &lt;code&gt;id&lt;/code&gt; and the &lt;code&gt;snippets&lt;/code&gt; (title, description, position, resourceId).&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Note that we&#39;re exposing an asynchronous function&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Eleventy will wait for this to finish before exposing the data&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// to the rest of our code&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Call the YouTube API using the client we created earlier&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; youTubeClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playlistItems&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Pass your own public playlist ID here&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;playlistId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;your-public-playlist-id&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Tell it what data you want to retrieve&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;id,snippet&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Maximum results you require from 0 to 50&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;maxResults&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Return an array of video items&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Log any errors so we can see them&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Allow the build to work but without video data&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the API call is successful then we&#39;ll be given the playlist data in &lt;code&gt;response.data&lt;/code&gt;. This will contain an array of &lt;code&gt;items&lt;/code&gt; which we&#39;ll return to Eleventy. Notice that I&#39;ve added error checking to the code. If there&#39;s an error then we can log it out so we&#39;ll see it on our command line. I also return an empty array so that our Eleventy build won&#39;t just stop due to an error referencing &lt;code&gt;null&lt;/code&gt; data.&lt;/p&gt;
&lt;p&gt;In the example, I&#39;m only attempting to read the first 50 videos in the playlist. This is the maximum you can retrieve in 1 call. The response will return you a &lt;code&gt;nextPageToken&lt;/code&gt; if more results are available. In that case, you can call the API again and pass a &lt;code&gt;page: nextPageToken&lt;/code&gt; key-value pair in the options passed to the API. Keep repeating this process to retrieve all of the results until you no longer receive a &lt;code&gt;nextPageToken&lt;/code&gt; which means you&#39;ve received all results.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;Note that the YouTube API has a quota of 10,000 units per day and each operation has a different cost associated with it, depending on what operation you&#39;re doing it. For most uses, this is more than enough but &lt;a href=&quot;https://developers.google.com/youtube/v3/getting-started#quota&quot;&gt;read more here&lt;/a&gt; in case you think you might have a high volume use case.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;using-the-data-in-your-template&quot;&gt;Using the data in your template&lt;/h2&gt;&lt;p&gt;Now that we&#39;ve retrieved the data, how do we use it?&lt;/p&gt;
&lt;p&gt;As we named the data file &lt;code&gt;youTubeVideos.js&lt;/code&gt;, we now have a global variable called &lt;code&gt;youTubeVideos&lt;/code&gt; which matches the name of the data file. This variable is exposed to our templates so we can iterate over the data in it to render each YouTube video link from the playlist.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example using a &lt;a href=&quot;https://www.11ty.dev/docs/languages/liquid/&quot;&gt;Liquid&lt;/a&gt; template.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{% for video in youTubeVideos %}
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://www.youtube.com/watch?v={{video.snippet.resourceId.videoId}}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;_blank&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;noreferrer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{video.snippet.thumbnails.high.url}}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{video.snippet.title}}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Title&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{video.snippet.title}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
{% endfor %}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take a look at the &lt;a href=&quot;https://www.marclittlemore.com/bots/&quot;&gt;chatbots&lt;/a&gt; page to see it in action.&lt;/p&gt;
&lt;h2 id=&quot;deployment-to-your-environment&quot;&gt;Deployment to your environment&lt;/h2&gt;&lt;p&gt;Now that we&#39;ve generated the YouTube playlist as a data file and used it in our local build, we mustn&#39;t forget to add the &lt;code&gt;YOUTUBE_API_KEY&lt;/code&gt; environment variable to our production environment. I use &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; for my production builds and you add this easily using their &lt;a href=&quot;https://docs.netlify.com/configure-builds/environment-variables/&quot;&gt;site settings&lt;/a&gt;. For other deployment platforms, such as &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt; or &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub pages&lt;/a&gt; you can add enviroment variables in their settings too. Just ensure you&#39;ve added the key prior to the build process or you won&#39;t see your videos.&lt;/p&gt;
&lt;p&gt;Hopefully that&#39;s given you a good idea of how to use the YouTube API with Eleventy. To speed up your development build, and avoid calling the API repeatedly, you could look to add a &lt;a href=&quot;https://www.11ty.dev/docs/quicktips/cache-api-requests/&quot;&gt;cache&lt;/a&gt; to save the playlist data locally. This isn&#39;t a necessity but it will speed up the build.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Build simple software</title>
    
    <link href="https://www.marclittlemore.com/build-simple-software/"/>
    <published>2021-01-21T00:00:00Z</published>
    
    <updated>2021-01-21T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/build-simple-software/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Nobody sets to build complex software.&lt;/p&gt;
&lt;p&gt;All developers set out with best laid plans to keep applications simple. We separate the concerns of the code with nice APIs and interfaces. We abstract our business logic away from our domain services. It&#39;s as simple as we can make it but over time the code gets more complex. But why?&lt;/p&gt;
&lt;p&gt;There are times when developers over-engineer a problem. We add &lt;a href=&quot;https://en.wikipedia.org/wiki/Design_Patterns&quot;&gt;Gang of four&lt;/a&gt; design patterns to the code because we care about separations of concern but often these layers of factories on top of facades on top of proxies make our code much more difficult to read. Code complexity is not always driven by the software engineers working on the code. There are plenty of reasons why it&#39;s not only a technical issue.&lt;/p&gt;
&lt;p&gt;Sofware isn&#39;t useful until it provides business value and is in front of users. There are often time constraints and deadlines so new features are added to the application in a short time frame. If external teams integrate with your services, there are often requirements from yet more stakeholders. Software is written with the best knowledge you have at the time of writing it. Software architecture changes over time as new requirements appear. The requirements of the application are based on the needs of a product team, the user experience (UX) team, and the developers working on it. Over time the engineering team may change and steer the architecture in different directions. As a software developer you&#39;ll always be learning and discover new patterns which make more sense. Ultimately, all software will rot and incur &lt;a href=&quot;https://www.marclittlemore.com/refactoring-code-broken-windows-theory/&quot;&gt;technical debt&lt;/a&gt;. There&#39;s no getting around this so we have to put plans in place to make it easier.&lt;/p&gt;
&lt;p&gt;So what can we as software developers do?&lt;/p&gt;
&lt;h2 id=&quot;how-do-we-reduce-software-complexity&quot;&gt;How do we reduce software complexity?&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/rossalexwilson&quot;&gt;Ross&lt;/a&gt;, the principal engineer on my team at the BBC, loves this quote from well known software engineer &lt;a href=&quot;https://en.wikipedia.org/wiki/Kent_Beck&quot;&gt;Kent Beck&lt;/a&gt; and I think this is applicable in software design.&lt;/p&gt;
&lt;p&gt;https://twitter.com/KentBeck/status/250733358307500032&lt;/p&gt;
&lt;p&gt;In this instance, Kent was referring to refactoring your code but it also applies to the design of your software. You simplify the design of your code such that you can lay simpler foundations for future requirements.&lt;/p&gt;
&lt;p&gt;Simplicity in code is not necessarily easy to achieve but there are some ways we can help to point us in the right direction.&lt;/p&gt;
&lt;h2 id=&quot;make-it-human-readable&quot;&gt;Make it human readable&lt;/h2&gt;&lt;p&gt;It&#39;s easy to write more code than you need to and overcomplicate your software. Try to eliminate unnecessary code where possible but not to the point of losing readibility. As developers we often spend more time reading code than writing it, especially if it&#39;s not a new project. Optimise for making this easy.&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;p&gt;&lt;strong&gt;&amp;quot;There are only two hard things in Computer Science: cache invalidation and naming things.&amp;quot;&lt;/strong&gt; - &lt;a href=&quot;https://www.karlton.org/2017/12/naming-things-hard/&quot;&gt;Phil Karlton&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;People underestimate how important it is to clearly name your variables and functions. If you can read the code like you&#39;re reading it set of instructions, it means you&#39;ve done a good job. If there&#39;s a potential to cause confusion in what the code does, consider simplifying the names. If you agree on naming conventions with your team, this makes life simpler.&lt;/p&gt;
&lt;h2 id=&quot;easily-testable&quot;&gt;Easily testable&lt;/h2&gt;&lt;p&gt;If you follow the concept of &lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;test-driven development&lt;/a&gt; (TDD) then you will be writing your tests before your code. This naturally leads you to only writing the code you need to pass the tests. Even if you&#39;re not following strict TDD and writing your tests afterwards, if you find that the code is difficult to test, then you&#39;ve perhaps you need to simplify the code to make this easier.&lt;/p&gt;
&lt;h2 id=&quot;separation-of-concerns&quot;&gt;Separation of concerns&lt;/h2&gt;&lt;p&gt;You should look at removing complexity by separating your code modules into their areas of concern. For example, if you&#39;re writing an API service, move your business logic away from your HTTP transport layer. You don&#39;t want to mix database calls and HTTP JSON responses together. Think about how you can isolate your code into only the areas that it should be concerned about. It simplifies the application and will also make testing easier.&lt;/p&gt;
&lt;h2 id=&quot;dont-repeat-yourself-but-within-reason&quot;&gt;Don&#39;t repeat yourself - but within reason&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;&amp;quot;don&#39;t repeat yourself&amp;quot; (DRY)&lt;/a&gt; software principal is the idea that looks to reduce the repetition of code. If you start to repeat the same block of code multiple times, then look to abstract it out into its own function or class. However, be wary or &lt;a href=&quot;https://en.wikipedia.org/wiki/Program_optimization&quot;&gt;premature optimisation&lt;/a&gt;. You might find that the two pieces of code actually have different business reasons for existing. You don&#39;t want to refactor into a single function which ends up with branching business logic inside it. My rule of thumb for DRY is to see if the code is used a 3rd time. Once that&#39;s the case, I&#39;ll usually refactor it to a shared function.&lt;/p&gt;
&lt;h2 id=&quot;reduce-your-dependencies&quot;&gt;Reduce your dependencies&lt;/h2&gt;&lt;p&gt;It&#39;s really easy to search the JavaScript &lt;a href=&quot;https://npmjs.com/&quot;&gt;npm&lt;/a&gt; registry, or the equivalent registry for other programming languages, to add some open-source code to your project. Be mindful of additional dependencies you&#39;re adding to your software by doing so. While some of the packages might be useful, it adds a layer of complexity in code that you and your team haven&#39;t written. Try not to import packages just because you can and only use them when you need to.&lt;/p&gt;
&lt;h2 id=&quot;avoid-developing-for-edge-cases&quot;&gt;Avoid developing for edge cases&lt;/h2&gt;&lt;p&gt;During tight deadlines, it&#39;s often easy to fix issues by adding special cases into your functions. Code which branches based upon state passed into the function, or even worse, based on global state. It&#39;s a slippery slope if you start adding these edge cases to your code. It&#39;s much harder to determine what a function does once there are more branches of logic to follow. Attempt to keep your code &lt;a href=&quot;https://en.wikipedia.org/wiki/Functional_programming&quot;&gt;functional&lt;/a&gt; and avoid the need for global state.&lt;/p&gt;
&lt;p&gt;As an example, about 20 years ago, I was involved in writing a AAA videogame. Right before mastering the finished DVD, we discovered issues with the final boss battle that I had developed the AI for. The main character had a move which was too powerful and would kill the final foe with one hit. This hadn&#39;t been thought of from the start so I was asked to add a line of code which removed the special move for this level only. So I had to add an additional &lt;code&gt;if&lt;/code&gt; statement to check the global level state and remove the special move for this level only. This was in the main characters input code. 🤦🏻‍♂️&lt;/p&gt;
&lt;p&gt;Don&#39;t do this!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;There&#39;s no silver bullet for writing simple code. You have to think about your architecture before hitting the keyboard. Make time to build a good foundation for your application architecture. Allow the team to think about good coding practices and don&#39;t try and cut corners. Make sure you build in contingency for refactoring tech debt as the requirements change.&lt;/p&gt;
&lt;p&gt;And remember to &lt;a href=&quot;https://en.wikipedia.org/wiki/KISS_principle&quot;&gt;Keep It Simple Stupid&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful resources&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;A great &lt;a href=&quot;https://github.com/kettanaito/naming-cheatsheet&quot;&gt;naming cheatsheet&lt;/a&gt; on how to get better at naming variables and functions in your code&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Stop overthinking everything</title>
    
    <link href="https://www.marclittlemore.com/stop-overthinking-everything/"/>
    <published>2021-01-20T00:00:00Z</published>
    
    <updated>2021-01-20T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/stop-overthinking-everything/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Some days I can&#39;t shut my brain off.&lt;/p&gt;
&lt;p&gt;I often worry about something that happened at work or home. Sometimes I remember a situation that happened in the mid-90s when I was DJing in a nightclub. Yes, this regularly happens and I&#39;m always surprised I can remember that far back! Today I&#39;ve been worrying whether the last 7 articles I&#39;ve written for my &lt;a href=&quot;https://www.marclittlemore.com/creating-a-writing-habit/&quot;&gt;writing habit challenge&lt;/a&gt; are good enough or not. I can&#39;t stop thinking about how I could have approached a situation differently and changed the outcome.&lt;/p&gt;
&lt;p&gt;It can be mentally exhausting.&lt;/p&gt;
&lt;p&gt;I&#39;m sure I&#39;m no different to many people in being an overthinker but I wonder how you can challenge your brain to organise the good thoughts and solve problems, rather than being bogged down with things you can&#39;t affect.&lt;/p&gt;
&lt;p&gt;Overthinking can take many forms. It can be thinking too much about the past situations that you could have changed. It can be worrying about future events and how they will play out. For me it&#39;s often deliberating when attempting to make a decision on something, and then questioning the decision once I&#39;ve made it. Just ask my wife &lt;a href=&quot;https://clarelittlemore.com/&quot;&gt;Clare&lt;/a&gt; how long it takes me to buy anything! It can also build a running commentary in your head, putting words into others mouths in a fictional situation. Inevitably, you think you know how the conversation will end and in your own mind that&#39;s usually badly.&lt;/p&gt;
&lt;p&gt;Overthinking isn&#39;t problem solving to find a solution. It&#39;s dwelling on the problem itself and not getting beyond that.&lt;/p&gt;
&lt;h2 id=&quot;how-can-we-deal-with-overthinking&quot;&gt;How can we deal with overthinking?&lt;/h2&gt;&lt;p&gt;I&#39;m trying to get better at scheduling my downtime to stop my brain from spinning. I used to spend too many hours in front of a laptop after 10 pm at night. Wherever possible, I put the computer and phone away to try and allow my thinking to slow down. I&#39;ve also tried to read more in bed as it&#39;s a much nicer way to end the day and helps me to avoid thinking too much about what&#39;s happened.&lt;/p&gt;
&lt;p&gt;Some research suggests, as strange as it sounds, that you &lt;a href=&quot;https://www.inc.com/amy-morin/the-simple-but-effective-way-to-stop-worrying-so-much-it-sounds-ridiculous-but-it-actually-works.html&quot;&gt;schedule your worries&lt;/a&gt; into a set time each day. I&#39;ve not tried it myself but sounds like an interesting idea.&lt;/p&gt;
&lt;p&gt;A collegue at work ran a workshop which referenced Steven Covey&#39;s classic book &lt;a href=&quot;https://www.franklincovey.com/the-7-habits/&quot;&gt;&amp;quot;The 7 Habits of Highly Effective People&amp;quot;&lt;/a&gt;. There&#39;s an idea of the &lt;a href=&quot;https://www.thensomehow.com/circles-of-influence/&quot;&gt;circle of influence and control&lt;/a&gt;. I found it&#39;s a good way to rationalise what you can and can&#39;t control and where you can influence situations. There&#39;s little point in thinking about the your company&#39;s policies, unless you&#39;re on the board of directors, as it&#39;s difficult to change these situations.&lt;/p&gt;
&lt;p&gt;Noticing that you&#39;re overthinking is a good thing. Once you realise that you&#39;re stuck on the same thoughts, you can move your mind to something else. Try and steer your thinking away from the bad and think about what great things you could do at the weekend instead.&lt;/p&gt;
&lt;p&gt;Move from dwelling on a problem to suggesting potential actions to solve it instead. If it&#39;s something you can control, see if you can come up with one or more solutions which you can explore instead. If it&#39;s something that&#39;s not within your circle of influence, try and move it aside and focus on areas you can control.&lt;/p&gt;
&lt;p&gt;Remember that overthinking is often your brain over-exaggerating towards the negative. Try and step back and remember a similar situation that&#39;s happened before. The chances are that it didn&#39;t turn out as bad as you thought. I know this is hard as I struggle with this a lot but it does help.&lt;/p&gt;
&lt;p&gt;So yes, I&#39;m trying to get better and stop overthinking.&lt;/p&gt;
&lt;p&gt;To realise that things in the past can&#39;t change.&lt;/p&gt;
&lt;p&gt;That things in the future haven&#39;t happened yet.&lt;/p&gt;
&lt;p&gt;I&#39;m trying to take more time out to reflect and meditate, especially with the current pandemic that we&#39;re all living through.&lt;/p&gt;
&lt;p&gt;Stay safe and keep thinking of the positives instead of dwelling on the negatives.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.headspace.com/blog/2017/06/01/stop-overthinking-start-living/&quot;&gt;Stop overthinking and start living&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.inc.com/amy-morin/10-signs-you-think-too-much-and-what-you-can-do-about-it.html&quot;&gt;10 signs you think too much&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/kaizen-habits/psychologists-explain-how-to-stop-overthinking-everything-e527962a393&quot;&gt;Psychologists explain how to stop overthinking everything&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.habitsforwellbeing.com/the-circle-of-concern-and-influence/&quot;&gt;The circle of concern and influence&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Create a serverless chatbot for Telegram using Vercel - the ultimate guide</title>
    
    <link href="https://www.marclittlemore.com/serverless-telegram-chatbot-vercel/"/>
    <published>2021-01-19T00:00:00Z</published>
    
    <updated>2021-01-19T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/serverless-telegram-chatbot-vercel/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I love helping people to automate their lives.&lt;/p&gt;
&lt;p&gt;Back in 2018/19, I spent a lot of time building &lt;a href=&quot;https://www.marclittlemore.com/bots/&quot;&gt;automated chatbots&lt;/a&gt; using the Facebook Messenger platform. It was a great way for businesses to automate interactions with their customers. More recently, Facebook have made it much harder to build bots on their platform by limiting their API, &lt;a href=&quot;https://about.fb.com/news/2020/12/changes-to-facebook-messaging-services-in-europe/&quot;&gt;especially if you&#39;re a European user&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the past year, I&#39;ve moved over to using Telegram to build bots and often use them to integrate with other online services and APIs. They have their own fully-featured &lt;a href=&quot;https://core.telegram.org/bots/api&quot;&gt;bot API&lt;/a&gt; which makes it much easier to interact with.&lt;/p&gt;
&lt;h2 id=&quot;telegram-messages&quot;&gt;Telegram messages&lt;/h2&gt;&lt;p&gt;Telegram supports two ways of interacting with the messages that users send to its bots. The first is &lt;a href=&quot;https://en.wikipedia.org/wiki/Push_technology#Long_polling&quot;&gt;long polling&lt;/a&gt;. This is similar to standard polling techniques where the client requests data from the server at regular intervals. With long polling, there&#39;s an expectation that the server may not reply instantly and then the client holds open the request and waits. This works if you are happy with deploying bot server code and infrastructure that never shuts down.&lt;/p&gt;
&lt;p&gt;The alternative way to receive messages from Telegram is using &lt;a href=&quot;https://en.wikipedia.org/wiki/Webhook&quot;&gt;webhooks&lt;/a&gt;. This method is event-driven and Telegram will send you a HTTP POST request only when a new message is sent from a user. This fits perfectly with the idea of event-driven architecture such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Serverless_computing&quot;&gt;serverless functions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at using Telegram&#39;s API and &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt; serverless functions to create a basic bot and deploy it to their cloud hosting.&lt;/p&gt;
&lt;p class=&quot;flex justify-center&quot;&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/botfather.jpg&quot; alt=&quot;Botfather&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;creating-a-new-telegram-bot&quot;&gt;Creating a new Telegram bot&lt;/h2&gt;&lt;p&gt;The first task is to create a new bot that our user can interact with. To do this we must talk to the Botfather. This is Telegram&#39;s own bot which creates and updates new bots. Either start a new chat with Botfather or click on this URL to open the bot in Telegram: &lt;a href=&quot;https://t.me/botfather&quot;&gt;https://t.me/botfather&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To create a new bot we must send it the &lt;code&gt;/newbot&lt;/code&gt; command. It will ask you for a name and username of the bot. The name is displayed in the contact details. The username is a shortname which is used for mentions, &lt;code&gt;@mybot&lt;/code&gt; for example, or in &lt;code&gt;t.me&lt;/code&gt; links for sharing. The username has to end in &lt;code&gt;bot&lt;/code&gt;. As bot usernames are unique, you can see from the screenshot that it took me a couple of attempts to get the bot name &lt;code&gt;MarcDevBot&lt;/code&gt; that hasn&#39;t already been used by another developer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/telegram-new-bot.png&quot; alt=&quot;Telegram new bot&quot;&gt;&lt;/p&gt;
&lt;p&gt;Botfather will send you an authorisation token which is a long string. You use this to send requests to the Telegram Bot API.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-red-100 border-red-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-red-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zm32 224c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;Make sure you keep your &lt;strong&gt;Telegram authorisation token&lt;/strong&gt; safe and don&#39;t share it with anyone. If you do, they can send messages to users on behalf of your bot by using it.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;p class=&quot;flex justify-center&quot;&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/vercel-logo.jpg&quot; alt=&quot;Vercel&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;vercel-account&quot;&gt;Vercel account&lt;/h2&gt;&lt;p&gt;To deploy our chatbot code to Vercel, we must create a Vercel account. Head to the &lt;a href=&quot;https://vercel.com/signup&quot;&gt;signup page&lt;/a&gt; and create a new account using your GitHub, GitLab, or BitBucket account. If you&#39;ve already got one, then that&#39;s great!&lt;/p&gt;
&lt;h2 id=&quot;creating-our-bot-serverless-function&quot;&gt;Creating our bot serverless function&lt;/h2&gt;&lt;p&gt;We&#39;re going to use Node.js and JavaScript and a couple of &lt;code&gt;npm&lt;/code&gt; packages to create a serverless function. I&#39;m going to assume that you have Node.js set up on your local development machine and understand the basics of adding packages to a project using a command terminal.&lt;/p&gt;
&lt;p&gt;Let&#39;s get the project set up. Create a directory called &lt;code&gt;test-bot&lt;/code&gt; and change directory into it:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; test-bot
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; test-bot&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we&#39;ll create a basic Node.js project using &lt;code&gt;npm&lt;/code&gt;. Here we&#39;re skipping the part where we fill in the details of the project into the &lt;code&gt;package.json&lt;/code&gt; file. I&#39;ll leave that for you to do later.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; init &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we want to deploy our serverless function using Vercel, we must install their command line tools. We can add the &lt;code&gt;vercel&lt;/code&gt; application as a global dependency, so that it&#39;s available everywhere and not just in our project, like this:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; vercel&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we can login to &lt;code&gt;vercel&lt;/code&gt; which allows it to know which account we&#39;re deploying the code to.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;vercel login&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel will send you an email with a link to verify your account. Once you click on that link it will save the correct tokens to your machine to allow you to deploy your application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/vercel-login.png&quot; alt=&quot;Vercel login&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;vercel-serverless-functions&quot;&gt;Vercel serverless functions&lt;/h2&gt;&lt;p&gt;Vercel can be used to deploy any frontend application to its cloud hosting but it also allows you to run &lt;a href=&quot;https://vercel.com/docs/serverless-functions/introduction&quot;&gt;serverless functions&lt;/a&gt;, using AWS Lambda and Google Cloud Functions under the hood. Making a new serverless function is easy. Vercel wants a directory called &lt;code&gt;/api&lt;/code&gt; in the root of your project. Inside that directory you can add a JavaScript, Go, Ruby, or Python exported function and it will appear as an API route. Let&#39;s add a new function to handle our Telegram messages.&lt;/p&gt;
&lt;p&gt;Create the &lt;code&gt;/api&lt;/code&gt; directory in the root of your project.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; api&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In your editor of choice create a file called &lt;code&gt;webhook.js&lt;/code&gt; in the &lt;code&gt;/api&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;cookies&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookies&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will expose an API function on the following path &lt;code&gt;/api/webhook&lt;/code&gt;. For basic usage, the name of the file represents the last part of our path (i.e. &lt;code&gt;webhook.js&lt;/code&gt; becomes &lt;code&gt;/webhook&lt;/code&gt;). It always sits under the &lt;code&gt;/api&lt;/code&gt; path unless you change it through configuration. We can test that the code works correctly in our browser by running a development version of Vercel using the following command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;vercel dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As this is the first time you&#39;ve run the development build, Vercel will ask you a few questions as shown below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/vercel-dev.png&quot; alt=&quot;Vercel dev&quot;&gt;&lt;/p&gt;
&lt;p&gt;At the end of the process it will expose an HTTP endpoint on port 3000 by default. Hit this URL in your browser and it will make a GET request to your newly exposed serverless function.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost:3000/api/webhook?hello=world&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You should see a similar response to the following:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;hello&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;world&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;cookies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;_ga&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GA1.1.10178417.1582143681&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;__smToken&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2PgmPwvNz7Ss6Os5nQexTPAl&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;_gid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;GA1.1.327749069.1610568137&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;io&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;WRSUGikIUBDcMAQ_AAAZ&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yay! Our serverless function is working locally and is responding with a JSON response. 🎉&lt;/p&gt;
&lt;h2 id=&quot;creating-the-telegram-bot-message-handler&quot;&gt;Creating the Telegram bot message handler&lt;/h2&gt;&lt;p&gt;Telegram expects to call a webhook by sending us a POST request when a message is entered by the user. Let&#39;s build the message handler to receive this. We can install an &lt;code&gt;npm&lt;/code&gt; package called &lt;code&gt;node-telegram-bot-api&lt;/code&gt; which will allow us to easily receive and send messages to a Telegram bot.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; node-telegram-bot-api&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s update our webhook API to receive the messages that Telegram sends us. A Telegram message contains a JSON body with a &lt;code&gt;message&lt;/code&gt; property in it. There are other messages that Telegram can send us but this function will only handle text messages for now.&lt;/p&gt;
&lt;p&gt;In order to send messages using the Bot API, we need to use the authorisation token that the Botfather sent us earlier. We don&#39;t want to hardcode this as a variable in our code as this would be a security risk. Instead we&#39;ll set it as an environment variable that we can use.&lt;/p&gt;
&lt;p&gt;Copy the following code over the &lt;code&gt;webhook.js&lt;/code&gt; code you wrote earlier.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// https://github.com/yagop/node-telegram-bot-api/issues/319#issuecomment-324963294&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Fixes an error with Promise cancellation&lt;/span&gt;
process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NTBA_FIX_319&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;test&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Require our Telegram helper package&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; TelegramBot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;node-telegram-bot-api&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Export as an asynchronous function&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// We&#39;ll wait until we&#39;ve responded to the user&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Create our new bot handler with the token&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// that the Botfather gave us&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Use an environment variable so we don&#39;t expose it in our code&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TelegramBot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TELEGRAM_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Retrieve the POST request body that gets sent from Telegram&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; body &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Ensure that this is a message being sent&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Retrieve the ID for this chat&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// and the text that the user sent&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;chat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Create a message to send back&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// We can use Markdown inside this&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;✅ Thanks for your message: *&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;text&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;*\nHave a great day! 👋🏻&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Send our new message back in Markdown and&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// wait for the request to finish&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; bot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;parse_mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Markdown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// If there was an error sending our message then we &lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// can log it into the Vercel console&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Error sending message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// Acknowledge the message with Telegram&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// by sending a 200 HTTP status code&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The message here doesn&#39;t matter.&lt;/span&gt;
    response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;OK&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;testing-our-function-locally&quot;&gt;Testing our function locally&lt;/h2&gt;&lt;p&gt;Telegram needs to send us a message to a public URL which has SSL/TLS encryption. As &lt;code&gt;vercel dev&lt;/code&gt; only exposes an HTTP and not an HTTPS endpoint, we need to expose an HTTPS endpoint to the public internet so that the Telegram server can send us messages. We can do this using a great library called &lt;code&gt;ngrok&lt;/code&gt;. This allows us to expose a secure connection to our local server.&lt;/p&gt;
&lt;p&gt;Let&#39;s install &lt;code&gt;ngrok&lt;/code&gt; as a development dependency for our project:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev ngrok&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To expose a web sever on port 3000 to the internet we can now run this command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ngrok http &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ngrok&lt;/code&gt; will now be up and running and will give us two URLs: one for HTTP and another for HTTPS. You need the HTTPS URL to set as the webhook URL for Telegram to communicate with. Note that &lt;code&gt;ngrok&lt;/code&gt; will generate a unique URL each time you run it. If you restart it then you&#39;ll also have to update the Telegram webhook URL.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-telegram-webhook&quot;&gt;Setting up the Telegram webhook&lt;/h2&gt;&lt;p&gt;Using the &lt;code&gt;ngrok&lt;/code&gt; HTTPS URL you were given, we can make a POST HTTP request to the Telegram API using &lt;code&gt;curl&lt;/code&gt; to tell it to send messages to our local serverless function. Use the following command to do this. Make sure your replace &lt;code&gt;&amp;lt;YOUR-BOT-TOKEN&amp;gt;&lt;/code&gt; (including the &amp;lt;&amp;gt; brackets) and the &lt;code&gt;https://your-ngrok-subdomain.ngrok.io&lt;/code&gt; URL to the ones you&#39;ve been given:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-X&lt;/span&gt; POST https://api.telegram.org/bot&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;YOUR-BOT-TOKEN&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/setWebhook &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Content-type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{&quot;url&quot;: &quot;https://your-ngrok-subdomain.ngrok.io/api/webhook&quot;}&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this succeeds then the API will send you the following JSON response:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;ok&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Webhook was set&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;testing-with-the-telegram-api&quot;&gt;Testing with the Telegram API&lt;/h2&gt;&lt;p&gt;Let&#39;s run the &lt;code&gt;vercel dev&lt;/code&gt; local mode whilst &lt;code&gt;ngrok&lt;/code&gt; is running. This will start the vercel development environment and allow the function to receive requests on port 3000. In order to communicate with Telegram&#39;s API, we have to set our authorisation token as an environment variable. Replace &lt;code&gt;&amp;lt;YOUR-BOT-TOKEN&amp;gt;&lt;/code&gt; with the one the Botfather gave you and run the following command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;TELEGRAM_TOKEN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;YOUR-BOT-TOKEN&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; vercel dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&#39;s chat to our bot in Telegram. Start a conversation with the bot you created earlier. Send it any message and you should get a response back.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/telegram-message.png&quot; alt=&quot;Telegram message&quot;&gt;&lt;/p&gt;
&lt;p&gt;The bot is up and running and responding to messages! 🎉&lt;/p&gt;
&lt;h2 id=&quot;deploying-to-vercel&quot;&gt;Deploying to Vercel&lt;/h2&gt;&lt;p&gt;Now that your bot is working as expected locally, let&#39;s deploy it to Vercel. Deploying with Vercel is amazingly simple and that&#39;s why I love it. Type the following in the root of your project:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;vercel&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vercel will build your project and deploy it to its servers for hosting as a serverless function. It will deploy it to the project name that you created earlier when you ran &lt;code&gt;vercel login&lt;/code&gt;. For me this was &lt;code&gt;test-bot&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before we can switch over to using our production Telegram bot, we need to set up an environment variable for the Botfather authorisation token. There are a few ways to do this but let&#39;s set a text variable using the Vercel console for simplicity. Head to the settings area of your project. The URL will be something like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://vercel.com/&amp;lt;your-vercel-username&amp;gt;/&amp;lt;your-bot-name&amp;gt;/settings/environment-variables&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/vercel-environment-variables.png&quot; alt=&quot;Vercel environment variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add a new &lt;code&gt;plaintext&lt;/code&gt; variable called &lt;code&gt;TELEGRAM_TOKEN&lt;/code&gt; which is what we expect to find in our code. Set the value to the authorisation token that the Botfather gave you earlier and hit &lt;code&gt;save&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once this environment variable has been added, we&#39;ll need to redeploy the application to ensure the serverless function reads it when it starts up. Vercel makes it easy again so run the following command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;vercel&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;and-finally&quot;&gt;And finally...&lt;/h2&gt;&lt;p&gt;Now that we&#39;ve got our serverless Telegram bot deployed to Vercel, we need to update Telegram to tell it to route the messages to our Vercel function. Set the Telegram webhook using a &lt;code&gt;curl&lt;/code&gt; command. Again, don&#39;t forget to replace &lt;code&gt;&amp;lt;YOUR-BOT-TOKEN&amp;gt;&lt;/code&gt; with the Telegram authorisation token and the &lt;code&gt;project-name.username.vercel.app&lt;/code&gt; domain name with your own URL which Vercel gave you when you deployed the application.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-X&lt;/span&gt; POST https://api.telegram.org/bot&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;YOUR-BOT-TOKEN&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/setWebhook &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Content-type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{&quot;url&quot;: &quot;https://project-name.username.vercel.app/api/webhook&quot;}&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&#39;s it. You&#39;ve now got a serverless Telegram bot deployed to Vercel which can respond to user messages. Try typing another message in Telegram to your bot and check that it responds correctly.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What&#39;s next?&lt;/h2&gt;&lt;p&gt;Now that you know how to deploy a Telegram bot using Vercel, it&#39;s time to update the code. Telegram has lots of fantastic features. You can send it commands and create a suite of automatic functions. You can use your bot to call out to external APIs to retrieve data for you. Have a think and see what amazing bots you can create!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;Give me a shout&lt;/a&gt; or reach out on &lt;a href=&quot;https://twitter.com/marclittlemore&quot;&gt;Twitter&lt;/a&gt; if you have any questions or need any help!&lt;/p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful resources&lt;/h2&gt;&lt;p&gt;I&#39;ve uploaded the code to a GitHub repository here: &lt;a href=&quot;https://github.com/MarcL/telegram-test-bot&quot;&gt;https://github.com/MarcL/telegram-test-bot&lt;/a&gt; Feel free to fork and reuse it how you wish.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href=&quot;https://core.telegram.org/api&quot;&gt;Telegram API&lt;/a&gt; for some in-depth knowledge of what bots can do.&lt;/p&gt;
&lt;p&gt;Take a look at the &lt;a href=&quot;https://vercel.com/docs&quot;&gt;Vercel documentation&lt;/a&gt; to see what other things you can deploy to their hosting.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Learn in public</title>
    
    <link href="https://www.marclittlemore.com/learn-in-public/"/>
    <published>2021-01-18T00:00:00Z</published>
    
    <updated>2021-01-18T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/learn-in-public/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I worked in the video games industry for almost 20 years. I &lt;a href=&quot;https://www.marclittlemore.com/games/&quot;&gt;made quite a few games&lt;/a&gt; and worked on many more unreleased demos. Every company I worked at was incredibly secretive about their work. Very few teams shared what they were working on externally. Some didn&#39;t even share much between their internal teams either. We weren&#39;t really using open source software libraries. The encouragement was to create our own code and share with nobody.&lt;/p&gt;
&lt;p&gt;I moved to the BBC in 2013 and it changed the way I thought about my work. We started using GitHub to share code. We created pull requests and reviewed and critiqued each other&#39;s code. We almost never had code reviews in my games development days so this was new to me! I started seeing the value in sharing things with others before it was 100% complete.&lt;/p&gt;
&lt;p&gt;The whole team was learning Node.js at the time and most of us hadn&#39;t touched it before. Sharing my code with the team encouraged them to comment on it and we all started learning the best way to architecture our applications.&lt;/p&gt;
&lt;p&gt;I realised that we were learning in public.&lt;/p&gt;
&lt;h2 id=&quot;what-is-learning-in-public-then&quot;&gt;What is learning in public then?&lt;/h2&gt;&lt;p&gt;I first read about the idea after following &lt;a href=&quot;https://twitter.com/swyx&quot;&gt;Shawn SWYX Wang&lt;/a&gt; on Twitter. It was the first time I&#39;d heard the phrase &amp;quot;learn in public&amp;quot; and I was intrigued.&lt;/p&gt;
&lt;p&gt;Most of us learn something new by consuming content. We read a book or an online article. We watch a YouTube video or a Netflix documentary. And the thinking ends there. We might use that knowledge in private but we don&#39;t often share it with others in a public forum.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learning in public&lt;/strong&gt; is the idea of creating a habit of re-teaching what you&#39;ve learnt so that others can benefit from it. It allows you to clarify your understanding of the subject using &lt;a href=&quot;https://effectiviology.com/protege-effect-learn-by-teaching/&quot;&gt;the protégé effect&lt;/a&gt;. This is where teaching others also helps you, the teacher, to absorb that information more easily.&lt;/p&gt;
&lt;p&gt;You can teach what you&#39;ve learnt it in lots of different ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a blog post or tutorial on your website&lt;/li&gt;
&lt;li&gt;a thread of tweets on Twitter&lt;/li&gt;
&lt;li&gt;a YouTube video or Twitch live stream&lt;/li&gt;
&lt;li&gt;creating a public example of code on GitHub&lt;/li&gt;
&lt;li&gt;speaking at a local meetup&lt;/li&gt;
&lt;li&gt;starting a newsletter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might have learnt everyone on the subject yet but you  &lt;strong&gt;try your best to be right&lt;/strong&gt; with the details you know right now. However, also &lt;strong&gt;try not to worry if you get things wrong&lt;/strong&gt;. As &lt;a href=&quot;https://xkcd.com/&quot;&gt;xkcd&lt;/a&gt; tells us, somebody will tell you when you&#39;re wrong on the internet:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xkcd.com/386/&quot;&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/xkcd-duty-calls.png&quot; alt=&quot;Duty Calls&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When you haven&#39;t got all of the details correct, people may well tell you that your work is terrible. Accepting public criticism is tricky at first. Try not to care too much - I know that&#39;s hard initially. Ask them what&#39;s wrong and update your blog post or create a new video with the new details. As Shawn Wang says: &lt;em&gt;&amp;quot;make the thing you wish you had found when you were learning&amp;quot;.&lt;/em&gt; Many more people than you think will find it useful.&lt;/p&gt;
&lt;h2 id=&quot;why-should-i-learn-in-public&quot;&gt;Why should I learn in public?&lt;/h2&gt;&lt;p&gt;I don&#39;t know about you but I can&#39;t remember what I did last week.&lt;/p&gt;
&lt;p&gt;Learning in public is a great way to help future you remember what you learnt last year. It helps the person who was in your shoes six months ago who has picked up the technology for the first time and wants to get started. Once you start writing more about a subject, you&#39;ll start to become an expert on it. People will come to you for advice and ask you questions online. I did this with my &lt;a href=&quot;https://www.marclittlemore.com/bots/&quot;&gt;chatbots&lt;/a&gt; knowledge. I&#39;d never written a Facebook Messenger chatbot before the end of 2017 and within three or four months I had become an &amp;quot;expert&amp;quot; in the field by &lt;a href=&quot;https://www.youtube.com/playlist?list=PLDvWRKT9Cd2g-L4_hStYOcmOfTF87U8FM&quot;&gt;sharing my videos&lt;/a&gt; of what I&#39;d learnt.&lt;/p&gt;
&lt;p&gt;In reality, a lot of online experts are online a few steps ahead of you. The reason you think they know so much is because they&#39;re sharing it with you. Most people don&#39;t do this. Share what you&#39;ve just learnt publicly and you can quickly become an expert too. Eventually people will ask you for advice and may well offer to pay you for your time. Again, this has happened with me and I consulted on a few chatbot projects. Learning in public could lead to a new side project. It will definitely open the door to more opportunities. Who is a new employer going to look more favourably at? The person with a simple resumé or the person with a resumé which links to an expert blog post that other people reference?&lt;/p&gt;
&lt;p&gt;Learning in public also gives you the opportunity to amplify your message. I can sit down with one person and teach them something I&#39;ve learnt. But if I write it down, or make a YouTube video, I may reach 5, 50, or 500 people more easily. It allows you to scale your teaching and also your own learning.&lt;/p&gt;
&lt;h2 id=&quot;what-to-do-next&quot;&gt;What to do next?&lt;/h2&gt;&lt;p&gt;Learn something and write it down. Share those notes on your Twitter account. If you&#39;ve got a website then write an article on there. If you don&#39;t have a blog, learn how to set one up. And then write about the experience!&lt;/p&gt;
&lt;p&gt;By writing out this article, it&#39;s made me attempt to clarify my thoughts on learning in public. Do the same with your learning and I look forward to reading more about it online.&lt;/p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful resources&lt;/h2&gt;&lt;p&gt;https://www.youtube.com/watch?v=znNxtSbuBjI&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.swyx.io/&quot;&gt;Shawn SWYX Wang&lt;/a&gt; &lt;a href=&quot;https://www.swyx.io/learn-in-public/&quot;&gt;Learn in public&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kentcdodds.com/blog/how-i-am-so-productive#increase-the-impact-of-your-value&quot;&gt;Kent C Dodds - How am I so productive&lt;/a&gt; - A great blog post about being productive and learning in public.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.buildinpublic.xyz/&quot;&gt;Build in public&lt;/a&gt;&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Gain confidence at public speaking</title>
    
    <link href="https://www.marclittlemore.com/gain-confidence-at-public-speaking/"/>
    <published>2021-01-17T00:00:00Z</published>
    
    <updated>2021-01-17T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/gain-confidence-at-public-speaking/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I was shy as a teenager.&lt;/p&gt;
&lt;p&gt;Really shy.&lt;/p&gt;
&lt;p&gt;The sort of shy where I&#39;d blush bright red if somebody I didn&#39;t know talked to me.&lt;/p&gt;
&lt;p&gt;The sort of shy where I&#39;d blush bright red if I knew them too!&lt;/p&gt;
&lt;div class=&quot;w-full h-0 pb-72 relative&quot;&gt;
    &lt;iframe src=&quot;https://giphy.com/embed/OpfkuToK5gSHK&quot; width=&quot;100%&quot; height=&quot;100%&quot; style=&quot;position:absolute&quot; frameBorder=&quot;0&quot; class=&quot;giphy-embed&quot; allowFullScreen=&quot;&quot;&gt;
    &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Since my teenage years, the thought of having to get up in front of people and give a speech or presentation absolutely terrified me. I had to explain my university dissertation to my tutors and peers at 21 and I mumbled and sweated my way through that. I then managed to avoid public speaking for a long time. I became less shy over time but I still avoided talking to large groups. And then two things changed which gave me more confidence to start giving speaking to an audience.&lt;/p&gt;
&lt;p&gt;Firstly, I got married in 2006. I spent 12 months prior to the big day preparing for my groom&#39;s speech. It filled me with fear but I practiced it so often that I knew most of the points by heart. As the groom, you have the obvious advantage that everyone is on your side and they want you to give a great performance. I gave the heartfelt speech and it was incredibly well received. That was the first inflection point which made me think I might enjoy public speaking.&lt;/p&gt;
&lt;p&gt;Althought I&#39;d enjoyed talking at my wedding, I carried on with my role in the games industry for many years without much thought of public speaking again. In 2013 I decided to change my technology career and moved to work at the BBC. This was the second inflection point. This is when I decided that enough was enough.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;I wanted to become a confident public speaker.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-to-get-better-at-public-speaking&quot;&gt;How to get better at public speaking&lt;/h2&gt;&lt;p&gt;So what&#39;s the secret? How did I go from becoming a nervous wreck to a confident public speaker?&lt;/p&gt;
&lt;p&gt;It was a simple difference. I said &amp;quot;Yes!&amp;quot; A lot.&lt;/p&gt;
&lt;p&gt;I decided that my job move was a chance to reinvent myself. Every time the engineering manager asked for somebody to share their work with the team. I said &amp;quot;Yes!&amp;quot; When there was a time to present my team&#39;s work to the wider department. I said &amp;quot;I&#39;ll do it!&amp;quot; Learning sessions about technologies I knew a little bit about? &amp;quot;Yes! I can talk about that.&amp;quot;&lt;/p&gt;
&lt;p&gt;I&#39;m sure everyone became bored of having me sharing my thoughts over and over again but it did wonders for my confidence. I was really nervous before each one but over time, I realised that I was getting better at speaking to an audience.&lt;/p&gt;
&lt;h2 id=&quot;so-what-should-you-do-to-gain-more-confidence&quot;&gt;So what should you do to gain more confidence?&lt;/h2&gt;&lt;h3 id=&quot;change-your-mindset&quot;&gt;Change your mindset&lt;/h3&gt;&lt;p&gt;Start thinking that you can be good at public speaking. Most people will want your talk to succeed and to hear you speak. The floor is yours so allow yourself to believe that you can do it. The crowd will be on your side even if your brain is telling you they&#39;re not.&lt;/p&gt;
&lt;h3 id=&quot;plan-an-outline-and-edit&quot;&gt;Plan an outline and edit&lt;/h3&gt;&lt;p&gt;Most of us don&#39;t have speech writers so we have to write the talks ourselves. Start by deciding on your main theme and then create an outline of the points you&#39;d like to make. Consider using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Rule_of_three_(writing)&quot;&gt;rule of three&lt;/a&gt; when grouping your points. It creates a good rhythm to your presentation.&lt;/p&gt;
&lt;p&gt;I don&#39;t like to write out every word I&#39;m going to say. I use my outline to remember key points and I&#39;m now confident enough to talk about each section. If your confidence won&#39;t let you do this yet, write out more of your speech but try to avoid just reading it to the audience. Take a look at it a few days later and edit it. Try short and snappy phrases. Long and winding speeches can quickly become boring.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/holman&quot;&gt;Zach Holman&lt;/a&gt; has a great site called &lt;a href=&quot;https://speaking.io/&quot;&gt;Speaking.io&lt;/a&gt; which has a helpful section on &lt;a href=&quot;https://speaking.io/plan/an-outline/&quot;&gt;outlining your talk&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;practice-makes-perfect&quot;&gt;Practice makes perfect&lt;/h3&gt;&lt;p&gt;I recently hosted a 300+ person online conference with some BBC colleagues. I gave the welcome speech and introduced 3 of our speakers, including a senior director at the BBC. I had so many compliments that I was a natural at presenting and public speaking. But do you know why that is?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;I make it look natural because I practice. A lot.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Prior to the big day, I probably practiced my speech to nobody at least 10 times. Don&#39;t rehearse the speech in your head. Speak the words out loud like you&#39;re presenting to a room full of people. Do this at least 5 to 10 times. You&#39;ll quickly realise what works and what doesn&#39;t. Remove or rewrite any words or sentences that trip you up.&lt;/p&gt;
&lt;h3 id=&quot;record-yourself-speaking&quot;&gt;Record yourself speaking&lt;/h3&gt;&lt;p&gt;If you have the time, it&#39;s useful to record yourself speaking. Record your talk using a microphone, maybe the ones on your headphones, but your computer microphone will work just as well. Recording the speech will give you an idea of how you sound when you speak. You&#39;ll spot the pauses and the &amp;quot;ums&amp;quot; and &amp;quot;ahs&amp;quot;. Try and spot what works and what doesn&#39;t and edit your speech accordingly.&lt;/p&gt;
&lt;h3 id=&quot;find-a-small-audience-first&quot;&gt;Find a small audience first&lt;/h3&gt;&lt;p&gt;Once you&#39;ve practiced then it&#39;s time to share with an audience. This could be your partner or best friend first. Ask for honest feedback from them. Even if they&#39;re not familiar with the content they can comment on the delivery. Try and share it with a few close colleagues. I&#39;ve shared my talks with my team of 9 people before giving it at our internal conferences. If anything isn&#39;t clear then they can tell you.&lt;/p&gt;
&lt;h3 id=&quot;use-fewer-words-per-slide&quot;&gt;Use fewer words per slide&lt;/h3&gt;&lt;p&gt;If your speech is accompanied by a set of PowerPoint or Keynote slides, use these as prompts to help you to remember what to say. Don&#39;t fill them with all of your speech and read off them. Key phrases are much better than a list of bullet points. Use your outline that you wrote earlier and with all that practicing you did, it&#39;ll spark a reminder of this part of your story.&lt;/p&gt;
&lt;h3 id=&quot;act-naturally-and-be-confident&quot;&gt;Act naturally and be confident&lt;/h3&gt;&lt;p&gt;This will be hard for the first few talks but if you come across as your confident self that I know you are, the audience will warm to you immediately. You are the expert in front of a crowd of people who want to hear what you have to say. Remember that. If you can inject a bit of humour into the talk then that&#39;s helpful to put both you and the crowd at ease. But remember it&#39;s not a stand-up comedy gig!&lt;/p&gt;
&lt;p&gt;Try and make some eye contact with your audience. Choose a few people in the group and imagine you&#39;re just talking to them. Spending the talk looking down at your notes, or even worse, holding them in front of your face, is not what the audience want. Don&#39;t use looking at your notes as a defence mechanism.&lt;/p&gt;
&lt;h3 id=&quot;a-final-reminder-itll-be-ok&quot;&gt;A final reminder - It&#39;ll be ok&lt;/h3&gt;&lt;p&gt;Take a deep breath before you start. Remember to speak slowly and don&#39;t rush it. And remember that things might go wrong. You might forget a section of your talk. You might introduce something before you wanted to. The technology might go wrong. These are all part of public speaking. Shrug it off and carry on. It&#39;ll be better next time. I know you&#39;ll be brilliant anway!&lt;/p&gt;
&lt;p&gt;I hope this article helps you to gain a bit more confidence. Go out and speak publicly to others and please say &amp;quot;yes&amp;quot; more when the opportunity arises.&lt;/p&gt;
&lt;h2 id=&quot;useful-resources&quot;&gt;Useful resources&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://speaking.io/&quot;&gt;Speaking.io&lt;/a&gt; - A fantastic site from Zach Holman which helps you to learn more about public speaking.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forge.medium.com/the-shy-persons-guide-to-confidently-speaking-in-public-137859abbcef&quot;&gt;The Shy Person’s Guide to Public Speaking&lt;/a&gt; - A great article on overcoming shyness and public speaking.&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>A beginner's guide to using the Obsidian Notes application</title>
    
    <link href="https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/"/>
    <published>2021-01-16T00:00:00Z</published>
    
    <updated>2022-11-08T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/beginners-guide-note-taking-obsidian/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I write a lot of notes both in my job and in my personal life. I&#39;ve tried a lot of different note taking applications over the years and more recently I&#39;ve started using &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian Notes&lt;/a&gt; as my application of choice.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at how to use the Obsidian Notes application for writing clearer notes.&lt;/p&gt;
&lt;h2 id=&quot;what-is-the-obsidian-notes-taking-app&quot;&gt;What is the Obsidian Notes taking app?&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;The Obsidian app is a beautiful, FREE note taking application which allows you to create and store your notes locally in the &lt;a href=&quot;https://www.markdownguide.org/&quot;&gt;Markdown&lt;/a&gt; file format. It allows you to link notes together and create a knowledge graph of information.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Obsidian notes are stored locally as files on your computer rather than in the cloud. Obsidian has a text editor which allows you to add markdown formatting to your notes. It also has a preview mode which allows you to see how your notes will look when they are rendered as HTML.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As a developer, Markdown is a format I write in regularly and this made the Obsidian app a perfect tool for me.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;While you can use the Obsidian app to edit your notes, markdown is a common format for software developers so &lt;strong&gt;you can also edit your notes using any text editor that supports the markdown format.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you&#39;re a software developer, you can use a tool like &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; to edit your notes if you like.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;obsidian-notes-are-stored-locally&quot;&gt;Obsidian notes are stored locally&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;An important thing to know about the Obsidian app is that it stores your markdown notes locally on your computer rather than in the cloud. Obsidian notes are simply a folder of plain text files.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As the files are stored locally, you never have to worry about a service being shut down or changing their pricing model. Markdown is an almost standard format so you can move your notes to a new note-taking application if you needed to.&lt;/p&gt;
&lt;p&gt;The downside of the files being stored locally is that you&#39;re now responsible for backing up your data. However, this is easily done using Google Drive, Dropbox, OneDrive, or some other online storage method. If you&#39;re a developer, you can even use a &lt;code&gt;git&lt;/code&gt; repository to store your notes.&lt;/p&gt;
&lt;h3 id=&quot;obsidian-links-notes-together-in-a-knowledge-graph&quot;&gt;Obsidian links notes together in a knowledge graph&lt;/h3&gt;&lt;p&gt;Obsidian is a tool which claims to give you &amp;quot;a &lt;a href=&quot;https://fortelabs.co/blog/basboverview/&quot;&gt;second brain&lt;/a&gt;, forever&amp;quot;. This means it can store all of your notes and help you to link each note&#39;s idea together.&lt;/p&gt;
&lt;p&gt;Linking your Obsidian notes together is known as a knowledge graph. It&#39;s a series of interlinked notes which allows you to find common themes between your notes. This knowledge graph is also found in the tool &lt;a href=&quot;https://roamresearch.com/&quot;&gt;Roam Research&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Linked notes is one of the great ideas behind Obsidian and Roam Research. By tagging concepts in your notes, and linking them to other notes using internal links, you slowly build up a graph which helps to you to discover similar ideas and thinking.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of my current set of notes in Obsidian:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/obsidian-graph.jpg&quot; alt=&quot;Obsidian Graph&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-is-personal-knowledge-management&quot;&gt;What is Personal Knowledge Management?&lt;/h2&gt;&lt;p&gt;This year I discovered the idea of &lt;a href=&quot;https://en.wikipedia.org/wiki/Personal_knowledge_management&quot;&gt;personal knowledge management&lt;/a&gt; (PKM). This is the idea of collecting and classifying your thoughts so that they can be easily retrieved but also help you to make sense out of them. It hadn&#39;t occurred to me that I should be bringing all of my notes together so that I could build new ideas from them. I mostly used the search functionality in any notes applications to find things I&#39;d recorded when I needed them. Reading about PKM brought me to a tool called &lt;a href=&quot;https://roamresearch.com/&quot;&gt;Roam Research&lt;/a&gt; which shared the idea of networked thought. It gives the user a way to link notes together in a more coherent way to create a graph of connected thinking. I used this for a while but I was looking for something in the open source space. And then I finally discovered &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using Obsidian for the last 4 or 5 months has really helped me to organise my notes and has started me on a journey to creating new ideas from concepts I find rather than letting the notes wither away and die.&lt;/p&gt;
&lt;h2 id=&quot;how-do-i-get-started-with-obsidian&quot;&gt;How do I get started with Obsidian?&lt;/h2&gt;&lt;p&gt;At the time of writing this, Obsidian is a note-taking application that is available for Mac, Windows, and Linux.&lt;/p&gt;
&lt;p&gt;You can easily &lt;a href=&quot;https://obsidian.md/download&quot;&gt;download Obsidian from their website&lt;/a&gt; and then install it on your local computer.&lt;/p&gt;
&lt;p&gt;Once Obsidian is installed, you&#39;ll be asked to create or open a vault. An Obsidian vault is simply a directory within which your notes will be stored as Markdown files. Obsidians notes are just a folder of plain text markdown files.&lt;/p&gt;
&lt;p&gt;Choose &amp;quot;create&amp;quot; if you don&#39;t have any files that you&#39;ve already created. Alternatively you can open a folder which contains existing Markdown files and they will be imported into Obsidian as a new vault.&lt;/p&gt;
&lt;p&gt;Once you&#39;re set up you can click on &amp;quot;create note&amp;quot; to create a new Markdown file for a new note.&lt;/p&gt;
&lt;h2 id=&quot;how-do-i-create-a-markdown-file-in-obsidian&quot;&gt;How do I create a markdown file in Obsidian?&lt;/h2&gt;&lt;p&gt;Each new note that&#39;s created is a Markdown file. &lt;a href=&quot;https://www.markdownguide.org/getting-started/&quot;&gt;Markdown syntax&lt;/a&gt; is relatively easy to get started with. It&#39;s a plaintext file format with specific syntax which maps to an HTML element.&lt;/p&gt;
&lt;p&gt;Here&#39;s are some examples of Markdown syntax:&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;An example Markdown file&lt;/span&gt; &lt;span class=&quot;&quot;&gt;example-markdown.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;#&lt;/span&gt; A main header&lt;/span&gt;

Some text as a paragraph. But this text is &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt; and this is in &lt;span class=&quot;token italic&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;italics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;*&lt;/span&gt;&lt;/span&gt;.

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; A secondary header&lt;/span&gt;

Another paragraph with some &lt;span class=&quot;token code-snippet code keyword&quot;&gt;&#96;inline code&#96;&lt;/span&gt; in it.

A &lt;span class=&quot;token url&quot;&gt;[&lt;span class=&quot;token content&quot;&gt;link&lt;/span&gt;](&lt;span class=&quot;token url&quot;&gt;https://another-website.com/&lt;/span&gt;)&lt;/span&gt; to an external URL.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;what-are-obsidian-backlinks&quot;&gt;What are Obsidian backlinks?&lt;/h2&gt;&lt;p&gt;While Markdown supports hyperlinking to another internal or external page using its link format, Obsidian adds the idea of the &lt;strong&gt;backlink&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Normal links in markdown files are mono-directional which means that a link points to the new page only.&lt;/p&gt;
&lt;p&gt;Obsidian backlinks allow bi-directional linking. This means that if page A links to page B, Obsidian also knows that page B is linking back to page A. This allows ideas to start linking together as you write new notes which creates a knowledge graph of linked notes.&lt;/p&gt;
&lt;p&gt;Obsidian backlinks are created using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Help:Link#Wikilinks_(internal_links)&quot;&gt;Wikilink&lt;/a&gt; format using double brackets. Here is an example of how to create a note which links to another page:&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Obsidian backlinks example&lt;/span&gt; &lt;span class=&quot;&quot;&gt;obsidian-backlink.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;#&lt;/span&gt; My new note&lt;/span&gt;

I want this note to link to [[another note]] using Obisidian backlinks.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, Obsidian will link to a new note but it won&#39;t create it until you follow the link. Pressing &lt;code&gt;CMD + click&lt;/code&gt; (Mac) or &lt;code&gt;CTRL + click&lt;/code&gt; (Windows) on the link will take you to a new page and then the note is created in your vault.&lt;/p&gt;
&lt;h3 id=&quot;what-are-obsidian-mentions&quot;&gt;What are Obsidian mentions?&lt;/h3&gt;&lt;p&gt;Obsidian backlinks are really useful for seeing which ideas link together in your notes. For each note, it has the concept of linked and unlinked mentions.&lt;/p&gt;
&lt;h4&gt;Obsidian linked mentions&lt;/h4&gt;&lt;p&gt;Obsidian linked mentions are backlinks in a note that contain an internal link to the active note. This are created when you explicitly link one page to another using the &lt;code&gt;[[backlink]]&lt;/code&gt; format.&lt;/p&gt;
&lt;h4&gt;Obsidian unlinked mentions&lt;/h4&gt;&lt;p&gt;Obsidian unlinked mentions are backlinks in a note that contain references to the page from another note but you haven&#39;t explicitly linked to it. Using these unlinked mentions, you can quickly see how ideas from multiple notes are related and could be linked together.&lt;/p&gt;
&lt;p&gt;The idea of linked and unlinked mentions are really useful to see how your notes are related to each other. You can use these backlinks to expand your thoughts and allow you to create new ideas from them.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of some linked and unlinked mentions from when I was building a &lt;a href=&quot;https://firebase.google.com/&quot;&gt;Google Firebase&lt;/a&gt; project and documenting my learning:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/obsidian-linked-unlinked-mentions.jpg&quot; alt=&quot;Obsidian linked and unlinked mentions&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-are-obsidian-tags&quot;&gt;What are Obsidian tags?&lt;/h3&gt;&lt;p&gt;In addition to linking notes together, Obsidian allows you to use tags. Tags are another kind of link.&lt;/p&gt;
&lt;p&gt;Adding tags to a note can make them easier to find in searches if you have a large vault of notes. They&#39;re helpful for adding context to your notes and allowing you to group them together. In your Markdown files you add a new tag using a Twitter-style hashtag.&lt;/p&gt;
&lt;p&gt;For example, in my own Obsidian vault, I use &lt;code&gt;#toread&lt;/code&gt; or &lt;code&gt;#towatch&lt;/code&gt; to gather blog posts or YouTube videos I should watch and take notes from.&lt;/p&gt;
&lt;div class=&quot;text-sm&quot;&gt;&lt;span class=&quot;uppercase pr-4&quot;&gt;Obsidian tags example&lt;/span&gt; &lt;span class=&quot;&quot;&gt;obsidian-tags.md&lt;/span&gt;&lt;/div&gt;
&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;#&lt;/span&gt; An example note&lt;/span&gt;

Here are a list of tags for this note:

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;#&lt;/span&gt;toread #towatch #a-long-tag-name&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;should-i-use-obsidian-tags-or-links&quot;&gt;Should I use Obsidian tags or links?&lt;/h3&gt;&lt;p&gt;You can and should use both tags and links in your notes.&lt;/p&gt;
&lt;p&gt;Links are connections from one Obsidian note to another note. And as I mentioned earlier, they&#39;re bi-directional so the notes are both linked together with a backlink.&lt;/p&gt;
&lt;p&gt;Tags are connections from a note to an idea. A tag is a link but it has no note linked to it.&lt;/p&gt;
&lt;p&gt;So you don&#39;t really have to choose between links and tags. Why not use both?&lt;/p&gt;
&lt;p&gt;I find that tags are useful for grouping notes together and links are useful for connecting ideas together. They are complimentary and you can easily use both together.&lt;/p&gt;
&lt;h3 id=&quot;differences-between-obsidian-tags-and-links&quot;&gt;Differences between Obsidian tags and links&lt;/h3&gt;&lt;p&gt;While you can use both tags and links together, there are a few important things to remember.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you change the name of a file within Obsidian, all links to that folder will automatically change to be pointing to the right place. This won&#39;t happen with tags.&lt;/li&gt;
&lt;li&gt;Clicking on a tag will open a search for all of the files that contain that tag.&lt;/li&gt;
&lt;li&gt;Clicking on a link will open the linked note if it exists or create a new one if it doesn&#39;t.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-the-obsidian-graph-view&quot;&gt;What is the Obsidian graph view?&lt;/h2&gt;&lt;p&gt;Obsidian allows you to create an unlimited vault of notes but its power comes in linking these notes together.&lt;/p&gt;
&lt;p&gt;The Obsidian graph view allows you to visualise connections between your notes and encourages you to explore how your thinking is linked together.&lt;/p&gt;
&lt;p&gt;To see the graph view click on &amp;quot;open graph view&amp;quot; in the Obsidian sidebar. This extended view allows you to filter by specific keywords or tags that you use in your notes.&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of notes related to my use of Obsidian:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/obsidian-graph-view.jpg&quot; alt=&quot;Obsidian graph view&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;I&#39;ve only scratched the surface with what Obsidian can do for your personal knowledge management system.&lt;/p&gt;
&lt;p&gt;Obsidian has a fantastic plugin system which allows more functionality to be added to it. These plugins are built by the Obsidian team and also from other developers.&lt;/p&gt;
&lt;p&gt;It also has a wonderful community around it and each of the Obsidian forum members are incredibly helpful. They all want others to learn how to get the best out of Obsidian. You can find their &lt;a href=&quot;https://forum.obsidian.md/&quot;&gt;forums here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Obsidian is constantly being improved by the developers with new releases coming out every couple of weeks. Before you know it you&#39;ll be heading down the rabbit hole of &lt;a href=&quot;https://fortelabs.co/blog/basboverview/&quot;&gt;building a second brain&lt;/a&gt; and &lt;a href=&quot;https://zettelkasten.de/posts/overview/&quot;&gt;Zettlekasten&lt;/a&gt; like I have.&lt;/p&gt;
&lt;p&gt;Take a look at Obsidian if you&#39;re considering levelling up your note taking system and &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;let me know what you think&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions-faq-about-obsidian-notes&quot;&gt;Frequently asked questions (FAQ) about Obsidian notes&lt;/h2&gt;&lt;h3 id=&quot;what-is-obsidian-notes&quot;&gt;What is Obsidian notes?&lt;/h3&gt;&lt;p&gt;Obsidian is a powerful note-taking application that allows you to create a folder of plaintext files for your notes. It&#39;s a great tool for personal knowledge management and allows you to create one or more vaults of notes that are linked together.&lt;/p&gt;
&lt;h3 id=&quot;how-to-backlink-in-obsidian&quot;&gt;How to backlink in Obsidian?&lt;/h3&gt;&lt;p&gt;To create a backlink in Obsidian, you use the Wikilink format using double brackets. For example, to link to another note called &amp;quot;another note&amp;quot; you would use &lt;code&gt;[[another note]]&lt;/code&gt;. This links your current note to &amp;quot;another note&amp;quot; and also provides a backlink from &amp;quot;another note&amp;quot; to your current note.&lt;/p&gt;
&lt;h3 id=&quot;is-obsidian-notes-better-than-notion&quot;&gt;Is Obsidian Notes better than Notion?&lt;/h3&gt;&lt;p&gt;Notion is a powerful web application which allows you to write notes, create tasks, and manage projects of content. While it has similarities with Obsidian in its ability to create notes, it&#39;s a very different tool for managing projects and content with a focus on collaboration.&lt;/p&gt;
&lt;p&gt;Obsidian is a private and secure way to store your own notes and ideas on your local computer.&lt;/p&gt;
&lt;h3 id=&quot;are-obsidian-notes-safe&quot;&gt;Are Obsidian notes safe?&lt;/h3&gt;&lt;p&gt;By default Obsidian stores everything locally in a folder of plaintext files. This means that your notes are only accessible on your computer and not on the internet. This makes it a very secure and private way to store your notes.&lt;/p&gt;
&lt;p&gt;You can sign up to Obsidian&#39;s paid service &lt;a href=&quot;https://obsidian.md/sync&quot;&gt;Obsidian Sync&lt;/a&gt; if you want to synchronise your notes to the cloud and share them with multiple computers. This is private and encrypted so it&#39;s still a safe way to store your notes.&lt;/p&gt;
&lt;h3 id=&quot;is-obsidian-hard-to-learn&quot;&gt;Is Obsidian hard to learn?&lt;/h3&gt;&lt;p&gt;The hardest part of learning Obsidian is understanding the Markdown format. If you&#39;re unfamiliar with it, spend a couple of hours creating notes and reading a Markdown formatting guide. Once you&#39;ve got the basics down, you&#39;ll be able to create notes quickly and easily in Obsidian.&lt;/p&gt;
&lt;p&gt;Obsidian&#39;s backlinks are really easy to learn and once you understand how they work, you&#39;ll be able to create connections between your notes in seconds.&lt;/p&gt;
&lt;p&gt;The most complicated part of Obsidian is deciding how you want to organise your notes. This is up to the individual to decide and experiment with. There&#39;s no right or wrong way to do it. You can use tags, backlinks, folders, or a combination of both to organise your notes.&lt;/p&gt;
&lt;h3 id=&quot;is-obsidian-good-for-note-taking&quot;&gt;Is Obsidian good for note-taking?&lt;/h3&gt;&lt;p&gt;I love it for note-taking! 😄&lt;/p&gt;
&lt;p&gt;You can keep it simple and write a note in Markdown format. You can link those notes together and create a vault of information. You can link those notes together and start to see how your thinking is linked.&lt;/p&gt;
&lt;h3 id=&quot;does-obsidian-have-a-mobile-app&quot;&gt;Does Obsidian have a mobile app?&lt;/h3&gt;&lt;p&gt;Yes! Obsidian has a mobile app and it works in the same way as the desktop application.&lt;/p&gt;
&lt;p&gt;You can down load the &lt;a href=&quot;https://obsidian.md/mobile&quot;&gt;Obsidian mobile app here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;who-is-obsidian-notes-best-for&quot;&gt;Who is Obsidian notes best for?&lt;/h3&gt;&lt;p&gt;Obsidian is a powerful note-taking application for many different sorts of people. Not everyone will use it in the same way, but it&#39;s useful in different ways.&lt;/p&gt;
&lt;h4&gt;Obsidian for software engineers&lt;/h4&gt;&lt;p&gt;I&#39;m a software engineering manager but I still have to stay technical. I use Obsidian to take notes on the latest technologies and frameworks I&#39;m exploring. I also use it to take notes with my team when I conduct 1:1s, for meeting notes, and for any notes that I take as I read books or watch videos.&lt;/p&gt;
&lt;h4&gt;Obsidian for university students&lt;/h4&gt;&lt;p&gt;Obsidian is the perfect tool for taking notes for university students. You can create a vault for all of your subjects or you could split it into separate vaults for the different areas of your course.&lt;/p&gt;
&lt;p&gt;The ability to link your notes together will really help you to understand how your studies are linked together. This will be especially useful when you come to revise for your exams.&lt;/p&gt;
&lt;h4&gt;Obsidian for academics and researchers&lt;/h4&gt;&lt;p&gt;Obsidian is the perfect tool if you work in academia and are researching and writing a new paper. You can create a vault for your research and create new notes for all of your ideas.&lt;/p&gt;
&lt;p&gt;Linking your Obsidian notes together will really help to ensure you have citations in your papers and you can easily find the sources you used.&lt;/p&gt;
&lt;h4&gt;Obsidian for writers&lt;/h4&gt;&lt;p&gt;Obsidian is the perfect tool for writers. You can create new notes for articles that you find on the internet or for any videos that you watch.&lt;/p&gt;
&lt;p&gt;Take notes of the ideas that you find and start to link them together using Obsidian backlinks. By capturing snippets of linked notes, you can easily research and write your next article.&lt;/p&gt;
&lt;h2 id=&quot;useful-resources-to-learn-more-about-obsidian-notes&quot;&gt;Useful resources to learn more about Obsidian notes&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/francescod_ales&quot;&gt;Francesco D&#39;Alessio&lt;/a&gt; from &lt;a href=&quot;https://toolfinder.co/&quot;&gt;Tool Finder&lt;/a&gt; has a great getting started guide for Obsidian:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=bLoukY64gYk&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/nickmilo&quot;&gt;Nick Milo&lt;/a&gt; has a really helpful set of videos on using Obsidian. He also has a course called &lt;a href=&quot;https://www.linkingyourthinking.com/&quot;&gt;Linking Your Thinking&lt;/a&gt; which helps you to build your own personal knowledge management system.&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=QgbLb6QCK88&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/justindirose&quot;&gt;Justin DiRose&lt;/a&gt; from &lt;a href=&quot;https://effectiveremotework.com/&quot;&gt;Effective Remote Working&lt;/a&gt; has a great set of videos on learning Obsidian too so start here:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=LDPrRQFgJxI&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Refactoring code: the broken windows theory</title>
    
    <link href="https://www.marclittlemore.com/refactoring-code-broken-windows-theory/"/>
    <published>2021-01-15T00:00:00Z</published>
    
    <updated>2021-01-15T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/refactoring-code-broken-windows-theory/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Every time we write software we incur technical debt.&lt;/p&gt;
&lt;p&gt;Every decision to add new code to a project has the potential to introduce new bugs. Over time a team will introduce disorder to their systems as new features are built and technical architecture changes. So how can you encourage a healthy codebase and avoid it falling into a state of disrepair?&lt;/p&gt;
&lt;p&gt;Let&#39;s think about the broken windows theory.&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;h4&gt;The Broken Windows Theory&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Broken_windows_theory&quot;&gt;broken windows theory&lt;/a&gt; is a criminological theory that states that visible signs of crime, anti-social behaviour, and civil disorder create an urban environment that encourages further crime and disorder, including serious crimes.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This theory, from 1982, suggests that a building with broken windows, a potential indicator of criminal behavior, causes the local residents to not care about their environment and other windows soon become broken. After a while, more windows are broken and ultimately the building becomes so damaged that it becomes abandonded.&lt;/p&gt;
&lt;p&gt;We can apply this theory to software development and the teams who build and own applications. Small hacks added to the code can quickly add up over time. Leaving badly named variables or functions can encourage the next developer to do the same. If the previous developer didn&#39;t care about the code, why should you? Over the time a software project can quickly incur more and more technical debt. Over time the project quickly becomes a tangled mess and you&#39;ll start to see your team wince when asked to work on that application.&lt;/p&gt;
&lt;h2 id=&quot;avoiding-rotting-software&quot;&gt;Avoiding rotting software&lt;/h2&gt;&lt;p&gt;Want to avoid software going bad? Let&#39;s look at fixing some broken windows we find.&lt;/p&gt;
&lt;p&gt;Martin Fowler calls it &lt;a href=&quot;https://martinfowler.com/bliki/OpportunisticRefactoring.html&quot;&gt;opportunistic refactoring&lt;/a&gt;. This is when a developer sees code that isn&#39;t as clear as it should be and takes the chance to fix it by tidying up variables or refactoring a function. If everyone does this regularly, the team can continue to keep the codebase healthy. This little act of fixing a small piece of code encourages the whole team to care about their code. It avoids a &amp;quot;cut and paste&amp;quot; mentality and builds a consistency in the code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By doing this we quickly build a culture of engineering excellence. 🎉&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One practice that I encourage is to avoid large pull requests. These become difficult to review and often get merged with few comments due to their size. If you&#39;re writing new code and spot the opportunity to fix one of these broken windows, sometimes it&#39;s more useful to note down where the code needs fixing rather than adding it to your current feature work. Instead raise a second pull request to make these changes so that the improvements are isolated from new features. Don&#39;t leave it for too long though or that code will start to rot again.&lt;/p&gt;
&lt;p&gt;Be pragmatic about fixing all of the issues you find. While there is a huge benefit to refactoring and fixing your broken windows, no software will ever be perfect. You can only work with the best knowledge you have right now. Consistent small changes to make the code better may make a bigger impact in the long run and will help you to balance building new features with fixing the technical debt.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Robert_Baden-Powell,_1st_Baron_Baden-Powell&quot;&gt;Robert Baden-Powell&lt;/a&gt;, the father of scouting, says it nicely:&lt;/p&gt;
&lt;div class=&quot;p-4 font-semibold text-xl sm:text-2xl italic border-l-4 border-red-600&quot;&gt;&lt;h4&gt;The Scouting Rule&lt;/h4&gt;
&lt;p&gt;“Try and leave the world a little better than you found it”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;- Robert Baden-Powell&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I think this is a wonderful rule to live your life by as well as using it to help you to decide when to refactor your software.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Become a better software engineering manager</title>
    
    <link href="https://www.marclittlemore.com/become-better-software-engineering-manager/"/>
    <published>2021-01-14T00:00:00Z</published>
    
    <updated>2021-01-14T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/become-better-software-engineering-manager/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I didn&#39;t set out to become a manager of software engineers. One day I was the principal software engineer on the team. The next I&#39;d shifted to a management track. I initially wasn&#39;t sure if I&#39;d made the right move but I now really love building great software teams.&lt;/p&gt;
&lt;p&gt;I&#39;ve dipped in and out of management over the years but I&#39;ve now been a software engineering team lead for the past 4 years. I manage a team of 8 software engineers of different levels of experience and every day presents a new challenge. Here&#39;s are some learnings which have helped me on my journey. I hope they can help you too.&lt;/p&gt;
&lt;h2 id=&quot;its-a-different-career&quot;&gt;It&#39;s a different career&lt;/h2&gt;&lt;p&gt;A software engineering management role is often seen as the next step up from being a software engineer. It&#39;s not. It&#39;s a different discipline. If you love coding, you probably won&#39;t be doing as much of it if any. There are many reasons to become a manager but make sure you know what you&#39;re getting into. It shouldn&#39;t just be the next step after becoming a senior engineer. People management is something different than managing a codebase. Take a look at some &lt;a href=&quot;https://charity.wtf/2019/09/08/reasons-not-to-be-a-manager/&quot;&gt;reasons to not be a manager&lt;/a&gt; written by the awesome &lt;a href=&quot;https://twitter.com/mipsytipsy&quot;&gt;Charity Majors&lt;/a&gt; before you consider a management role.&lt;/p&gt;
&lt;h2 id=&quot;youll-have-imposter-syndrome&quot;&gt;You&#39;ll have imposter syndrome&lt;/h2&gt;&lt;p&gt;You&#39;ve spent 20+ years writing code. You can reverse a binary tree on a whiteboard while deploying a Kubernetes cluster. If you can do that, being a manager must be easy right?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;STOP!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Your day of coding will be replaced 1:1s with your team, stakeholder meetings, backlog grooming in Jira, looking through résumés, writing technical documents, and so on. For a while you won&#39;t know what you&#39;re doing but perserve. The imposter syndrome may always be there but it will stop nagging you eventually. Ensure you find a network of peers that can help you to learn. Find other engineering leaders who have done this for a bit longer than you have. Share your journeys and you&#39;ll find that other people struggle at times too. A network of peers is really helpful. Meet them regularly and it will help you all to grow.&lt;/p&gt;
&lt;h2 id=&quot;build-a-culture-of-positivity-and-honesty&quot;&gt;Build a culture of positivity and honesty&lt;/h2&gt;&lt;p&gt;Your job is now to manage and lead a group of people to success. It&#39;s your job to set the tone for the team and help to define the team&#39;s culture. Be the one who builds a culture where all voices matter. A junior engineer can have great ideas like your more experienced developers but you must allow them to feel comfortable enough to share them. Ensure that your meetings are inclusive and make all of the team feel comfortable enough to share their opinions. Why not use a collaborative document instead if you feel that some people are less comfortable at public speaking with more senior engineers around. Help to set the expectations for code reviews and ensure that it feels like a learning experience rather than a telling off. Appreciate your team in public. If someone has written some fantastic code or released a great new feature, share and celebrate it both inside and outside of the team. Most people are quick to complain about mistakes but aren&#39;t so fast at thanking people for great work. Be the champion of your team!&lt;/p&gt;
&lt;h2 id=&quot;trust-is-the-key&quot;&gt;Trust is the key&lt;/h2&gt;&lt;p&gt;Most people won&#39;t let you down. Trust them to do their best work without micromanaging them and telling them exactly what to do. Yes, it&#39;s your resonsibility to ensure the work gets done but do this by setting the direction and not piloting the ship. There may be times when the team doesn&#39;t enjoy the work so your role is to explain the difficult choices which may have been made. If you&#39;ve built an culture of honesty and openness, the team will understand. Allowing autonomy in the team will lead to greater motivation and a belief in their own decisions. If you trust your team then your team will trust you.&lt;/p&gt;
&lt;h2 id=&quot;become-a-great-listener-and-communicator&quot;&gt;Become a great listener and communicator&lt;/h2&gt;&lt;p&gt;Your new role as a manager is mostly about clear communication. You&#39;ll be talking with project stakeholders about the requirements and deadlines for the project. You&#39;ll be sharing details with your team and setting the technical strategy for what you need to do. You&#39;ll be hearing about the needs and struggles of your team in their 1:1s. You&#39;ll have to provide mentoring and feedback for your team. What you say and how you say it will matter to the team, your manager, and other people you deal with. Try and learn how to write and speak clearly. This isn&#39;t easy but it&#39;s a skill you should attempt to get better at.&lt;/p&gt;
&lt;h2 id=&quot;know-that-some-conversations-will-be-difficult&quot;&gt;Know that some conversations will be difficult&lt;/h2&gt;&lt;p&gt;Your job as a manager means you&#39;ll have to deal with the good and the bad. Your team won&#39;t be happy all of the time. You&#39;ll need to be a great listener and able to deal with tricky conversations. This has been even more apparent over 2020 and a global pandemic! If you&#39;ve built a culture of openness, these conversations should happen regularly and you should embrace the fact that your team feel comfortable enough to be honest with you about how they feel. If you don&#39;t know the answer, it&#39;s ok to say you don&#39;t know. Ensure you&#39;ve got support networks to help you find answers. If it&#39;s a conflict within the team, talk to everyone involved and help to resolve it as quickly as you can.&lt;/p&gt;
&lt;h2 id=&quot;your-job-isnt-to-know-everything&quot;&gt;Your job isn&#39;t to know everything&lt;/h2&gt;&lt;p&gt;When you first start to lead a team, you expect to be able to answer all of the questions from all of the people. You quickly realise that this isn&#39;t achievable. It&#39;s time to be comfortable with saying &amp;quot;I don&#39;t know&amp;quot; to people. It&#39;s better to become an enabler for the team. You might not know the answer but you can find out from your senior engineers, or other team members. Open up your network to the team and connect them with the people who can answer their questions. You become more valuable to the team when you realise you don&#39;t have to know everything but you know where to find the answers.&lt;/p&gt;
&lt;h2 id=&quot;advocate-for-your-team&quot;&gt;Advocate for your team&lt;/h2&gt;&lt;p&gt;Your team won&#39;t necessarily have the voice that you have as a manager. It&#39;s your job to shout loudly about how amazing they are. You should be sharing the great work they&#39;re doing with the rest of the department. You can be a supporter of team members and help to guide them in their career. It&#39;s up to you to help to mentor and develop the team. Open up your network to them and help all of your engineers to find new opportunities to grow. If you do this you&#39;ll bring the whole team up. I&#39;ve always loved the saying &lt;a href=&quot;https://en.wikipedia.org/wiki/A_rising_tide_lifts_all_boats&quot;&gt;&amp;quot;A rising tide lifts all boats&amp;quot;&lt;/a&gt;. Bring everyone along on the journey with you and help the whole team to succeed.&lt;/p&gt;
&lt;h2 id=&quot;keep-on-learning&quot;&gt;Keep on learning&lt;/h2&gt;&lt;p&gt;Being a better software engineering manager is an on-going process. Ask for help from your manager and peers. Talk to your team and ask them how you can improve what you do. Ask for a step-level 1:1 with your manager&#39;s manager and learn from them. New challenges will come at you each day but embrace them and work out strategies to deal with them. Never stop learning and share your experiences with other managers. Let&#39;s help each other!&lt;/p&gt;
&lt;p&gt;This isn&#39;t an exhaustive list and I&#39;m sure I&#39;ll think of more ways of becoming a better manager as I learn them myself. You can &lt;a href=&quot;https://mdcore.github.io/2020/12/22/Towards-a-High-View-of-Managers.html&quot;&gt;read some other great suggestions&lt;/a&gt; on my good friend Gavin&#39;s website. He&#39;s another software engineering manager that I trust.&lt;/p&gt;
&lt;p&gt;If you want to read about my journey from a being a developer to a software engineering team lead then I shared my thoughts in &lt;a href=&quot;https://devtomanager.com/interviews/marc-littlemore/&quot;&gt;an interview on the Dev To Manager website&lt;/a&gt;.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Creating a writing habit</title>
    
    <link href="https://www.marclittlemore.com/creating-a-writing-habit/"/>
    <published>2021-01-13T00:00:00Z</published>
    
    <updated>2021-01-13T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/creating-a-writing-habit/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;At the start of 2020, I wrote a &lt;a href=&quot;https://www.marclittlemore.com/2019-personal-retrospective/&quot;&gt;review of 2019&lt;/a&gt;. One of my main goals for last year was to find my writing &amp;quot;voice&amp;quot;. The plan was to write more in 2020 but I wrote a grand total of 1 blog post. I&#39;m not sure that a single blog post can really be considered a big win for my writing! 2020 really wasn&#39;t a year that was conducive to getting things done for me. We all struggled in different ways throughtout the pandemic and I couldn&#39;t manage to kickstart a writing habit.&lt;/p&gt;
&lt;p&gt;I&#39;ve always been an avid note taker in meetings but I&#39;d never looped back to use my bullet points to build coherent writing. At some point during 2020 I discovered &lt;a href=&quot;https://roamresearch.com/&quot;&gt;Roam Research&lt;/a&gt; as a tool for networked thought. I moved my note taking into Roam from &lt;a href=&quot;https://dynalist.io/&quot;&gt;Dynalist&lt;/a&gt; but then I discovered an application called &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt;. While very similar to Roam, it was the community that drew me in. A lot of friendly people who were encouraging each other to write more and link ideas using Obsidian as their tool of choice. I continued to use this to create private notes for myself, but have not been great and bringing them together to write more in public.&lt;/p&gt;
&lt;p&gt;Now is the time to change this.&lt;/p&gt;
&lt;p&gt;I&#39;ve been reading a lot about consitency in everything you do. It&#39;s a fundamental for building any habit. My ability to write software and quickly pick up new technologies is born out of my love of learning and applying new languages, frameworks, or libraries when I find them. I tend to spend many evenings reading through documentation and writing code to apply what I&#39;ve learnt. But with public writing I tend to stumble. I can write README files for my code repositories. It&#39;s much easier to be factual. But I get nervous about sharing my thoughts with the outside world. So is this a habit we can learn to get better at? I hope to find out.&lt;/p&gt;
&lt;p&gt;I admired &lt;a href=&quot;https://nathanbarry.com/&quot;&gt;Nathan Barry&lt;/a&gt; of &lt;a href=&quot;https://convertkit.com/&quot;&gt;ConvertKit&lt;/a&gt; for his challenge to &lt;a href=&quot;https://nathanbarry.com/365/&quot;&gt;write for a whole year&lt;/a&gt;. This feels like a long time frame to try and commit to. Instead, I&#39;d like to create a small habit by sharing my thoughts publicly for 30 days. I&#39;m sure &lt;a href=&quot;https://jamesclear.com/&quot;&gt;James Clear&lt;/a&gt; writes about similar ideas in his &lt;a href=&quot;https://jamesclear.com/atomic-habits&quot;&gt;Atomic Habits&lt;/a&gt; book (which is on my Kindle but I&#39;ve not yet read it). I noticed the hashtag &lt;a href=&quot;https://twitter.com/search?q=ship30for30&quot;&gt;#ship30for30&lt;/a&gt; in some tweets from people I follow. It&#39;s a &lt;a href=&quot;https://ship30for30.com/&quot;&gt;community of writers&lt;/a&gt; who are sharing their blog posts for 30 days to build their own habits. While I&#39;ve not joined the community yet, I&#39;m going to attempt to write something new every day without thinking too much about it. If I can do this for 30 days then I&#39;ll be incredibly happy and hopefully will have built a habit and found my writing style. It&#39;s really easy to get bogged down with attempting to share your best work so by being habitual I hope to stop worrying.&lt;/p&gt;
&lt;p&gt;Here&#39;s my plan:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Capture daily thoughts in Obsidian.&lt;/strong&gt; Most days I attempt to write down ideas that come to mind during the day. These are sometimes linked to things I&#39;ve previously thought about and using Obsidian to link my notes, I can hopefully find new ideas to write about.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find a quiet space away from where I normally work.&lt;/strong&gt; I sit in the same room in our house every day and it&#39;s become my office given that I&#39;ve worked at home 100% of the time since March 2020. By finding a different environment within the house, I hope that it&#39;ll inspire me to not think about work and instead free my mind to write from my notes and capture coherent thoughts into a blog post.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set a time.&lt;/strong&gt; A habit needs regularity. I&#39;m planning on writing a blog post after my working day is done. As the whole family are currently locked down and working or schooling from home, I can normally start and finish my day early. This affords me an hour to write from around 4.30 pm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set a word goal.&lt;/strong&gt; Honestly, I&#39;d be happy to write a couple of paragraphs but it seems sensible to aim for a word count each day. I like the #ship30for30 idea of &amp;quot;atomic esssays&amp;quot;. These are up to 250 words and encapsulate a theme. I think this is manageable each day. Although I&#39;d quite like to write 1000+ word essays, I think the consitency of writing matters more than the length of the work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don&#39;t overthink things.&lt;/strong&gt; Says Marc as he writes a list of rules he&#39;s going to try and stick to! Seriously though, I want to write and not worry too much about what I commit to the internet. To write, quickly edit, and share is the main goal.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the first of 30 days of writing. Let&#39;s see if I can keep it up.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Visualise your code in style with pretty screenshots</title>
    
    <link href="https://www.marclittlemore.com/create-pretty-code-screenshots/"/>
    <published>2020-11-14T00:00:00Z</published>
    
    <updated>2020-11-14T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/create-pretty-code-screenshots/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;If you&#39;re a developer, and you hang out on developer Twitter, then you&#39;ll have seen people sharing their hot code tips. They&#39;re often accompanied by code examples and the &amp;quot;hot tip&amp;quot; fire 🔥 or lightbulb 💡 emojis.&lt;/p&gt;
&lt;p&gt;Here&#39;s a great example from &lt;a href=&quot;https://twitter.com/SimonHoiberg&quot;&gt;Simon Høiberg&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;JavaScript Tip 💡&lt;br&gt;&lt;br&gt;Make an argument &amp;#39;required&amp;#39; in JavaScript using this small trick 👇 &lt;a href=&quot;https://t.co/yZ2w5Kd58b&quot;&gt;pic.twitter.com/yZ2w5Kd58b&lt;/a&gt;&lt;/p&gt;&amp;mdash; Simon Høiberg (@SimonHoiberg) &lt;a href=&quot;https://twitter.com/SimonHoiberg/status/1327314535540162565?ref_src=twsrc%5Etfw&quot;&gt;November 13, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;These are &lt;em&gt;not ordinary code screenshots&lt;/em&gt; straight from a code editor. They contain the code, syntax highlighting for the language, and are often embedded in a browser window with a colourful background.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;🔥 It&#39;s a beautiful code screenshot!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you read the comments on these code tweets, you&#39;ll usually see a list of people asking how to create such pretty code screenshots. So how do we do it?&lt;/p&gt;
&lt;p&gt;Read on to find out how to create beautiful code screenshots to share with your Twitter or Instagram followers.&lt;/p&gt;
&lt;h2 id=&quot;websites&quot;&gt;Websites&lt;/h2&gt;&lt;h3 id=&quot;carbonhttpscarbonnowsh&quot;&gt;&lt;a href=&quot;https://carbon.now.sh/&quot;&gt;Carbon&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/carbon-screenshot.png&quot; alt=&quot;Carbon screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://carbon.now.sh/&quot;&gt;Carbon&lt;/a&gt; is one of the easiest tools to use. There are no applications or extensions to install. You can quickly create great looking code screenshots on their website. Their code is also &lt;a href=&quot;https://github.com/carbon-app/carbon&quot;&gt;open source on GitHub&lt;/a&gt; if you want to use it yourself.&lt;/p&gt;
&lt;p&gt;Type your code into their online editor and you can see a prettified version of it inside a Mac-styled window. It allows you to choose a variety of code themes and can syntax highlight your code in various programming languages. You can also alter the background colour, padding, and many other window or editor options. Play around with it to see what you can create.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;If you use &lt;a href=&quot;https://docs.github.com/en/free-pro-team@latest/github/writing-on-github/creating-gists&quot;&gt;GitHub gists&lt;/a&gt; then you can create an image from one of those. Append the identifier of your gist to the Carbon URL and you&#39;ll see a pretty version of your code. For example here&#39;s &lt;a href=&quot;https://gist.github.com/MarcL/0561c83e8445f3ffc801a5a13415abef&quot;&gt;my gist&lt;/a&gt; and it we use the ID &amp;quot;&lt;code&gt;0561c83e8445f3ffc801a5a13415abef&lt;/code&gt;&amp;quot; in the Carbon URL like this &amp;quot;&lt;code&gt;https://carbon.now.sh/0561c83e8445f3ffc801a5a13415abef&lt;/code&gt;&amp;quot; then we see the screenshot above.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;rayso&quot;&gt;Ray.so&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/ray.so-screenshot.png&quot; alt=&quot;Ray.so screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ray.so/&quot;&gt;Ray.so&lt;/a&gt; is another easy site to create beautiful screenshots. It&#39;s created by the team behind &lt;a href=&quot;https://raycast.com/&quot;&gt;Raycast&lt;/a&gt; and it&#39;s a lot more opinionated than Carbon. It only allows a minimal set of colour options but does have both light and dark modes. It supports lots of languages with its syntax highlighting support, allows you to change the title of the window, and also the padding. It offers a more curated experience than Carbon but it&#39;s similarly easy to use.&lt;/p&gt;
&lt;h3 id=&quot;chalkist&quot;&gt;Chalk.ist&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/chalkist-screenshot.png&quot; alt=&quot;Chalk.ist screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://chalk.ist/&quot;&gt;Chalk.ist&lt;/a&gt; is a new site which I found on Twitter in April 2022. It&#39;s created by a developer called &lt;a href=&quot;https://twitter.com/Idered&quot;&gt;Kasper Mikiewicz&lt;/a&gt; and adds some additional features to the normal code screenshot idea. It allows you to add your Twitter handle if you enable it. This will be great for social sharing.&lt;/p&gt;
&lt;p&gt;Another useful feature is a code difference view, like you&#39;d see in your code editor. This is useful for sharing development tutorials or for teaching. While it currently only offers 3 colour themes, it does give you line numbers and a reflection to make it look different to the other sites.&lt;/p&gt;
&lt;h2 id=&quot;vs-code-extensions&quot;&gt;VS Code extensions&lt;/h2&gt;&lt;p&gt;If you&#39;re a developer who uses &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VS Code&lt;/a&gt; there are a couple of extensions which will make creating pretty code screenshots incredibly easy. VS Code extensions add new features, themes, and more to the editor. If you install one of these extensions, you can avoid the need to use an external website and screenshot your code instantly.&lt;/p&gt;
&lt;h3 id=&quot;polacodehttpsmarketplacevisualstudiocomitemsitemnamepnppolacode&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=pnp.polacode&quot;&gt;Polacode&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I&#39;ve used this extension for a while and it&#39;s an easy way to create shareable screenshots of your code without leaving the editor. Install it via the extensions sidebar as you would with other extensions. You can then use the command palette to choose Polacode. This will bring up a new tab where you can cut and past code into the Polacode window.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/polacode-screenshot.png&quot; alt=&quot;Polacode screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;Cutting and pasting code from your open project will allow Polacode to match your current theme. Hit the button underneath the code snippet and you&#39;ll get a lovely code screenshot to share. You can update the background colour and shadow using VS Code&#39;s settings for the extension. The only issue I have with this extension is that it struggles with longer lines of code. These often wrap onto the next line which isn&#39;t as aesthetically pleasing. For this you&#39;ll have to edit the code yourself, perhaps using &lt;a href=&quot;https://prettier.io/&quot;&gt;Prettier&lt;/a&gt; or similar.&lt;/p&gt;
&lt;h3 id=&quot;codesnaphttpsmarketplacevisualstudiocomitemsitemnameadpykecodesnap&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=adpyke.codesnap&quot;&gt;CodeSnap&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Similar to Polacode, CodeSnap is another VS Code extension. Again, you can easily install it via the VS Code extensions sidebar. CodeSnap is a bit more fully featured than Polacode and allows more adjustments in the settings menu. It also has quick access by right clicking in the current window to choose it from the pop-up menu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/codesnap-screenshot.png&quot; alt=&quot;CodeSnap screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;One of the advantages of CodeSnap over Polacode is that it automatically captures any text in your clipboard and creates a pretty code screenshot based on that. It&#39;s also attempts to&lt;/p&gt;
&lt;h2 id=&quot;non-code-screenshots&quot;&gt;Non-code screenshots&lt;/h2&gt;&lt;p&gt;In some cases you might not need to take screenshots of code. Instead you can make pretty screenshots of websites, photos, or any other image you might have. There are a few ways to do this.&lt;/p&gt;
&lt;h3 id=&quot;screelyhttpswwwscreelycom&quot;&gt;&lt;a href=&quot;https://www.screely.com/&quot;&gt;Screely&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/screely-screenshot.png&quot; alt=&quot;Screely screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.screely.com/&quot;&gt;Screely&lt;/a&gt; is a great website which works in a similar way to Carbon. It allows you to use any image file and displays it in a browser-style window. Again, you have the ability to style the image with a variety of options for the background colour, padding, and window type and style. Have fun and make your non-code images beautiful too.&lt;/p&gt;
&lt;h2 id=&quot;using-your-operating-system&quot;&gt;Using your operating system&lt;/h2&gt;&lt;p&gt;While there are websites and code editor extensions you can use, maybe you just want to use your operating system. You can just take screenshots of your code using Windows or Mac OSX. You&#39;ll have your styling and code theme exactly the same as you see it on the screen. Let&#39;s look at how we can do that.&lt;/p&gt;
&lt;h3 id=&quot;windows-users&quot;&gt;Windows users&lt;/h3&gt;&lt;p&gt;Windows 10 makes it simple to save a copy of whatever is on your screen by using the &amp;quot;Print Screen&amp;quot; key on your keyboard. This if normally labelled as &lt;code&gt;PrtScn&lt;/code&gt; on a Windows keyboard and is often located on the top row near the function keys. On laptops, you often have to hold down the function key first before pressing &lt;code&gt;PrtScn&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you press &lt;code&gt;PrtScn&lt;/code&gt; on its own then it will copy the entire screen to the clipboard. You can then paste the image into Microsoft Paint or similar picture editing software.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you use press the &lt;code&gt;Alt key&lt;/code&gt; and &lt;code&gt;PrtScn&lt;/code&gt; then this copies the active window into the clipboard. Again, you&#39;ll need to paste this into your picture editing software to save it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you press the &lt;code&gt;Windows&lt;/code&gt; key and &lt;code&gt;PrtScn&lt;/code&gt; then it&#39;ll save the entire screen as an image file. This will be saved in the &amp;quot;Pictures&amp;quot; folder in a sub-folder called &amp;quot;Screenshots&amp;quot;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you press the &lt;code&gt;Windows&lt;/code&gt; key with &lt;code&gt;Shift&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt;, you can screenshot a portion of the screen. The screen will be covered with an overlay and mouse cursor turns into a plus (+) symbol, which indicates that capture mode is on. At the top of the screen you&#39;ll see a menu bar with the options: rectangular snip, freeform snip, or fullscreen snip. If you choose rectangular or freeform, you can select a region on the screen. This copies that area to the clipboard.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mac-users&quot;&gt;Mac users&lt;/h3&gt;&lt;p&gt;Mac users also have easy ways to create good looking screenshots using the OSX operating system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;Shift + Command + 4&lt;/code&gt; to turn the cursor into a crosshair. This then allows you to drag and select part of the screen to capture as an image. If you release the mouse, this gets saved to your desktop (unless you&#39;ve &lt;a href=&quot;https://www.macworld.co.uk/how-to/change-where-mac-screenshots-saved-3682381/&quot;&gt;changed the folder&lt;/a&gt; you save images to.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you press and hold the space bar before releasing the mouse button, you can move the selected area around the screen before taking the screenshot. This is useful for changing your selection and making it pixel perfect. Release the mouse button to save the image to your selected screenshot folder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you press &lt;code&gt;Shift + Command + 4&lt;/code&gt; (&lt;code&gt;⇧ + ⌘ + 4&lt;/code&gt;) and then press the space bar, this turns the cursor into a camera icon. If you move over any open window and press the left mouse button, this will save an image of the whole window, including the window bar, to your chosen screenshot folder. This is a great way to make screenshots that look great.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;accessibility&quot;&gt;Accessibility&lt;/h2&gt;&lt;p&gt;Don&#39;t forget that posting screenshots of code aren&#39;t accessible. Make sure that you add a link to a &lt;a href=&quot;https://gist.github.com/&quot;&gt;GitHub gist&lt;/a&gt; or to your code repository so that people with any visual impairments can read your code in other ways.&lt;/p&gt;
&lt;p&gt;Got any other ways to style your images that I&#39;ve not mentioned? &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;Send me a message&lt;/a&gt; and let me know.&lt;/p&gt;
&lt;p&gt;👉🏻 Like this post? You should &lt;a href=&quot;https://twitter.com/marclittlemore&quot;&gt;follow me on Twitter&lt;/a&gt;&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Different types of software tests</title>
    
    <link href="https://www.marclittlemore.com/different-types-of-software-tests/"/>
    <published>2020-04-28T00:00:00Z</published>
    
    <updated>2020-04-28T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/different-types-of-software-tests/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;h2 id=&quot;understanding-types-of-software-testing&quot;&gt;Understanding types of software testing&lt;/h2&gt;&lt;p&gt;New developers often don&#39;t know where to start with testing their code. They know that it&#39;s a good idea but there&#39;s an overwhelming amount of information out there. Common questions and comments I hear are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What&#39;s a unit test?&lt;/li&gt;
&lt;li&gt;How does it differ from an integration test?&lt;/li&gt;
&lt;li&gt;Should I be writing end-to-end tests?&lt;/li&gt;
&lt;li&gt;I don&#39;t know where to start!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;ll help you to understand the differences in testing types and make you a little less scared to get started on your software testing journey.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-test&quot;&gt;What is a test?&lt;/h2&gt;&lt;p&gt;This seems like a simple question right but a lot of people get scared when you mention software tests. There&#39;s an easy pattern which you can follow which will simplify how you think about writing tests. It&#39;s known as &lt;strong&gt;Arrange, Act and Assert (AAA)&lt;/strong&gt; and it&#39;s a common way of writing tests.&lt;/p&gt;
&lt;p&gt;Firstly, you need to set up our &lt;strong&gt;pre-conditions&lt;/strong&gt; for the function under test. This is your &lt;strong&gt;Arrange&lt;/strong&gt;. Here you can initialise any objects, or data you need for your test. You can also define the output value you expect from your code after execution.&lt;/p&gt;
&lt;p&gt;Next, you &lt;strong&gt;call your code&lt;/strong&gt; with the specified inputs for your test. This is your &lt;strong&gt;Act&lt;/strong&gt;. Call your function or method with the given input as though you were calling the code in production.&lt;/p&gt;
&lt;p&gt;Lastly, you will &lt;strong&gt;verify your expectations&lt;/strong&gt; based upon either the output value, or the code paths executed. This is your &lt;strong&gt;Assert&lt;/strong&gt;. You know what you expect your code to do so you can now check that it happened. If it didn&#39;t, then the test has failed and your code, or sometimes your test, is incorrect.&lt;/p&gt;
&lt;h2 id=&quot;unit-tests&quot;&gt;Unit tests&lt;/h2&gt;&lt;p&gt;Most of the code that you write will need to interact with other code. It&#39;ll call another function or class method in some way. We want to split up our code into the smallest &lt;strong&gt;unit&lt;/strong&gt; that it makes sense to test. Most of my projects are written in JavaScript using Node.js so for me this is typically a function. We then test this small piece of code. Testing a small amount of code is known as a &lt;strong&gt;unit test&lt;/strong&gt;. If your test still has to allow your code to use an external service, for example an API request or database call, then it&#39;s not a unit test.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;unit test&lt;/strong&gt; should be one of the most basic tests you have. As you want to test the code in isolation, you might have to stub or mock some external dependencies to define specific scenarios. You&#39;ll find that &lt;a href=&quot;https://www.marclittlemore.com/how-to-write-high-quality-unit-tests/&quot;&gt;writing unit tests&lt;/a&gt; often helps to define the structure and design of your code. If it&#39;s difficult to test, it&#39;s often a &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_smell&quot;&gt;code smell&lt;/a&gt; that means you could refactor your code to make it easier to test.&lt;/p&gt;
&lt;p&gt;One major factor which defines a &lt;strong&gt;unit test&lt;/strong&gt; is its speed. Due to the isolation of the code from its other dependencies, you expect these tests to be incredibly fast. You will often end up writing a lot more unit tests than you will the other test types. A typical unit test suite may have thousands of tests and you&#39;d expect a single test to take much less than a second to run.&lt;/p&gt;
&lt;h2 id=&quot;integration-tests&quot;&gt;Integration tests&lt;/h2&gt;&lt;p&gt;While your unit tests aim to be simple and test a single module or function, &lt;strong&gt;integration tests&lt;/strong&gt;, as their name suggest, tests how code modules fit together in your application (i.e. how they integrate). In your unit tests, the aim is to isolate the external dependencies but for your integration tests, you may allow your code to communicate with data sources like your database, or also external APIs. This isn&#39;t a requirement and you can still mock these external dependencies if you want to.&lt;/p&gt;
&lt;p&gt;Integration tests allow you to test the full flow of your application and test how code modules interface with each other. The classic gif below shows what could happen if you have unit tests for your project and no integration tests. Both unit tests appear to pass correctly, but when the code for both is combined, it&#39;s actually incorrect.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://gallery.mailchimp.com/72d3502c470827973d3e8dd63/images/3f98393a-27c9-4576-ba70-38a464d2f8ae.gif&quot; alt=&quot;Unit vs integration tests&quot;&gt;&lt;/p&gt;
&lt;p&gt;Integration tests are generally slower than your unit tests so you&#39;ll often have fewer of them to test complete code paths. You may need configuration of external applications, like databases or HTTP requests to external APIs, if they&#39;re not mocked out. These types of tests are great for testing higher level logic and data flow through your application.&lt;/p&gt;
&lt;p&gt;An example of an &lt;strong&gt;integration test&lt;/strong&gt; is to test the full flow of an API on a server. This may involve starting a server which runs your application code, responding to HTTP requests, authorising a client or user, validating query parameters or body data, connecting to a database or other APIs, converting data to JSON, and eventually returning the data.&lt;/p&gt;
&lt;p&gt;One thing to consider is that you can still mock external dependencies if you want to. It&#39;s often better to have a thin wrapper around an external service which can return fake data in your integration tests. This still allows you to test your complete application flow as an &lt;strong&gt;integration test&lt;/strong&gt;, but not talk to a production API or database. A good example would be an application which uses the Twitter API to send tweets. You wouldn&#39;t want it to send a tweet every time you ran your test suite!&lt;/p&gt;
&lt;h2 id=&quot;functional-end-to-end-tests&quot;&gt;Functional / End-to-end tests&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Function tests&lt;/strong&gt; are also know as &lt;strong&gt;end-to-end (E2E) tests&lt;/strong&gt; and they test the complete functionality of your code. They runn the complete application, usually in a browser, without mocking any external dependencies at all. These types of tests are the slowest to run so you&#39;ll generally have a lot less of these than integration or unit tests.&lt;/p&gt;
&lt;p&gt;An example of a &lt;strong&gt;functional test&lt;/strong&gt; is to verify that a user can sign into a website and that they can view the expected data once authorised. This test would use automated browser interaction to enter the username and password and click on the sign-in button. The test would verify that the title of the next page is as expected and that the user&#39;s data is displayed. This is often done by querying the DOM to determine if elements are present. There are a lot of pre-conditions for this test, such as having a valid username and password combination, and knowning the DOM elements to verify. This means that functional tests are often more brittle and prone to breaking than unit or integration tests. Be wary of relying on user interface data for test expectations as developers or designers often change it!&lt;/p&gt;
&lt;h2 id=&quot;test-doubles&quot;&gt;Test doubles&lt;/h2&gt;&lt;p&gt;When writing tests, there are rarely functions or classes that operate in isolation. Most code interacts with other modules in some way. If you want to isolate your function for testing you can use a &lt;strong&gt;test double&lt;/strong&gt;. This is the generic term for when you replace a production object for testing purposes with a fake. Think of it like a film where a &lt;em&gt;stunt double&lt;/em&gt; replaces the actual actor for the dangerous scenes.&lt;/p&gt;
&lt;p&gt;There are various kinds of test doubles that you can use in your tests and there will be different uses for each of them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dummy&lt;/strong&gt; - A dummy object is used in place of your production object and will match the interface of the thing it replaces. However, you don&#39;t care what it does. Think of it as a simple placeholder which you&#39;ll need as one of your function parameters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stub&lt;/strong&gt; - A stub is an object or function which responds with a pre-programmed response. It normally only responds to what has been programmed for the test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spy&lt;/strong&gt; - A spy is a function which wraps an object and can record information about how the object is used. This is useful for avoiding altering the behaviour of the function, but still determining how it was interacted with.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mock&lt;/strong&gt; - Mocks are fake objects which have fake methods. They also have pre-programmed responses like stubs. However, the main difference to spies and stubs is that they also have pre-programmed expectations. If the mock is not used as expected by your code, the test will fail.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fake&lt;/strong&gt; - A fake object is similar to a stub and it will return some expected values. However, it also implements some of the functionality of the object it&#39;s replacing. An example would be an in-memory data store which replaces the production database.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;watch-the-video&quot;&gt;Watch the video&lt;/h2&gt;&lt;p&gt;Hopefully this been a brief but helpful walkthrough of the different types of software tests you can write. I&#39;ve recorded a video which summarises what you&#39;ve learnt. I talk about the different types of tests you might write as a developer and give some information on test doubles and some of the tools that exist in the JavaScript universe which will help you to write tests. You can watch it here:&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=pMmXJcDLUFA&lt;/p&gt;
&lt;p&gt;If this post was valuable to you, you can &lt;a href=&quot;https://twitter.com/marclittlemore&quot;&gt;follow my journey on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;Want to learn more about testing your JavaScript code? Check out my &lt;a href=&quot;https://www.marclittlemore.com/javascript-testing/&quot;&gt;other JavaScript testing articles&lt;/a&gt; for more tips and techniques!&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Add time and date to your Messenger chatbot with the Chatfuel JSON API</title>
    
    <link href="https://www.marclittlemore.com/how-to-add-time-and-date-to-your-messenger-chatbot-using-the-chatfuel-json-api/"/>
    <published>2020-02-14T00:00:00Z</published>
    
    <updated>2020-02-14T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/how-to-add-time-and-date-to-your-messenger-chatbot-using-the-chatfuel-json-api/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;h2 id=&quot;watch-the-video&quot;&gt;Watch the video&lt;/h2&gt;&lt;p&gt;https://www.youtube.com/watch?v=UDrGpc4Dp8w&lt;/p&gt;
&lt;p&gt;One of the main benefits of creating a Facebook Messenger chatbot is that you don&#39;t have to be present to answer messages for 24 hours per day. Your chatbot should be on hand to do this while you&#39;re fast asleep. What would be even better is if it could understand where your user lives and give them relevant content for their time of day. It could let them know whether your shop is open for business. Or it could send your users the breakfast menu rather than the dinner menu because it knows it&#39;s 9am where they are. This functionality doesn&#39;t exist in Chatfuel by default so let me teach you how to build an API which adds time and date functions to your chatbot.&lt;/p&gt;
&lt;h2 id=&quot;timezones-coordinated-universal-time&quot;&gt;Timezones &amp;amp; Coordinated Universal Time&lt;/h2&gt;&lt;p&gt;When your users talk to you on Facebook Messenger, they may live anywhere around the world. This means we need to know what the time is where they live. Each user can potential live in a different &lt;a href=&quot;https://en.wikipedia.org/wiki/Time_zone&quot;&gt;time zone&lt;/a&gt;. This means we may need to add or subtract a number of hours to the time where your server is to allow for this time difference. Although you might think that it&#39;s always a whole number of hours, there are time zones which have half hours too. But what time is it where your API server is hosted? Well, this depends on where the data centre is that is storing and executing your code. What happens if your server is in New York, which is 5 hours behind &lt;a href=&quot;https://en.wikipedia.org/wiki/Greenwich_Mean_Time&quot;&gt;Greenwich Mean Time&lt;/a&gt; (GMT), but your user is Germany, which is 1 hour ahead of GMT. And what happens if &lt;a href=&quot;https://en.wikipedia.org/wiki/Daylight_saving_time&quot;&gt;daylight saving time&lt;/a&gt; has happened in one of those countries? Calculating the time is hard and things can get even more complicated. That&#39;s why we have a time standard which everyone uses. It&#39;s called &lt;a href=&quot;https://en.wikipedia.org/wiki/Coordinated_Universal_Time&quot;&gt;Coordinated Universal Time&lt;/a&gt; or UTC. The Wikipedia page will explain why it&#39;s not an acronym of CUT! Using UTC allows us to define all times as relative to this standard. If we store all of our times in UTC, life becomes a bit easier.&lt;/p&gt;
&lt;h2 id=&quot;calculating-a-users-time&quot;&gt;Calculating a user&#39;s time&lt;/h2&gt;&lt;p&gt;I&#39;ve set up an &lt;a href=&quot;https://glitch.com/edit/#!/chatfuel-demo-bot?path=routes/timeDate.js:1:0&quot;&gt;example Node.js Express server using Glitch&lt;/a&gt; which exposes an API for all of my Chatfuel demos. The API for this article is available on the following endpoint:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://chatfuel-demo-bot.glitch.me/timedate&quot;&gt;https://chatfuel-demo-bot.glitch.me/timedate&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first thing we need to do is to calculate the user&#39;s local time using UTC. JavaScript (and Node.js) has a global object called &lt;code&gt;Date&lt;/code&gt; which expose a set of methods we can use to determine and manipulate the date and time. First, we need to determine the time right now. We can use the &lt;code&gt;Date.now()&lt;/code&gt; function which will return us the UTC &lt;a href=&quot;https://en.wikipedia.org/wiki/Unix_time&quot;&gt;epoch time&lt;/a&gt;. This is the number of milliseconds since the start of UNIX time (at 00:00 on January 1st, 1970). We can then use this to calculate how many milliseconds difference between the user&#39;s timezone and the time now. Using JavaScript&#39;s &lt;code&gt;Date&lt;/code&gt; object, we can then inspect this and pull out the date and time to use in our chatbot. Let&#39;s look at some code to do this:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Calculate the number of milliseconds in an hour&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - 1000 milliseconds in a second,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - 60 seconds in a minute&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - 60 minutes in an hour&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_HOUR_IN_MILLISECONDS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Pass in an offset in hours which we use to calculate the user&#39;s time&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getUserDateInTimezone&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;offsetInHours&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Calculate the UNIX timestamp in UTC&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; utcDate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
    &lt;span class=&quot;token comment&quot;&gt;// Get the user&#39;s offset in milliseconds&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The offsetInHours could be a negative number if&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// the user&#39;s timezone is behind UTC&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; offsetInMilliseconds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_HOUR_IN_MILLISECONDS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; offsetInHours&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
    &lt;span class=&quot;token comment&quot;&gt;// Create a new Date object which adds the time offset to the time now&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;utcDate &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; offsetInMilliseconds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we&#39;ve written a function to calculate a new &lt;code&gt;Date&lt;/code&gt; object, we can use the built in functions to inspect the data and return it to our user. As we&#39;re using Express to expose an API, we&#39;ll use its routing methods to expose a HTTP GET endpoint which allows our Chatfuel chatbot to make a request using the JSON API. We will allow it to pass through a timezone as a query parameter which Facebook Messenger can provide us. We can then set some user attributes which are returned to Chatfuel and can be used in our chatbot flows. Let&#39;s look at how we can do this:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; express &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;express&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; router &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; express&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ... add the time calculation code from above here&lt;/span&gt;

router&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Allow a query parameter called &quot;timezone&quot; to be passed with the GET request&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Default it to zero if we don&#39;t send one&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;timezone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Calculate the user&#39;s current Date in UTC using our code from above&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; userDateInTimezone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUserDateInTimezone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timezone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
    &lt;span class=&quot;token comment&quot;&gt;// Inspect the Date object and pull out the data we need&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Build an object which we can return to Chatfuel&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dateObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Get the day&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userDateInTimezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// Note that month is zero-indexed (from 0 for January to 11 for December)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// For it to make sense for an actual month number, add one to it!&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userDateInTimezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMonth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// Get the full 4-digit year e.g. 2020&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userDateInTimezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFullYear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Get the number of hours&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userDateInTimezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getHours&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Get the number of minutes&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userDateInTimezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMinutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Get the number of seconds&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userDateInTimezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Get a standard ISO-8601 formatted string&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;isoTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userDateInTimezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toISOString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Format the data so that Chatfuel can set user attributes&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; userAttributes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;set_attributes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dateObject
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Return a JSON response that Chatfuel can understand&lt;/span&gt;
    response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userAttributes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// If we&#39;re putting our routes into separate modules for clarity&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// then we need to export the routes for use in the Express application&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; router&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last thing you need to do is to use this code in your Chatfuel chatbot. You can use the redirect blocks to check the value of &amp;quot;hours&amp;quot; returned after calling your API. If they are greater than 18, or 6pm, you could consider it to be evening and change the user responses accordingly. In a similar way, you can check if the value is greater than 6, or 6am, and then consider it to be breakfast time. Once you&#39;ve got the user&#39;s time and date, you can give timely responses to your user. Watch &lt;a href=&quot;https://www.youtube.com/watch?v=UDrGpc4Dp8w&quot;&gt;my video&lt;/a&gt; for more examples.&lt;/p&gt;
&lt;p&gt;All of the code is available on my &lt;a href=&quot;https://glitch.com/~chatfuel-demo-bot&quot;&gt;Chatfuel demo Glitch project&lt;/a&gt;. Feel free to clone it and use it for your own APIs. If you spot any errors, or have any questions, then please &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;send me a message&lt;/a&gt;. I love to hear from people and I&#39;m always happy to answer your questions.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>2019: Personal Retrospective</title>
    
    <link href="https://www.marclittlemore.com/2019-personal-retrospective/"/>
    <published>2020-01-06T00:00:00Z</published>
    
    <updated>2020-01-06T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/2019-personal-retrospective/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I started my 2020 by thumbing through Twitter as I often do, and often try to avoid doing. Between the technology and political tweets were personal 2019 reviews and retrospectives.
In software development, we use the idea of the &lt;a href=&quot;https://www.scrum.org/resources/what-is-a-sprint-retrospective&quot;&gt;agile sprint retrospective&lt;/a&gt;). A sprint is a period in which you attempt to produce new features for your software application. A sprint retrospective is a team meeting when you discuss how the last sprint went. What went well, what could have gone better, and any actions to make sprints better. These personal reviews took this idea and applied it to their lives. Here is my personal 2019 retrospective.&lt;/p&gt;
&lt;h2 id=&quot;what-went-well&quot;&gt;What went well?&lt;/h2&gt;&lt;p&gt;One of my big goals every year is to spend quality time with &lt;a href=&quot;https://clarelittlemore.com/&quot;&gt;Clare&lt;/a&gt; and our children. This year, we had a fantastic holiday to Disney World and Universal Studios in Florida. We planned to go in 2018, but issues with my feet changed our plans. While I had some toe issues before and after the holiday, we managed to walk for five miles on most days. A fantastic holiday! As the family loves theatre, we saw a surprising amount of musicals and plays throughout the year. The children also performed in four shows and I&#39;m proud of how far they&#39;ve come with their acting and singing.&lt;/p&gt;
&lt;p&gt;Another major goal was to ensure I saw more of my friends. Once you hit your 40s, you end up with more family commitments so seeing old friends becomes harder. This year I had two major get-togethers with a great group of mates. The first one involved far too much gin! As we all head towards the wrong side of our 40s, I also managed to celebrate a couple of 50th birthdays. Music has always been a major part of my life and I saw more live music in 2019. Thanks to some tickets for my birthday, I saw &lt;a href=&quot;https://open.spotify.com/album/2D67AgXVKjql7tniG3jQKl&quot;&gt;The Midnight Hour&lt;/a&gt; in Liverpool. This ended with us meeting Ali Shaheed Muhammad from A Tribe Called Quest. A big-time legend from my hip-hop youth. It was also great to see the Loop Daddy himself, &lt;a href=&quot;https://www.marcrebillet.com/&quot;&gt;Marc Rebillet&lt;/a&gt; at Manchester’s Albert Hall in November. If you don&#39;t know who he is then Google him. Seeing a man improvising songs in a pair of boxer shorts, a silk dressing gown, and patent leather shoes is something to behold. Part comedy. Part sex symbol. Yet he&#39;s a brilliant musician and singer. One of the best live shows I’ve seen! The final gig of the year was &lt;a href=&quot;https://en.wikipedia.org/wiki/Kruder_%26_Dorfmeister&quot;&gt;Kruder &amp;amp; Dorfmeister&lt;/a&gt;. What I thought was going to be a chilled trip-hop experience turned into a middle-aged rave. Banging tunes and a great light show made it a fantastic night.&lt;/p&gt;
&lt;p&gt;For the past three years, I&#39;ve been a Software Engineering Team Lead at the BBC. This means that I build and manage high performing teams and don&#39;t write much code during the day any more. Most of my technology learning happens in the evenings after the kids have gone to bed (which gets much later as they get older!). I wondered how much software I wrote in 2019, so I had a look at my &lt;a href=&quot;https://github.com/MarcL&quot;&gt;GitHub commits&lt;/a&gt; for the whole year. I surprised myself with the number of new technologies I picked up, most of which I hadn&#39;t touched before. These were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://botkit.ai/&quot;&gt;BotKit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/&quot;&gt;Google Cloud Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt; location-based API which imports data from &lt;a href=&quot;https://airtable.com/&quot;&gt;Airtable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gatsbyjs.org/&quot;&gt;Gatsby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://graphql.org/&quot;&gt;GraphQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.shopify.co.uk/&quot;&gt;Shopify&lt;/a&gt; applications and &lt;a href=&quot;https://polaris.shopify.com/&quot;&gt;Polaris&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sparkar.facebook.com/ar-studio/&quot;&gt;Spark AR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/MarcL/markdown-to-mailchimp&quot;&gt;Markdown to Mailchimp&lt;/a&gt; email generation (as I dislike the Mailchimp WYSIWYG editor!)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/marcl/og-image&quot;&gt;Open Graph image generation&lt;/a&gt; using serverless functions&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zeit.co/docs&quot;&gt;Zeit Now 2.0&lt;/a&gt; (moving a Now v1.0 API to use their new serverless functions)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://firebase.google.com/&quot;&gt;Firebase&lt;/a&gt; serverless function to scrape Amazon book rankings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://www.typeform.com/blog/inspiration/business-ideas/&quot;&gt;&amp;quot;Scratching your own itch&amp;quot;&lt;/a&gt; is always a good way to gain more knowledge of areas you&#39;re unfamiliar with. Since 2017 I&#39;ve become an expert in building &lt;a href=&quot;https://www.marclittlemore.com/bots/&quot;&gt;Messenger chatbot experiences&lt;/a&gt; and I&#39;ve often used chatbot platforms like &lt;a href=&quot;https://chatfuel.com/&quot;&gt;Chatfuel&lt;/a&gt; to build them. As I was consulting for a Messenger-based travel company, they were looking for cheaper options. This led me to build a solution using BotKit hosted using Google Cloud Platform. This was a fantastic learning experience for GCP App Engine, Cloud Firestore. It also allowed me to create a location-based API using Firebase functions and Airtable. Unfortunately, the chatbot isn&#39;t yet live due to a lack of money from the company involved. See also &amp;quot;what didn&#39;t go well&amp;quot;!&lt;/p&gt;
&lt;p&gt;A big win for me this year was deciding to refactor my wife Clare&#39;s author website using Gatsby. The previous incarnation used Wordpress and needed some love. Gatsby also allowed me to learn some GraphQL in the process. This led me to build some Shopify integrations for my chatbot API and understand their integration. My chatbot API needed a bit of love this year as I&#39;d left it to stagnate using the old Zeit Now 1.0 framework. This lets you deploy an Express server and spin up compute instances as needed. Zeit used to host the whole API this way. With their new &lt;a href=&quot;https://zeit.co/blog/now-2&quot;&gt;Zeit Now 2.0&lt;/a&gt;, they pivoted to a serverless approach. The advantage of this allows the API to scale on a per endpoint basis. Yet, this requires a separate function for each API endpoint. While this was a big rewrite on my part, it made my application API much simpler. One observation I have of my 2019 project is that one learning moment leads to another. A problem or unknown solution in the first project often brought a discovery of a new technology which could help. I just wish I&#39;d done more &lt;a href=&quot;https://www.swyx.io/writing/learn-in-public&quot;&gt;learning in public&lt;/a&gt; as &lt;a href=&quot;https://twitter.com/swyx&quot;&gt;Shawn &amp;quot;SWYX&amp;quot; Wang&lt;/a&gt; suggests.&lt;/p&gt;
&lt;p&gt;My work life was pretty good for most of the year. The BBC launched one of its big diversity initiatives - the &lt;a href=&quot;https://www.bbc.co.uk/blogs/internet/entries/29697bc8-9b32-4b41-8d42-33f1ed625859&quot;&gt;&amp;quot;Step Into Tech&amp;quot;&lt;/a&gt; scheme and I became an advocate and advisor for this. It was a 14-week training programme to teach the basics of software engineering to women who were looking for a career change. I helped to mentor four of the 2018 cohort and encouraged them to shadow our team to see what we did. With the help of a senior engineer, all four women made a change to our codebase. They deployed their changes to our live applications, something they should be proud of. One of the women joined us in September, and it&#39;s been rewarding to mentor and guide her new journey. I also ended up spending a lot of 2019 hiring and building up my engineering team. By the end of the year, I&#39;d interviewed and hired four fantastic software engineers. Big thanks to my team for their help and support in this. A highlight of my working year was attending the amazing &lt;a href=&quot;https://theleaddeveloper.com/&quot;&gt;Lead Developer conference&lt;/a&gt; in London in June 2019. As a software engineering team lead, it&#39;s much harder to see the impact you make on a team as you no longer write code. This brought together some excellent speakers from the engineering teams across the world. It was fantastic to hear presentations from likeminded team leads, and to hear the challenges of building high functioning teams.&lt;/p&gt;
&lt;p&gt;One of my biggest wins of the year was to use a habit tracker, a Google Sheet, to see where I was spending my time. I wanted to practice &lt;a href=&quot;https://en.wikipedia.org/wiki/Quantified_self&quot;&gt;quantified self&lt;/a&gt; and so recorded statistics about my life for the whole of 2019. I tracked the parts of my life I thought I should improve, such as when I went to bed, when I exercised (or didn&#39;t!) and so on. One of these was reading. I&#39;ve often found it tricky to stick to a regular reading schedule but by recording when I read, I tricked myself into reading more. I finished ten books in 2019 so that&#39;s a personal win for me.&lt;/p&gt;
&lt;h2 id=&quot;what-could-have-gone-better&quot;&gt;What could have gone better?&lt;/h2&gt;&lt;p&gt;After my &lt;a href=&quot;https://www.marclittlemore.com/how-i-almost-died/&quot;&gt;near-death experience&lt;/a&gt; in 2014, I knew I should attempt to get more exercise. I&#39;ve encountered a few health issues following my hospitalisation so I spent 2019 attempting to get fit. This began well but as has happened in the past, I wrestled with more issues with my toe. The amputation of my right great toe creates more pressure on the next toe. This means a greater potential for ulcers (holes) in it. It&#39;s a constant balance between more cardio exercise whilst avoiding further complications. My habit tracker showed that I spent 26% of my year exercising. I should have spent more time getting my bike in order and cycling but I didn&#39;t prioritise this. This would avoid any major impact on my feet versus running, which my podiatrist advises me to avoid.&lt;/p&gt;
&lt;p&gt;Since 2017 I&#39;ve become an expert at &lt;a href=&quot;https://www.marclittlemore.com/bots/&quot;&gt;building Messenger chatbots&lt;/a&gt;. I acted as a consultant for a London-based travel company based in London at the end of 2018 and the beginning of 2019. This was a side-project which I hoped would lead to more consultancy work. I leveraged the BotKit framework to build a cost-effective bot without the cost of an external platform such as Chatfuel. While this was a great learning experience, I spent a lot of time working on the project to finish the required features. As the company is a startup, it had little spare cash and failed to pay my final invoices. Given the effort I put into the project, this caused frustrations on my part. I had a similar experience in 2018 where I built a &lt;a href=&quot;https://dialogflow.com/&quot;&gt;Dialogflow&lt;/a&gt; and &lt;a href=&quot;https://www.twilio.com/&quot;&gt;Twilio&lt;/a&gt; chatbot for an American entrepreneur and no payment materialised. It&#39;s made me realise that I need to become better at negotiating full payment and contracts up-front.&lt;/p&gt;
&lt;p&gt;As well as developing a better reading habit, I wanted to build a habit of regular writing. I missed the mark with this with only 11% of my days spent writing. My one success here was writing and releasing a 30-page guide to JSON for chatbots (which you can get &lt;a href=&quot;https://www.marclittlemore.com/bots/&quot;&gt;here&lt;/a&gt;). I failed to find my writing voice so this is a big goal for me in 2020.&lt;/p&gt;
&lt;p&gt;Since late 2017 I&#39;ve had plans to build an online course. I love helping people and want to share my knowledge with us to generate a side income from it. My JSON guide was my initial &amp;quot;lead magnet&amp;quot; to encourage people onto my mailing list but I failed to capitalise on this. I spent too much time thinking about what I should do rather than creating. I should have been releasing early and often and learning what people want. Paralysis by analysis happens far too often so I&#39;d like to avoid this in 2020.&lt;/p&gt;
&lt;h2 id=&quot;plans-for-2020&quot;&gt;Plans for 2020&lt;/h2&gt;&lt;p&gt;My main goal for 2020 is being addressed by this blog post as I plan to find my writing &amp;quot;voice&amp;quot;. Since school, I&#39;ve often told myself &amp;quot;I&#39;m not very good at English&amp;quot; and &amp;quot;I&#39;m much better at maths&amp;quot;. I often write in my day job, be it technical papers or tickets, or emails to senior stakeholders. But I rarely write for a wider audience. The plan for 2020 is to blog more often and do a lot more &lt;a href=&quot;https://www.swyx.io/writing/learn-in-public&quot;&gt;learning in public&lt;/a&gt;. I&#39;m will endeavour to write about the technologies that I&#39;m learning, how I&#39;m building my online course, and more.&lt;/p&gt;
&lt;p&gt;As 2019 wasn&#39;t a great year for fitness, I need to get much fitter this year. As I enjoy cycling, I&#39;m going to get outside more on my mountain bike, both alone and with the family. My wife and I are also trying to get the family into a regular habit of getting outside to walk more. We live near some beautiful countryside and it seems a shame not to see more of it. This will combine cardio exercise with fresh air which always helps to clear your head.&lt;/p&gt;
&lt;p&gt;Since stomach surgery in 2014, I&#39;ve had a more difficult relationship with food. I love cooking and have always enjoyed eating great food but I often struggle to eat well. I have to consume much smaller portion sizes and I&#39;m often sick after eating. I now find meat more difficult to digest so I plan to lean towards a more vegetarian diet. This has obvious health benefits for me and also has the potential to help with &lt;a href=&quot;https://www.bbc.co.uk/news/science-environment-49238749&quot;&gt;climate change&lt;/a&gt;. I&#39;d love to head towards a mostly plant-based diet but I like cheese too much. I&#39;ve started the year as more of a &lt;a href=&quot;https://www.bbcgoodfood.com/howto/guide/what-flexitarian-diet&quot;&gt;flexitarian&lt;/a&gt;, with a much-reduced meat diet. I&#39;ll see where this year takes me but I may well be completely vegetarian by the end of the year.&lt;/p&gt;
&lt;p&gt;This year I want to get some traction on my side projects and aim to make a reasonable side income through it. I&#39;ll be publishing &lt;a href=&quot;https://www.youtube.com/c/marclittlemore&quot;&gt;more videos&lt;/a&gt; on YouTube and this will be part of a marketing push to drive more traffic to my website. I am committing to publishing a short course in the first quarter of 2020. I&#39;ll be doing some research in the next couple of weeks and aim to release an outline before the end of January 2020.&lt;/p&gt;
&lt;p&gt;As well as spending more time with the family, I want to get back to some of my old hobbies. I spend most of my evenings reading about technology or writing new software. This hasn&#39;t allowed me enough time for non-tech related hobbies. I increased my rate of reading in 2019 and the goal for 2020 is to read twenty books. I also want to resurrect my old love of making music. I&#39;ve not made any new tracks since the early 2010s so it&#39;s time to dust off the audio interface and MIDI keyboard and get back to making electronic music. 2020 could be the year I dust off the decks and get &lt;a href=&quot;https://www.djcruze.co.uk/&quot;&gt;DJ Cruze&lt;/a&gt; rocking the house again. Watch this space!&lt;/p&gt;
&lt;p&gt;I&#39;ve set myself some audacious goals for 2020 but there&#39;s little point in not trying to better myself. I look forward to reviewing them all in 2021 to see how far I&#39;ve travelled. Will I have achieved most of them? Let&#39;s see next year.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This post is inspired &lt;a href=&quot;https://www.knutmelvaer.no/blog/2020/01/2019-a-personal-retrospective/&quot;&gt;Knut Melvær&lt;/a&gt;, &lt;a href=&quot;https://lengstorf.com/2019-personal-retrospective/&quot;&gt;Jason Lengstorf&lt;/a&gt;, &lt;a href=&quot;https://justinjackson.ca/2019-review&quot;&gt;Justin Jackson&lt;/a&gt;, &lt;a href=&quot;https://blog.stephsmith.io/another-year-under-the-sun/&quot;&gt;Steph Smith&lt;/a&gt;, and &lt;a href=&quot;https://www.madisonkanna.com/2019-in-review/&quot;&gt;Madison Kanna&lt;/a&gt;, while listening to the wonderful French band &lt;a href=&quot;http://wearephoenix.com/&quot;&gt;Phoenix&lt;/a&gt; and the album &lt;a href=&quot;https://open.spotify.com/album/2TVvPbLNPTCZS8lPHs1rZW?si=LV0DLrnpRKmnulbSsLIHcw&quot;&gt;Wolfgang Amadeus Phoenix&lt;/a&gt;. A classic from 2009.&lt;/em&gt;&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>How to validate an email adress using the Chatfuel JSON API</title>
    
    <link href="https://www.marclittlemore.com/how-to-validate-an-email-address-using-the-chatfuel-json-api/"/>
    <published>2019-11-24T00:00:00Z</published>
    
    <updated>2019-11-24T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/how-to-validate-an-email-address-using-the-chatfuel-json-api/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Facebook Messenger chatbots are a great addition to your marketing toolbox but they have one big problem.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Facebook controls the platform and can change its rules when it wants to.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Their &lt;a href=&quot;https://developers.facebook.com/docs/messenger-platform/policy/policy-overview/#current_policy&quot;&gt;recent changes to their broadcast and subscription messaging rules&lt;/a&gt; means it&#39;s more difficult to contact users after 24 hours if they&#39;re not engaging with your bot. While stopping people from abusing Messenger messages is the right thing, how to you keep your user&#39;s engaged?&lt;/p&gt;
&lt;p&gt;I always recommend that you should consider Facebook Messenger as one of your marketing channels but you always need a multi-channel marketing approach. That&#39;s why it&#39;s useful to ask your chatbot users for their email address and add them to an email list in your Email Service Provider (ESP) like &lt;a href=&quot;https://mailchimp.com/&quot;&gt;Mailchimp&lt;/a&gt; or &lt;a href=&quot;https://convertkit.com/&quot;&gt;ConvertKit&lt;/a&gt;. Messenger allows you to ask for an email address using one of its quick replies but if the email isn&#39;t available, or the user chooses to type it in, how do we make sure we&#39;ve got valid data?&lt;/p&gt;
&lt;h2 id=&quot;validating-an-email-using-chatfuel&quot;&gt;Validating an email using Chatfuel&lt;/h2&gt;&lt;p&gt;Let&#39;s take a look at how we can validate a given user email using Chatfuel as our chatbot platform. Chatfuel allows you to ask for a user&#39;s email using it&#39;s &amp;quot;Save User Email&amp;quot; element. Adding this to your chatbot flow allows you to send a quick reply to your and ask for an email address which is then stored in a Chatfuel user attribute. If you pass this to your server side API using the JSON API plugin, you can&#39;t validate it on the server to determine if it&#39;s valid.&lt;/p&gt;
&lt;h2 id=&quot;watch-the-video&quot;&gt;Watch The Video&lt;/h2&gt;&lt;p&gt;Take a look at how I build this email validation using &lt;a href=&quot;https://glitch.com/&quot;&gt;Glitch&lt;/a&gt; and a Node.js Express web server together with Chatfuel&#39;s JSON API.&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=r4h4DD1DNE8&lt;/p&gt;
&lt;h2 id=&quot;how-to-validate-an-email-address&quot;&gt;How to validate an email address?&lt;/h2&gt;&lt;h3 id=&quot;using-a-regular-expression&quot;&gt;Using a regular expression&lt;/h3&gt;&lt;p&gt;Validating email addresses correctly can be tricky. One way is to use something called a &lt;a href=&quot;https://en.wikipedia.org/wiki/Regular_expression&quot;&gt;regular expression&lt;/a&gt;. This is a sequence of characters that define a search pattern that you attempt to match against. You&#39;ll find hundreds of different email regular expressions on the internet which give you various queries to validate against. However, they often come with caveats that they make a trade-off between speed and accuracy. You can use one of these to match the given email address with a specific pattern but you may incorrectly accept or reject some email addresses. The official regular expression which matches all email addresses is &lt;a href=&quot;http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html&quot;&gt;ridiculously big&lt;/a&gt;. I can&#39;t imagine attempting to debug this if it doesn&#39;t work as expected in your programming language. While a regular expression will confirm if your email matches a specific pattern, we can&#39;t confirm whether we can send an email to it.&lt;/p&gt;
&lt;h3 id=&quot;send-the-user-an-email&quot;&gt;Send the user an email&lt;/h3&gt;&lt;p&gt;A nicer way is to determine if you can &lt;a href=&quot;https://medium.com/hackernoon/the-100-correct-way-to-validate-email-addresses-7c4818f24643&quot;&gt;send an email to the given email address&lt;/a&gt;. This works really well for verifying an email address when signing up for an online service, but it&#39;s not that easy when you need to validate an email in a chatbot flow. Flows in Messenger or WhatsApp can&#39;t wait for your user to check their email and click on a link.&lt;/p&gt;
&lt;h3 id=&quot;checking-the-mx-record&quot;&gt;Checking the MX record&lt;/h3&gt;&lt;p&gt;However, we can do something better by checking that the email has a valid domain to send email to. If we find a domain name in the given email address, we can check the DNS (Domain Name Server) records for the domain. These records can tell us the IP address on which the domain is hosted. They can also be used to see if email can be sent to the domain. For this we can check if the domain has an MX (Mail eXchange) record. If it does, we can send email to it.&lt;/p&gt;
&lt;h3 id=&quot;writing-an-api-service-to-check-for-a-valid-email-domain&quot;&gt;Writing an API service to check for a valid email domain&lt;/h3&gt;&lt;p&gt;Checking for an MX record does take some time so first we can verify that the email address has the format of an email address. For this you can check for the presence of an @ symbol. If it exists then the user has at least attempted to give us something which is similar to an email address! If you retrieve the rest of the string after the @ symbol, this represents the domain name. In my video below, I use Node.js and the standard &lt;a href=&quot;https://nodejs.org/docs/latest/api/dns.html&quot;&gt;dns library&lt;/a&gt; to determine if an MX record exists for that domain. If it does, then you should be able to send email to that address and we can consider it valid.&lt;/p&gt;
&lt;p&gt;Here&#39;s some example code to retrieve an email address and validate it.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dns &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;dns&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The dns library will return:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ENOTFOUND - if no records exist at all for the domain&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ENODATA - if no MX record was found for the domain,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// or if the user data was invalid&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;hasMxRecordError&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    error &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ENOTFOUND&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ENODATA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// A promise-based method to determine if the&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// MX record exists for the given email&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;checkValidMxRecord&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Determine if email is in the correct format&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;@&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;isEmail&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Invalid email address does not contain @ symbol&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Split the email address and destructure to find the domain&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// TODO: You could also validate the username if you wanted to&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; domain&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;@&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Read the DNS records and see if an MX record exists for the domain&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; dns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolveMx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; addresses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Check if any errors occurred&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasMxRecordError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Email has invalid MX record&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
            &lt;span class=&quot;token comment&quot;&gt;// You may not need the records but we can return them in case&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addresses&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make my APIs more robust, I always return user attributes back to Chatfuel to inform it whether an API has succeeded or not. This allows us to avoid hard coding any messages in the API and gives back control to Chatfuel, or whichever chatbot platform you use, and use that to display the correct user messaging. It&#39;s a good way to inform the user that they&#39;ve potentially typed in their email incorrectly. You can also use this validation check to gate your content to only allow access to people who supply their email address.&lt;/p&gt;
&lt;p&gt;Of course, this method has a flaw in that we can&#39;t confirm that the username is valid without sending an email to it. We&#39;ll find this out when we send an email to them from our mailing list but at least we got halfway there.&lt;/p&gt;
&lt;p&gt;I hope you found this useful. All of the code is available on my &lt;a href=&quot;https://glitch.com/~chatfuel-demo-bot&quot;&gt;Chatfuel demo Glitch project&lt;/a&gt;. Feel free to clone it and use it for your own APIs. If you spot any errors, or have any questions, then please [send me a message]/(/contact/). I love to hear from people and I&#39;m always happy to answer your questions.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-emerald-100 border-emerald-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-emerald-800&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;/div&gt;&lt;/div&gt;</content>
    
  </entry>
  
  <entry>
    
    <title>Lessons Learned From Running A Code Club</title>
    
    <link href="https://www.marclittlemore.com/lessons-learned-from-running-a-code-club/"/>
    <published>2017-04-01T00:00:00Z</published>
    
    <updated>2017-04-01T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/lessons-learned-from-running-a-code-club/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Here in the UK we have an organisation called &lt;a href=&quot;https://www.codeclub.org.uk/&quot;&gt;Code Club&lt;/a&gt; which aims to help all children learn to code. It&#39;s a network of volunteers and educators who run free coding clubs for 9 to 11 year olds. I have two children, aged 7 and 9, and I want to encourage them to learn more about computing and technology and not just watch gaming videos on YouTube. When my eldest son moved from his infant school to his junior school a couple of years ago, the teachers told me how ill prepared they were for the move from Information Communication Technology (ICT) to computing. This was due to ICT being more about how to use a computer rather than how to write computer code. Having over 20 years of professional programming experience, I thought that this would be a good time to get involved to help my son&#39;s school. Unfortunately, due to &lt;a href=&quot;https://www.marclittlemore.com/how-i-almost-died/&quot;&gt;almost dying&lt;/a&gt;, it took a bit longer to help out thank expected but I finally started running a Code Club this year. It&#39;s harder work than I imagined but it&#39;s a fantastically rewarding experience. Here are a some lessons I have learned along the way which could help others.&lt;/p&gt;
&lt;h2 id=&quot;lessons-learned-in-running-my-code-club&quot;&gt;Lessons Learned In Running My Code Club&lt;/h2&gt;&lt;p&gt;My original plan for my Code Club was to teach one group of children for around 10 weeks. I decided to only run the club for the Y5 and Y6 pupils, ages 9 to 11, and my plan was to start with the basics and continue to build on the projects each week. When the school announced the Code Club it was over-subscribed with around 30 children enrollong. Instead, I ran the club for two 5 week periods with 15 students in each class.&lt;/p&gt;
&lt;h3 id=&quot;teaching-is-hard&quot;&gt;Teaching is hard&lt;/h3&gt;&lt;p&gt;My wife is an English teacher of over 17 years. She laughed at me when I returned home after the first lesson and exclaimed &amp;quot;that was hard work&amp;quot;. Any teachers reading this will also be laughing at my naïvety. I blindly assumed that all children would listen and implement the coding lessons I&#39;d prepared. They did. Sometimes.&lt;/p&gt;
&lt;p&gt;Some children will listen. Others, not so much. There&#39;s noise, there are questions, somebody needs to go to the toilet, everyone needs to go to the toilet, somebody is showing the whole class their project and so on. It&#39;s great fun but it can get chaotic. There&#39;s a great article about &lt;a href=&quot;https://blog.codeclub.org.uk/2016/07/13/embracing-chaos-in-your-code-club/&quot;&gt;embracing the chaos&lt;/a&gt; on the Code Club website that everyone should read. I quickly realised that allowing them to do their own thing was part of them building their confidence in computer programming.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Embrace the chaos. It may be hard initally if you&#39;re not an educator and aren&#39;t used to children in a classroom environment. It gets easier and much more fun pretty quickly though.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;class-size&quot;&gt;Class size&lt;/h3&gt;&lt;p&gt;I had a class size of 15 children in each group. As a non-teacher, this felt like a good class size to be able to control. For the first two weeks of Code Club I was alone in teaching until another parent helper had received his DBS check. It is harder than I imagined to be able to teach a class and answer all of their questions with one teacher and 15 children. Once an additional pair of hands was on-board, it made teaching much easier and big thanks to Dan for helping every week.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Make teaching easier by having a ratio of at least 1 adult helper to 6 or 7 children.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;a-variety-of-programming-experience&quot;&gt;A variety of programming experience&lt;/h3&gt;&lt;p&gt;I chose to use &lt;a href=&quot;https://scratch.mit.edu/&quot;&gt;Scratch&lt;/a&gt; for all of my projects. It&#39;s a good choice for drag-and-drop programming and introduces the children to the basics of computing in a simple environment. It avoids complicated syntax errors when using a language such as Python or JavaScript. Most of the children had used Scratch at school but there was a range of abilities. Some questioned the basics while others raced ahead adding new features to their projects. My Code Club is in the school library so I always introduce the lessons with a set of slides on the TV. I introduce the lesson and talk through the basics but then have a set of enhancements that the children could work towards if they have the time. This helped to set the goal for the slower students while challenging the quicker ones. I also shared my version of the game as a challenge to make their&#39;s better than mine.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Consider extra features that the children could add to the project. If you&#39;re not an experienced programmer why not write a list of ideas that the children could add to your resources, even if you&#39;re not sure how to do them.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;preparation-of-lessons-is-key&quot;&gt;Preparation of lessons is key&lt;/h3&gt;&lt;p&gt;It&#39;s perhaps an obvious point to make, especially if you&#39;re a teacher, but you should always run through the Code Club resources a couple of nights before. This helped me to understand the Code Club approach to the problem which often differed to my own. As I mentioned above, I always create a &amp;quot;stretch goal&amp;quot; for the children to try and achieve. This could involve improving the graphics, adding start and end game screens, adding sound and any other fun ideas I could think of. This ended up as a great challenge for me too. Scratch is restrictive when you&#39;re used to writing complex applications or games but working out a Scratch-based way of thinking was great fun.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Run through the lesson the night before and you&#39;ll encounter any issues that the children will have before they do.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;using-sound&quot;&gt;Using sound&lt;/h3&gt;&lt;p&gt;I ran the &amp;quot;Rock Band&amp;quot; project on my first club. The sound of 15 computers blaring out sound effects can be tricky to manage, especially when the children find the music! Ask them to keep the noise down. If you&#39;re lucky they will. You may allow the use of headphones too.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Projects using sound can make for a noisy classroom!&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;adding-more-graphics&quot;&gt;Adding more graphics&lt;/h3&gt;&lt;p&gt;Scratch provide the children with great graphics and sound effects but they want to make each project their own. For my initial Rock Band lesson, I also showed them a &lt;a href=&quot;https://scratch.mit.edu/projects/153704607/&quot;&gt;Pikachu version&lt;/a&gt; as I knew seeing a Pokemon would get them engaged. I&#39;d put the graphics on a USB stick but formatted it on my Mac. My fatal mistake was not formatting it for a FAT32 file system so it didn&#39;t work and there was some disappointment. The other issue to consider is whether the computers have internet access and whether the school allow access. You&#39;ll find that they want to download graphics to personalise their projects so you&#39;ll need to decide if this is possible. Thankfully, most schools have good safety policies and internet filtering. If the children can use their own graphics, their projects become their own.&lt;/p&gt;
&lt;p&gt;Show the children the costume editor in Scratch and they can start creating their own sprites or variations of the existing ones. They love doing this but be aware that they will spend the whole lesson drawing sprites if they can.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Discuss with your school what their internet policy is for the children. Allow them to get creative with their own graphics.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;rubber-duck-debuggingrubber-duck-debugging&quot;&gt;&lt;a href=&quot;https://www.marclittlemore.com/rubber-duck-debugging/&quot;&gt;Rubber duck debugging&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In software engineering we have a method of finding problems in our code known as &lt;a href=&quot;https://www.marclittlemore.com/rubber-duck-debugging/&quot;&gt;rubber duck debugging&lt;/a&gt;. The idea is that in telling the problem to someone else, such as a rubber duck, you&#39;ll actually understand the problem and will explain the solution yourself. If you&#39;ve never tried it before, please do. It works! The children sometimes couldn&#39;t work out why their code didn&#39;t work as expected. Instead of pointing out what was wrong, I&#39;d ask them to explain what they&#39;ve done. I&#39;d ask questions about each line of Scratch code. Most of the children would instantly see the problem and could fix it themselves. Computer programming is all about failure. You have to get it wrong to fully understand how to correct it.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: If children have problems, ask them questions about their code. Try not to immediately offer solutions. They&#39;ll often work it out themselves.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;schools-cant-afford-the-best-technology&quot;&gt;Schools can&#39;t afford the best technology&lt;/h3&gt;&lt;p&gt;I use a MacBook Pro for work. I am a developer who needs high end technology to do my job. Schools are not the same. They have constant budgetary constraints. They need to decide where to spend their hard fought for money. It won&#39;t be on the best computers. My son&#39;s school had reasonably usable Windows laptops with the latest version of Scratch on them. We generally had no issue with them. However, I helped out with teaching some programming at my daughter&#39;s infant school and it is a different story. They had half a room of desktop PCs and around 10 or 12 notebook-style laptops. The desktop PCs were fine but the notebooks had tiny screens and missing keys. The Scratch interface didn&#39;t fit on the full screen meaning that children had to scroll up and down to add new script blocks. It was much harder for the children couldn&#39;t to do this.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Investigate the equipment the schools have before you start. You&#39;ll have to work with what you&#39;ve got but it helps to understand the problems you may encounter before you start.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;show-and-tell&quot;&gt;Show and tell&lt;/h3&gt;&lt;p&gt;One of the best ideas I used was to have a &amp;quot;show and tell&amp;quot; at the end of the lesson. I didn&#39;t start this until the second week but it finishes off the lesson extremely well. The children like to show their own unique take on the projects. I was often amazed at the direction the children took and it made for interesting learning for me too. Show off their projects on the big screen or projector if you have one. Make sure all of the children watch as it encourages their creativity for next time. Remember though, if you have 15 in the class, it can take quite a while to share each project. I initially gave 10 minutes to do this but ended up needing 15 to 20 minutes in the future weeks.&lt;/p&gt;
&lt;div class=&quot;w-full px-3 py-4 shadow-lg border-t-2 bg-blue-100 border-blue-800 flex flex-row items-center break-words&quot;&gt;&lt;div class=&quot;w-1/6&quot;&gt;&lt;div class=&quot;flex flex-none items-center justify-center bg-white rounded-full w-10 h-10 mx-auto&quot;&gt;
    &lt;svg class=&quot;w-8 h-8 inline-block&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;
      &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; class=&quot;w-8 h-8 inline-block fill-blue-800&quot; viewBox=&quot;0 0 384 512&quot;&gt;&lt;!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --&gt;&lt;path d=&quot;M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
    &lt;/svg&gt;
  &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;w-5/6&quot;&gt;&lt;p&gt;&lt;em&gt;Tip: Allow at least 15 minutes before the end of the lesson for the children to show their project to the rest of the class. They&#39;re proud of what they&#39;ve done and it gives them a great sense of achievement in front of their classmates.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;make-it-fun&quot;&gt;Make it fun!&lt;/h3&gt;&lt;p&gt;Ultimately, you and the children both need to have fun. Some of the children told me &amp;quot;it was awesome - I can&#39;t wait for next week&amp;quot;. It&#39;s been a pleasure to teach them all and I hope tht I&#39;ve influenced some of them to eventually follow a path into software development. If you don&#39;t run a Code Club then why don&#39;t you try? The Code Club website allows you to search for clubs in the local area where you could help out. Or you could start one yourself. You don&#39;t have to be a computer expert to start one. The children will help &lt;strong&gt;you&lt;/strong&gt; if you get stuck!&lt;/p&gt;
&lt;h2 id=&quot;the-projects-for-my-club&quot;&gt;The projects for my club&lt;/h2&gt;&lt;p&gt;If you&#39;re interested in following a similar structure to my club, here are the projects that we did. I&#39;ve also listed some of the enhancements that I added and have given links to my versions so you can use and alter them.&lt;/p&gt;
&lt;h4&gt;Week 1 - &lt;a href=&quot;https://www.codeclubprojects.org/en-GB/scratch/rock-band/&quot;&gt;Rockband&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This project gives a great basic introduction to Scratch. It shows how to play sounds, change costumes based upon state and about user input. I expanded upon it with my &lt;a href=&quot;https://scratch.mit.edu/projects/153704607/&quot;&gt;Beatbox Pikachu&lt;/a&gt; version.&lt;/p&gt;
&lt;h4&gt;Week 2 - &lt;a href=&quot;https://www.codeclubprojects.org/en-GB/scratch/ghostbusters/&quot;&gt;Ghost Catcher&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This is a &amp;quot;whack-a-mole&amp;quot; style gme where you click on ghosts as they appear and disappear to score points. A great introduction to variables which record the score and display a timer. Enhancements could include varying the frequency, size and speed of ghosts and adding a high score. My version shows a complete game with a game over screen. You&#39;ll find my Ghostbusters game &lt;a href=&quot;https://scratch.mit.edu/projects/153704687/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Week 3 - &lt;a href=&quot;https://www.codeclubprojects.org/en-GB/scratch/chatbot/&quot;&gt;Chatbot&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Ths is probably my favourite project. You add code so that Scratch asks a question and the children use the response within their code. It gives a great introduction to computer logic, for example &amp;quot;if...then&amp;quot;. I started simple but then asked them to create their own &amp;quot;artificial intelligence&amp;quot;. My enhanced version is an AI which responds to simple commands such as &amp;quot;hide&amp;quot;, &amp;quot;moonwalk&amp;quot;, &amp;quot;basketball&amp;quot;, &amp;quot;dab&amp;quot;, &amp;quot;bottle flip&amp;quot; and my character responds accordingly. Bottle flipping was one of their favourites and everyone tried to recreate it! 😄 Here&#39;s &lt;a href=&quot;https://scratch.mit.edu/projects/153704739/&quot;&gt;my version&lt;/a&gt; on the Scratch website.&lt;/p&gt;
&lt;h4&gt;Week 4 - &lt;a href=&quot;https://www.codeclubprojects.org/en-GB/scratch/clone-wars/&quot;&gt;Clone Wars&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This project was particularly enjoyed by the Y5 children in my second group. It&#39;s a space shoot-em-up with a &lt;a href=&quot;https://www.youtube.com/watch?v=XhYVcwhSWjI&quot;&gt;Galaxians&lt;/a&gt; feel to it. In this you learn about cloning sprites for spawning bullets and enemy AI. This is an interesting project for explaining how I would experiment with variables to create a fun game. Show them how to change the speed of enemies, the time between bullets and what difference it makes to the game. My version adds a shield, lives and a game over screen so it&#39;s almost a real game. My version is on the Scratch website &lt;a href=&quot;https://scratch.mit.edu/projects/153704809/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Week 5 - &lt;a href=&quot;https://www.codeclubprojects.org/en-GB/scratch/flappy-parrot/&quot;&gt;Flappy Parrot&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;This final project was the one that the Y6 children were most excited about. Flappy Bird is an iconic mobile game for most of them and it was great to see their ideas - Flappy Taco anyone? It introduces more complex movement to the children in the form of gravity. It also shows the game developer trick of pretenting the bird moves forward by actually moving the pipes backwards. This was quite a mind-blowing trick for some of the children but great to see them understanding it. This is also a good project to show a full set of game states with. Try adding a start and end screen. Mine used the original Flappy Bird graphics and awarded a medal if you beat the high score. You can find my version of Flappy Bird &lt;a href=&quot;https://scratch.mit.edu/projects/153704839/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Simple Error Handling For Your Production Express Server</title>
    
    <link href="https://www.marclittlemore.com/simple-error-handling-for-your-production-express-server/"/>
    <published>2017-02-15T00:00:00Z</published>
    
    <updated>2017-02-15T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/simple-error-handling-for-your-production-express-server/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;Error handling is a big part of writing code and it&#39;s something that every developer should think about. When starting to write code, it&#39;s easy to overlook what happens when your code fails. As developers we like to assume we write perfect code. We never do though do we?! If you&#39;re writing a customer facing application then you should always try and gracefully catch any errors and return human-friendly error messages to give a better user experience. This is especially true if you have an application which deals with 3rd party APIs or other external dependencies, like a database server. In these cases, you can&#39;t guarantee that the services will be available and you should be ready to handle this.&lt;/p&gt;
&lt;p&gt;In my current job I write a lot of server-side JavaScript and we have a few &lt;a href=&quot;http://expressjs.com/&quot;&gt;Express&lt;/a&gt; applications running with Node.js. While checking out Twitter this evening I saw an interesting question from top JavaScript afficianado &lt;a href=&quot;http://wesbos.com/&quot;&gt;Wes Bos&lt;/a&gt;. He asked an interesting question about error handling with Express servers:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Talk to me about your Express error handling strategy. How (not what package) do you handle app errors as well as model validation fails?&lt;/p&gt;&amp;mdash; Wes Bos (@wesbos) &lt;a href=&quot;https://twitter.com/wesbos/status/831930095967358989&quot;&gt;February 15, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;Our &lt;a href=&quot;https://www.bbc.com/signin&quot;&gt;applications&lt;/a&gt; are used by millions of people each day so we have quite robust error handling for our Express servers. Hence this was a question I am qualified to answer. I&#39;ll explain our approach to error handling using a simple example with some basic Express middleware functions and error handlers. The code uses ES6 syntax and Promises so if you&#39;re not familiar with these, then check out Wes Bos&#39; excellent &lt;a href=&quot;https://es6.io/&quot;&gt;ES6 course&lt;/a&gt; and read up on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;&gt;Promises&lt;/a&gt;. I&#39;ve not shown the code for the service calls as it&#39;s not important to know what that code does. What is important is that we ensure that our Express application correctly handles the errors which may occur after we execute the function.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-a-simple-express-application&quot;&gt;Setting Up A Simple Express Application&lt;/h2&gt;&lt;p&gt;Firstly, we can set up a basic user-facing Express application. It&#39;ll expose a few routes for us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/login&lt;/code&gt; - this will display the user login page on a GET request and allow us to POST user credentials to it&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/user&lt;/code&gt; - this will display the user&#39;s data (whatever that may be)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Express server and routing code could look something like this.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;startServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Display the user login page&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/login&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        showLoginMiddleware
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Allow the user to submit their credentials&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/login&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        validateLoginMiddleware&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        showLoginMiddleware
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Assume that the user is validated before we hit this&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pages/success&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Mount 404 handler as penultimate middleware&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;notFoundHandler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Final middleware is our catch-all error handler&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;defaultErrorHandler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Server started on port &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;port&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that I&#39;ve added two error handlers and these are simple Express middleware functions. The first is &lt;code&gt;notFoundHandler&lt;/code&gt;. This will handle any requests which are application isn&#39;t set up to handle, for example if somebody hits a URL which we haven&#39;t defined. The second is &lt;code&gt;defaultErrorHandler&lt;/code&gt;. This is the most important middleware as it allows us to catch any application errors that we aren&#39;t sure what to do with. I&#39;ll explain how these are implemented shortly.&lt;/p&gt;
&lt;h2 id=&quot;handling-known-errors&quot;&gt;Handling Known Errors&lt;/h2&gt;&lt;p&gt;The first question Wes asked was what error handling pattern should be used by an Express server in order to handle known developer errors, such as model validation failure or documented API failures. In the server setup above, we declared a GET and POST route for our &lt;code&gt;/login&lt;/code&gt; URL so let&#39;s define some middleware functions for them.&lt;/p&gt;
&lt;p&gt;The first is for the GET request and it simply renders our login page. We can assume that this is a Jade or Pug template which renders a form allowing the user to enter their username and password.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;showLoginMiddleware&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pages/login&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next middleware is used by our POST route and retrieves the &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; properties from our request body. We then pass these to our authentication service which validates that we have a user we know about. Notice that it&#39;s promise based so we can nicely catch our errors but you can achieve the same with a callback if you&#39;re more familiar with them. If our &lt;code&gt;authenticateUser&lt;/code&gt; service succeeds, then we simple redirect the application to our &lt;code&gt;/user&lt;/code&gt; endpoint.&lt;/p&gt;
&lt;p&gt;If the user&#39;s credentials are not valid then we know that our service throws a &lt;code&gt;InvalidCredentialsError&lt;/code&gt; typed &lt;code&gt;Error&lt;/code&gt;. This is because we either wrote the service or we know this from 3rd party API documentation. This typed error contains a &lt;code&gt;message&lt;/code&gt; property which we can show to the user so it&#39;s now a simple case of remembering the error for use in our re-rendered page. In my example, I&#39;m using the &lt;code&gt;response.locals&lt;/code&gt; object to assign an error property which gets used in the subsequent page re-render. I call &lt;code&gt;next()&lt;/code&gt; in order to call the next middleware and this just uses &lt;code&gt;showLoginMiddleware&lt;/code&gt; again which renders the same login page but shows the error message if it exists.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateLoginMiddleware&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Authenticate the user credentials with our service&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;authenticateUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// User credentials were correct&lt;/span&gt;
            response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;InvalidCredentialsError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;loginError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// User credentials were incorrect&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Set an error message&lt;/span&gt;
            response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;locals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; loginError&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token comment&quot;&gt;// Re-render the same page where we display our error message&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;unknownError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Woah! Something bad happened but we don&#39;t know what&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;unknownError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I&#39;m using &lt;a href=&quot;http://bluebirdjs.com/docs/api/catch.html&quot;&gt;Bluebird&#39;s typed promise catch method&lt;/a&gt; in the above example.&lt;/p&gt;
&lt;p&gt;Using a Promise returned from our authentication service also allows us to add a final &lt;code&gt;catch&lt;/code&gt; function to our middleware. &lt;em&gt;But why do we need to add this final catch block?&lt;/em&gt; Well our authentication service needs to determine if our user credentials are correct so it could do this by checking with a 3rd party API, such as an OAuth service, or it could be using a database query. What happens if either of these resources is unavailable? They may have heavy load. They might just fail due to unexpected server downtime. Either of these could cause a request timeout or an unknown response. Our &lt;code&gt;catch&lt;/code&gt; block can now handle this more gracefully by passing the error on to the generic Express error handling middleware.&lt;/p&gt;
&lt;h2 id=&quot;handling-unexpected-application-errors&quot;&gt;Handling Unexpected Application Errors&lt;/h2&gt;&lt;p&gt;Express allows us to define middleware functions which have access to the request, response and next middleware in the applications request-response cycle. It also gives us the ability to define a default middleware for our error handler. This makes it simple for us to handle two different general error conditions: an error handler for when we don&#39;t match any of our routes (i.e. a 404 handler) and a default error handler for all other Express errors.&lt;/p&gt;
&lt;p&gt;To use a 404 error middleware, we just use the same &lt;code&gt;(request, response, next)&lt;/code&gt; middleware function signature but &lt;strong&gt;we need to ensure it is the last normal middleware in the application&lt;/strong&gt;. This means that it&#39;ll match any route that we don&#39;t already handle with the previous middleware. In the example below, I create a &lt;code&gt;notFound&lt;/code&gt; middleware which displays a &amp;quot;page not found&amp;quot; template. I also add a logger warning. It may not be an error as such but if you&#39;re logging events in your application, and you should be in some form, then it&#39;s useful to see what resource the user is attempting to access.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;notFoundHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;Unhandled resource&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unknown resource&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;originalUrl
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pages/notFound&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we have the special Express middleware which allows us to handle errors. In my user authentication middleware above, called &lt;code&gt;validateLoginMiddleware&lt;/code&gt;, you&#39;ll notice that I catch any unknown errors and then call the &lt;code&gt;next&lt;/code&gt; middleware but also pass an error to it i.e. &lt;code&gt;next(uknownError)&lt;/code&gt;. By calling the next middleware with an error parameter, it allows Express to bypass all of the other middleware functions for the current route and instead calls our special error middleware. In the example below, we log the error and also return a 500 error and specific error page. In this we can warn the user that we&#39;re having some problems without letting our application crash. This greatly improves our users experience even if there&#39;s nothing we can do about the error itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT:&lt;/strong&gt; The Express error middleware has a specific signature with the parameters &lt;code&gt;(error, request, response, next)&lt;/code&gt;. If you&#39;re using a linter such as ESLint, it will complain that you have an unused parameter in &lt;code&gt;next&lt;/code&gt; if you don&#39;t call it. &lt;strong&gt;If you remove it then the middleware will no longer be called via &lt;code&gt;next(error)&lt;/code&gt; as it no longer matches.&lt;/strong&gt; Ensure that you add an ignore rule for your linter but don&#39;t remove the unused parameter. I learnt this the hard way by fixing the lint warning and wondered why the error middleware wasn&#39;t called! You must also register this error handling middleware &lt;strong&gt;as the last middleware in the application&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// eslint-disable-next-line no-unused-vars&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;defaultErrorHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Uncaught error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pages/error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;Hopefully this has given you a useful insight how to properly use Express middleware for your error handling. In short, make sure you do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;add a standard middleware for any routes which you don&#39;t handle specifically - assign this to the Express application &lt;strong&gt;as the penultimate route&lt;/strong&gt; and it will act as your 404 error handler&lt;/li&gt;
&lt;li&gt;add an Express error middleware with the additional error parameter as your default error - assign this to the Express application &lt;strong&gt;as the last middleware after all other middleware functions have been registered&lt;/strong&gt; and it will act as your default error handler&lt;/li&gt;
&lt;li&gt;attempt to catch any other model or service errors by using &lt;code&gt;Promise.catch&lt;/code&gt; functions or &lt;code&gt;try/catch&lt;/code&gt; blocks to gracefully handle known errors.&lt;/li&gt;
&lt;/ul&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Crafting High Quality Unit Tests: Tips and Best Practices</title>
    
    <link href="https://www.marclittlemore.com/how-to-write-high-quality-unit-tests/"/>
    <published>2016-11-13T00:00:00Z</published>
    
    <updated>2016-11-13T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/how-to-write-high-quality-unit-tests/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;One of the often asked questions about unit testing is &amp;quot;how do I ensure I&#39;m writing good quality unit tests?&amp;quot; It&#39;s easy to start of down the path of &lt;a href=&quot;https://www.marclittlemore.com/talks/7-tips-for-writing-great-unit-tests/&quot;&gt;writing unit tests&lt;/a&gt; for your code but you need to make sure you&#39;re writing dependable tests which are easy to maintain. In this article, I talk about the defining characteristics of solid unit testing and how to ensure that the quality is high.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-unit&quot;&gt;What is a unit?&lt;/h2&gt;&lt;p&gt;The first questions to ask is &amp;quot;what is a unit?&amp;quot; when talking about writing your unit tests. There are differing definitions of what a unit actually is, but it&#39;s often defined as &amp;quot;the smallest component that it makes sense to test&amp;quot;. For object-oriented programming, this could be a whole class or an interface but, as with a functional programming approach, it could also be a single method or function. To test the unit you simply have to define your expectations of what it does, given any of the specified inputs. That&#39;s it! If you&#39;re the developer in charge of writing the code then the test defines what you know &lt;strong&gt;will happen&lt;/strong&gt;. If you didn&#39;t write the code under test, you can write your tests based upon your expectations of what you think &lt;strong&gt;should happen&lt;/strong&gt;. If either of these things change then you want your tests to fail. Your unit tests then define a solid contract with your code.&lt;/p&gt;
&lt;h2 id=&quot;what-are-good-qualities-of-unit-tests&quot;&gt;What are good qualities of unit tests?&lt;/h2&gt;&lt;p&gt;Your unit tests should be the first line of defence for your project. As your tests are so important to your code quality, you should always attempt to write high quality unit tests. But what does that actually mean? Let&#39;s find out by looking at some of the features of good quality tests.&lt;/p&gt;
&lt;h2 id=&quot;human-readable-and-understandable-test-names&quot;&gt;Human readable and understandable test names&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/human-readable-test-names.png&quot; alt=&quot;Human readable test names&quot; :=&quot;&quot; title=&quot;Human readable test names&quot;&gt;&lt;/p&gt;
&lt;p&gt;As silly as this may sound, you should spend some time ensuring that your test names make sense. This means that they should show the developer reading them exactly what the code under test does. The tests should be written for you and your team and not for a computer. Make sure that they&#39;re easy to read and make sense as a sentence. An example could be &lt;em&gt;&amp;quot;should add a new user to the database&amp;quot;&lt;/em&gt; or &lt;em&gt;&amp;quot;should call the authentication API with the expected user credentials&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;An advantage of writing good test names is that you should be able to scan through each test module and see what the feature requirements for the code. This suite of tests should then be treated as living documentation for your project. If your code&#39;s functionality changes then update your test names accordingly.&lt;/p&gt;
&lt;h2 id=&quot;isolation-of-dependencies&quot;&gt;Isolation of dependencies&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/stub-unit-test-dependencies.jpg&quot; alt=&quot;Stub unit test dependencies&quot; :=&quot;&quot; title=&quot;Stub unit test dependencies&quot;&gt;&lt;/p&gt;
&lt;p&gt;One of the most important feature of a unit test is that it&#39;s isolated from any other external influences. This ensures that you&#39;re only testing the code that you&#39;ve written and aren&#39;t dependent on the state of other modules. In order to do this you should write &lt;a href=&quot;http://martinfowler.com/bliki/TestDouble.html&quot;&gt;test doubles&lt;/a&gt; to replace your production dependencies for testing purposes. These generally take the form of a stub or mock object which returns the data you specify in order to fulfil your test expectation.&lt;/p&gt;
&lt;p&gt;Some examples of things that you should make sure that you isolate are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Calls to services&lt;/strong&gt; - these could be external 3rd party API calls, for example the Twitter API, or simply function calls to your own services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serialisation calls&lt;/strong&gt; - avoid actually serialising your data to real data stores by stubbing these function calls or mocking them with fake objects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State&lt;/strong&gt; - ensure that any state of the system under test has pre-conditions which are initialised by you in your test setup. This could involve stubbing your configuration, initialising structures or objects with expected data or again mocking or stubbing external code dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;provide-just-one-reason-to-fail&quot;&gt;Provide just one reason to fail&lt;/h2&gt;&lt;p&gt;A unit test should only have one reason to fail so you should always avoid putting multiple expectations into one test. You want to know exactly why a test fails and you&#39;ll just make it harder if your test contains more than one assertion. By keeping it simple, and using only one test expectation, it allows you to quickly see &lt;strong&gt;why&lt;/strong&gt; a test has failed. This will mean that you write more tests but it makes the code much easier to reason about. You&#39;ll also find that it guides you to write tests to cover more of your code.&lt;/p&gt;
&lt;h2 id=&quot;make-them-speedy&quot;&gt;Make them speedy&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/make-unit-tests-fast.png&quot; alt=&quot;Make Unit Tests Fast&quot; :=&quot;&quot; title=&quot;Make unit tests fast&quot;&gt;&lt;/p&gt;
&lt;p&gt;Your unit tests should execute as quickly as possibly. Each test should have a small scope and should have all of its dependencies stubbed. By avoiding executing production code, such as that which serialises data to a database or uses 3rd party API calls, you can ensure that the whole of your unit test suite runs in a matter of seconds. This gives the team the confidence to run the tests often and will result in much better code quality. You can see from the image above that in one of our projects we run 2193 tests in 18 seconds, approximately 122 tests per second. This total includes integration tests as well as unit tests but is still fast enough for us to include those in every test run.&lt;/p&gt;
&lt;h2 id=&quot;independent-of-environment&quot;&gt;Independent of environment&lt;/h2&gt;&lt;p&gt;Your unit tests should always be independent of environment. This means that the tests suite passes all of the tests on your machine, your colleagues&#39; machine and any continuous build server. You definitely want to avoid the &amp;quot;it works on my machine&amp;quot; problem.&lt;/p&gt;
&lt;p&gt;Ensure that the unit tests execute the same on all environments by correctly stubbing or mocking any dependencies and setting any state or external data. Also make sure that there is no dependency on other tests. Each test should run in isolation and should not need the result of a previous test in order to execute correctly. In the same vein, make sure that a test doesn&#39;t have any side effects, for example adding data to a database. You need identical data inputs and state each time the test suite runs. If the tests don&#39;t have consistency, and tests begin to fail, your team will lose faith in unit testing and they will quickly be ignored.&lt;/p&gt;
&lt;h2 id=&quot;tests-unit-completely&quot;&gt;Tests unit completely&lt;/h2&gt;&lt;p&gt;While some tests are better than none, the best quality unit tests ensure that all of the code paths are covered. This is often done in combination with test-driven development practices which mean you never write any code without a corresponding test. Even if you write your tests after you&#39;ve written your code, you should try and test every route through your code.&lt;/p&gt;
&lt;p&gt;Another good practice is to attempt to cover all edge cases for your code if possible. You&#39;ll find that you sometimes forget some obscure inputs, or maybe receive input data that you didn&#39;t expect, but try and determine the full requirements of your code and write your tests accordingly. Even if you don&#39;t, it&#39;s still incredibly satisfying to find a new bug, add an edge case test for it that fails as you expect, and then to fix it while still ensuring that all of your previous tests still pass.&lt;/p&gt;
&lt;h2 id=&quot;ultimately-make-unit-testing-easy&quot;&gt;Ultimately, make unit testing easy&lt;/h2&gt;&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/make-unit-testing-easy.jpg&quot; alt=&quot;Make unit testing easy&quot; :=&quot;&quot; title=&quot;Make unit testing easy&quot;&gt;&lt;/p&gt;
&lt;p&gt;You should always aim to make your unit tests easy. They should be easy to run, either from a simple terminal command or from your task runner or dependency manager. They should be easy to maintain such that refactoring the internals of your code should result in little work to update your tests as your test expectations will still be met.&lt;/p&gt;
&lt;h2 id=&quot;simple-questions-to-ask-for-each-unit-test&quot;&gt;Simple questions to ask for each unit test&lt;/h2&gt;&lt;p&gt;The best way to write high quality unit tests is to write the test with these questions in mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What are you testing?&lt;/li&gt;
&lt;li&gt;What should the code you&#39;re testing do?&lt;/li&gt;
&lt;li&gt;What is the actual output of the code under test?&lt;/li&gt;
&lt;li&gt;What is the expected output of the code under test?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you answer all of those questions, you&#39;ll find you quickly have a higher quality unit test suite with high code coverage and you and your team can have much better confidence when deploying to your production environment.&lt;/p&gt;
&lt;p&gt;If you spot any errors or have and questions then please &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;contact me&lt;/a&gt;.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>How To Unit Test Express Routes</title>
    
    <link href="https://www.marclittlemore.com/how-to-unit-test-express-routes/"/>
    <published>2016-11-06T00:00:00Z</published>
    
    <updated>2016-11-06T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/how-to-unit-test-express-routes/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;I was swiping through my Twitter timeline the other night when I saw a really interesting tweet from &lt;a href=&quot;https://kentcdodds.com/&quot;&gt;Kent C Dodds&lt;/a&gt;, a great JavaScript developer who works at PayPal, and someone you should really follow on Twitter if you don&#39;t already. He asked the following question.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;[POLL]: Is it reasonable to *unit* test express routes? Or is it better to do an *integration* test of your server (with supertest for ex.)?&lt;/p&gt;&amp;mdash; Kent C. Dodds (@kentcdodds) &lt;a href=&quot;https://twitter.com/kentcdodds/status/794251165844185088&quot;&gt;November 3, 2016&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;I thought the poll posed an interesting question as to whether you should test Express routes or not. As I&#39;m quite an evangelist of test driven development at the BBC, and I like a challenge and love writing code, I thought I&#39;d have a think about it. I like to aim for as much test coverage as possible but I&#39;m pragmatic enough to understand that 100% code coverage doesn&#39;t necessarily mean you&#39;re always testing the right things.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yes, you should unit test Express routes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My answer to the poll was &amp;quot;Yes, you should unit test Express routes&amp;quot;, which I appreciate goes against the majority who said unit tests weren&#39;t reasonable. At the minimum, I think that you should &lt;strong&gt;definitely&lt;/strong&gt; test Express routes with some solid integration testing. In our team, we use &lt;a href=&quot;https://github.com/visionmedia/supertest&quot;&gt;supertest&lt;/a&gt; for our HTTP expectations and also &lt;a href=&quot;https://github.com/node-nock/nock&quot;&gt;nock&lt;/a&gt; to mock responses from API calls and it works really well for testing full flows on Express routes. I&#39;ll be honest and say that we don&#39;t currently have full unit testing for the Express routes on the three main Node.js projects that our team work on, but having thought about it, I think there are some reasons why we probably should consider it.&lt;/p&gt;
&lt;h2 id=&quot;reasons-you-should-unit-test-express-routes&quot;&gt;Reasons you should unit test Express routes&lt;/h2&gt;&lt;h3 id=&quot;better-code-coverage&quot;&gt;Better code coverage&lt;/h3&gt;&lt;p&gt;Firstly, it should give you better code coverage. Having 100% code coverage should never be the end goal of your testing efforts, but writing the best tests to assert your expectations of what your code should be doing is. I feel that aiming towards full coverage of your code means that you have complete confidence in it at the unit level, and you can reason that your modules should perform the functions you expect them to.&lt;/p&gt;
&lt;h3 id=&quot;explicit-expectations-of-middleware-used&quot;&gt;Explicit expectations of middleware used&lt;/h3&gt;&lt;p&gt;Next, I think it helps that you&#39;re being explicit in the middleware you think you are calling for each route. It&#39;s always a good idea to split your middleware out into separate modules as this makes unit testing of each middleware much clearer by reading the test asserts and it makes writing the code much simpler. By subsequently adding tests for each route, you can also then assert that you&#39;re calling the right middleware. A good example of this would be to verify that you&#39;re adding your authentication layer to the expected routes. You want to know that you need to authenticate your user before they can see your administration, or per-user, routes and this is simple to do with an expectation that the authentication middleware is called on those specific routes.&lt;/p&gt;
&lt;h3 id=&quot;security-best-practices&quot;&gt;Security best practices&lt;/h3&gt;&lt;p&gt;Lastly, I&#39;ve recently taken up the role as security champion of our team and have been working with &lt;a href=&quot;http://blog.diniscruz.com/&quot;&gt;Dinis Cruz&lt;/a&gt;, a developer and application security expert, to ensure that our applications are more secure and adhere to &lt;a href=&quot;https://www.owasp.org/index.php/Main_Page&quot;&gt;OWASP&lt;/a&gt; best practices. One of Dinis&#39; suggestions is to write tests which assert the security expectations of your application. In this case, writing tests which assert that the specified routes apply your security middleware feel like an ideal use case for &lt;a href=&quot;https://www.marclittlemore.com/talks/7-tips-for-writing-great-unit-tests/&quot;&gt;writing unit tests&lt;/a&gt; for Express routes in addition to your integration testing. In doing so it gives you much better confidence that you&#39;re writing secure code and that those specified routes are protected.&lt;/p&gt;
&lt;h2 id=&quot;how-to-write-express-route-unit-tests&quot;&gt;How to write Express route unit tests&lt;/h2&gt;&lt;p&gt;So it&#39;s easy to say that we should unit test Express routes, but how do you do it in practice? As Kent said in his later tweets, there don&#39;t seem to be any good examples of how to do it so I&#39;ve written a basic Express application and some corresponding tests in this &lt;a href=&quot;https://github.com/MarcL/unit-test-express-routes&quot;&gt;project on GitHub&lt;/a&gt; which show you a good way to set these tests up. We&#39;ll be using &lt;a href=&quot;https://mochajs.org/&quot;&gt;mocha&lt;/a&gt;, &lt;a href=&quot;http://chaijs.com/&quot;&gt;chai&lt;/a&gt;, &lt;a href=&quot;http://sinonjs.org/&quot;&gt;sinon&lt;/a&gt;, &lt;a href=&quot;https://github.com/domenic/sinon-chai&quot;&gt;sinon-chai&lt;/a&gt; (for some syntactic sugar) and &lt;a href=&quot;https://github.com/thlorenz/proxyquire&quot;&gt;proxyquire&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Install the project as follows:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone git@github.com:MarcL/unit-test-express-routes.git
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; unit-test-express-routes
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you should see a basic Express application in the &lt;code&gt;src/launch.js&lt;/code&gt; module and the corresponding tests in the &lt;code&gt;test/launch.test.js&lt;/code&gt; module. The application starts an Express server on port 7080 and exposes three example routes: &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/login&lt;/code&gt; and &lt;code&gt;/dashboard&lt;/code&gt;. These are meant to mimic a homepage, a login page and a dashboard page which is only exposed after authentication.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; express &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;express&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; homepage &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./middleware/homepage&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; login &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./middleware/login&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; authenticate &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./middleware/authenticate&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dashboard &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./middleware/dashboard&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupRoutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; homepage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/login&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; login&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/dashboard&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        authenticate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        dashboard
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7080&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;setupRoutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Server running on: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;port&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;&#96;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    start
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see from the code above, there are four example middlewares which have been split into their own modules. Splitting up your middleware like this is always a good idea as it allows you to separate the unit tests for the middeware from the route code. This makes the code much simpler and easy to read and allows you to write clean and understandable tests.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Splitting up your middleware like this is always a good idea as it allows you to separate the unit tests for the middeware from the route code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We need to set up our tests so that we can stub the Express application that is created and then we can return a fake server that we can use in our test expectations. As each test needs this fake server to be initialised, I chose to do this using Mocha&#39;s &lt;code&gt;beforeEach&lt;/code&gt; pre-condition block. As we don&#39;t need to return anything when we perform an &lt;code&gt;app.get&lt;/code&gt; to set up our routes which respond to the GET request, we can use &lt;code&gt;sinon&lt;/code&gt; spy. However, we want to return a fake HTTP server for the &lt;code&gt;app.listen&lt;/code&gt; call, so we make this a &lt;code&gt;sinon&lt;/code&gt; stub. We can then set up a fake Express server which is return via a &lt;code&gt;sinon&lt;/code&gt; stub.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Initialise our spy and stub&lt;/span&gt;
    spyExpressGet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    stubExpressListen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Create fake express application with our spy and stub methods&lt;/span&gt;
    fakeExpress &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; spyExpressGet&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; stubExpressListen
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Return our fake express application when express() is called&lt;/span&gt;
    stubExpress &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fakeExpress&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we can set up a fake HTTP server which is what we&#39;d expect &lt;code&gt;app.listen&lt;/code&gt; to return. We do this so we can compare it in our test which checks that the app is listening as we expect.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// We never use the fake HTTP server but we want to compare it&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fakeHttpServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// app.listen returns a fake HttpServer&lt;/span&gt;
stubExpressListen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fakeHttpServer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s easy to spy on each of the middlewares used for our routes by creating &lt;code&gt;sinon&lt;/code&gt; spies. We don&#39;t need to test any of these middlewares here, so we don&#39;t need to use a stub. You should test each middleware separately from this route code.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;spyHomepage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
spyLogin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
spyAuthenticate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
spyDashboard &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now use &lt;code&gt;proxyquire&lt;/code&gt; to override the module dependencies. This will allow us to inject our fake Express server and to spy on all of the middleware. All of these spies and stubs will be used in our test expectations. The &lt;code&gt;proxyquire&lt;/code&gt; initialisation looks complex, but we&#39;re simply stubbing the call to &lt;code&gt;app.express&lt;/code&gt; so that it returns our &lt;code&gt;stubExpress&lt;/code&gt; and making sure that the calls to &lt;code&gt;import&lt;/code&gt; or &lt;code&gt;require&lt;/code&gt; the middleware modules in the real code will now be using our spies.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Use proxyquire to stub required modules and return&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// our spies so we can check assertions&lt;/span&gt;
server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;proxyquire&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;../../src/launch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; stubExpress&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&#39;./middleware/homepage&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; spyHomepage&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&#39;./middleware/login&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; spyLogin&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&#39;./middleware/authenticate&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; spyAuthenticate&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&#39;./middleware/dashboard&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; spyDashboard&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The hard part has now been done as we&#39;ve set up our server before each test. We can now inspect the routes and make expectations on them  so let&#39;s take a look at our tests for that.&lt;/p&gt;
&lt;h2 id=&quot;express-route-tests&quot;&gt;Express route tests&lt;/h2&gt;&lt;h3 id=&quot;assertion-that-our-server-is-listening&quot;&gt;Assertion that our server is listening&lt;/h3&gt;&lt;p&gt;First, we can write tests to check that our server is returning the expected HTTP server and runs on either the default port or a port we pass when initialising. We have stubbed &lt;code&gt;app.listen&lt;/code&gt; to return a fake HTTP server so we can check that our call to &lt;code&gt;server.start()&lt;/code&gt; returns it correctly. We can also confirm that it is called with either the default port of 7080, or another port that we pass through to the function call. The expectations are simple and readable so our fellow developers know exactly what the code should do. Note, for simplicity I&#39;m using the general &lt;code&gt;sinon.match.func&lt;/code&gt; matcher to match &lt;em&gt;any&lt;/em&gt; function for the second parameter to &lt;code&gt;app.listen&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should return expected http server&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; returnedServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;returnedServer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;eql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fakeHttpServer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should listen on default port 7080&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        stubExpressListen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;should&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;have&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;been&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calledWithExactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;match&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;func&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should listen on expected port if passed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; expectedPort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8888&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedPort&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        stubExpressListen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;should&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;have&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;been&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calledWithExactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedPort&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sinon&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;match&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;func&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then check each route using our &lt;code&gt;spyExpressGet&lt;/code&gt; which spies on &lt;code&gt;app.get&lt;/code&gt; for our routes. This is a simple case of confirming that each call sets the expected route string and the corresponding middleware(s).&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should setup default route&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    spyExpressGet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;should&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;have&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;been&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calledWithExactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spyHomepage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should setup login route&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    spyExpressGet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;should&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;have&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;been&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calledWithExactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/login&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; spyLogin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should setup dashboard route&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    spyExpressGet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;should&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;have&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;been&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calledWithExactly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;/dashboard&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        spyAuthenticate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        spyDashboard
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use sinon&#39;s &lt;code&gt;calledWithExactly&lt;/code&gt;, albeit using the syntactic sugar of &lt;code&gt;sinon-chai&lt;/code&gt;, to assert that we&#39;re setting the exact middleware for each route. This now gives us complete confidence that each route is only using the middleware that it should.&lt;/p&gt;
&lt;p&gt;Notice that I&#39;ve only stubbed the GET requests in my example project. If you wanted to validate the other routes which use other HTTP verbs then you should create a spy or stub for it and pass it into the fake Express application &lt;code&gt;fakeExpress&lt;/code&gt;. You can then assert your expectations that &lt;code&gt;app.post&lt;/code&gt;, &lt;code&gt;app.put&lt;/code&gt; or &lt;code&gt;app.delete&lt;/code&gt; are called with the expected route string and middleware.&lt;/p&gt;
&lt;h2 id=&quot;tldr&quot;&gt;tl;dr&lt;/h2&gt;&lt;p&gt;Hopefully this was an easy-to-read explanation of how to create unit tests for Express routes. In case there is too much text to read above, my opinion is that, yes, I think you can easily unit test your Express routes and you probably should. You should do the following if you&#39;re planning to do so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;proxyquire&lt;/code&gt; to override dependencies so that you can stub/spy your express application and middleware functions&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;sinon&lt;/code&gt; to stub your middleware, which you should have already extracted to their own modules&lt;/li&gt;
&lt;li&gt;Use these stubs to assert your expectations that the Express application routes use the correct middleware&lt;/li&gt;
&lt;li&gt;These tests are an additional layer of confidence and are especially useful for security concerns&lt;/li&gt;
&lt;li&gt;You should &lt;strong&gt;always&lt;/strong&gt; have additional integration tests which test the full flow of the routes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#39;ve got any questions, need any clarification or have any comments on this then please &lt;a href=&quot;https://www.marclittlemore.com/contact/&quot;&gt;contact me&lt;/a&gt; or tweet at me.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>Halloween Sound Effects Using Spotify And Raspberry Pi</title>
    
    <link href="https://www.marclittlemore.com/spooky-halloween-sound-effects-with-raspberry-pi-and-spotify/"/>
    <published>2016-11-04T00:00:00Z</published>
    
    <updated>2016-11-04T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/spooky-halloween-sound-effects-with-raspberry-pi-and-spotify/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;For Halloween back in 2015, I had an unused Raspberry Pi and decided that I should make a Halloween project in order to learn electronics again, and to scare the local children as they came trick or treating. I connected the Raspberry Pi GPIO pins to a breadboard with a &lt;a href=&quot;https://www.coolcomponents.co.uk/big-dome-push-button-red.html&quot;&gt;big red dome push button&lt;/a&gt;. I hid the Pi and electronics inside a shoe box painted black, and made it look like a spooky spider as you can see in the picture below.&lt;/p&gt;
&lt;blockquote class=&quot;instagram-media&quot; data-instgrm-captioned=&quot;&quot; data-instgrm-version=&quot;7&quot; style=&quot; background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);&quot;&gt;&lt;div style=&quot;padding:8px;&quot;&gt; &lt;div style=&quot; background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;&quot;&gt; &lt;div style=&quot; background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;&quot;&gt;&lt;/div&gt;&lt;/div&gt; &lt;p style=&quot; margin:8px 0 0 0; padding:0 4px;&quot;&gt; &lt;a href=&quot;https://www.instagram.com/p/9eGMutCXUh/&quot; style=&quot; color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;&quot; target=&quot;_blank&quot;&gt;Our Raspberry Pi powered Halloween spider. Press the big red button if you dare! LEDs to be added tonight. #halloween #spider #raspberrypi #spooky&lt;/a&gt;&lt;/p&gt; &lt;p style=&quot; color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;&quot;&gt;A photo posted by Marc Littlemore (@marclittlemore) on &lt;time style=&quot; font-family:Arial,sans-serif; font-size:14px; line-height:17px;&quot; datetime=&quot;2015-10-30T17:33:27+00:00&quot;&gt;Oct 30, 2015 at 10:33am PDT&lt;/time&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; defer=&quot;&quot; src=&quot;https://platform.instagram.com/en_US/embeds.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;Unfortunately, this year I&#39;ve had a major operation so wasn&#39;t feeling up to recreating or bettering my Raspberry Pi project from last year. Instead, I decided to simplify it in order to play random spooky sound effects via the Raspberry Pi and combine it with some spooky ambient music from Spotify. This is a simple project to set up so read on if you want to scare your local trick or treaters!&lt;/p&gt;
&lt;h2 id=&quot;what-youll-need&quot;&gt;What You&#39;ll Need&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;A Raspberry Pi (v2/3 or Zero) and SD card with Raspbian installed&lt;/li&gt;
&lt;li&gt;A PC or Mac with a Spotify account&lt;/li&gt;
&lt;li&gt;An audio mixer (or an input into your Mac or PC for software mixing)&lt;/li&gt;
&lt;li&gt;A set of speakers&lt;/li&gt;
&lt;li&gt;Some audio cables (probably 3.5mm but it will depend on your setup)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first thing you&#39;ll need to do is to install Node.js on the Rasbperry Pi, assuming that you haven&#39;t already. You&#39;ll also need &lt;code&gt;git&lt;/code&gt; installed for for cloning the project from GitHub, but this should come with the standard Rasbian install.&lt;/p&gt;
&lt;p&gt;Next you&#39;ll need your PC or Mac up and running and you&#39;ll want Spotify on it. You can use a Spotify free account but bear in mind that you&#39;ll have adverts popping up which might not scare the neighbours as much as the spooky music will. Alternatively, you can use your own playlist of haunting music or find another one on SoundCloud or YouTube. Again, just watch out for adverts playing or you&#39;ll ruin the effect.&lt;/p&gt;
&lt;p&gt;You&#39;ll need a set of speakers attached to your computer in some way. I run the output of my PC to an amplifier and speakers so I can boost the volume, but you can just use a small pair of computer speakers straight from the computer. Just make sure they&#39;re loud enough for people to be able to hear them. I kept my speakers indoors and put them by open windows but if you can run your speakers outside, and they&#39;re waterproof, then that&#39;ll work too.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/stanton-dj-mixer.jpg&quot; alt=&quot;Stanton DJ Mixer&quot; :=&quot;&quot; title=&quot;Stanton DJ Mixer&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, you&#39;ll need something to mix the two audio sources together. I&#39;ve spent nearly 30 years DJing so I had my Stanton DJ mixer to hand and plugged both the PC and the Raspberry Pi into it using RCA cables. This allowed me to alter the volumes of the two input sources and the final volume of the output to my amplifier. If you don&#39;t have an audio mixer, you can easily achieve the same thing by using a software mixer on your PC or Mac. Simply attach the output of the Raspberry Pi to the microphone input of your computer using a 3.5mm RCA cable and start the audio mixer in your settings or preferences panel. Note that the output of the Raspberry Pi seems to be really quiet in comparison to the output from the PC. You may be able to change this but I didn&#39;t investigate it too much. It just means that you&#39;ll need to make the Pi audio a lot higher than that from the PC using your hardware or software mixer.&lt;/p&gt;
&lt;h2 id=&quot;install-nodejs-on-your-raspberry-pi&quot;&gt;Install Node.js on your Raspberry Pi&lt;/h2&gt;&lt;p&gt;As I love working with Node.js and JavaScript, I decided that this was an easy way to get a project up and running. I installed Node when I first got the Raspberry Pi back in April 2015 so I have version 0.10 by following &lt;a href=&quot;http://weworkweplay.com/play/raspberry-pi-nodejs/&quot;&gt;this guide&lt;/a&gt;. However, at the time of writing, I think the easiest way to install v4.2.1 is by following the instructions on the &lt;a href=&quot;https://github.com/nathanjohnson320/node_arm&quot;&gt;Node Arm GitHub project&lt;/a&gt; which does the following:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;wget&lt;/span&gt; http://node-arm.herokuapp.com/node_latest_armhf.deb
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; dpkg &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; node_latest_armhf.deb&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may need to update the &lt;code&gt;gcc&lt;/code&gt; compilers too, dependent on what version of Raspbian you&#39;re using, but follow the Node Arm guide if you need to do that.&lt;/p&gt;
&lt;p&gt;As Linux doesn&#39;t have the best out-of-the-box audio support, you&#39;ll also have to install the &lt;a href=&quot;http://www.alsa-project.org/main/index.php/Main_Page&quot;&gt;Advanced Linux Sound Architecture (ALSA)&lt;/a&gt; library to allow easier playback of MP3 files. One of the NPM packages used is &lt;a href=&quot;https://github.com/TooTallNate/node-speaker&quot;&gt;node-speaker&lt;/a&gt; and it relies on this library to play audio files. It should be a simple installation using &lt;code&gt;apt-get&lt;/code&gt; on the Raspberry Pi as follows:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libasound2-dev&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;install-the-code&quot;&gt;Install the code&lt;/h2&gt;&lt;p&gt;Next you&#39;ll need to install my Halloween Pi project in order to play the random sounds. As mentioned above, the original project used a button and some LEDs last year so the master branch of the &lt;a href=&quot;https://github.com/MarcL/halloweenpi&quot;&gt;Halloweenpi GitHub project&lt;/a&gt; contains that code that uses the GPIOs to send signals from the buttons and to the LEDs. However, I&#39;ve added a branch which just plays random sound effects in the &lt;code&gt;random-play-sounds&lt;/code&gt; branch. Just clone the project from this specific branch and install all of the dependencies:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt; random-play-sounds git@github.com:MarcL/halloweenpi.git
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; halloweenpi
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After installation you should have the project and all of the samples necessary. Run the project and you should start hearing sound effects at intervals of between 15 and 40 seconds. I found this to be about right so that you actually get to hear them without them being too repetitive.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/raspberry-pi-spooky-halloween-sound-effects.jpg&quot; alt=&quot;Raspberry Pi and Spotify&quot; :=&quot;&quot; title=&quot;Raspberry Pi and Spotify Setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now you need to also play the spooky ambient music from a Spotify playlist. I created this &lt;a href=&quot;https://open.spotify.com/user/marclittlemore/playlist/18JeNeypZVxJLMqVcGvR84&quot;&gt;Spotify Playlist of spooky Halloween noises&lt;/a&gt; which you can use. It&#39;s nearly 7 hours long so it should be enough for playing throughout the night but feel free to make your own too. You&#39;ll have to use your audio mixer to determine the correct balance between the ambient music from Spotify and the one-shot sound effects triggered by the Raspberry Pi. As I mentioned earlier, the Raspberry Pi output seems to be incredibly quiet so I probably had my inputs at around 20% of the PC output mixed with 100% of the Pi output.&lt;/p&gt;
&lt;p&gt;All you need to do now is leave this running and position your speakers in a place, and at a volume, where they can be heard by your visitors. A lot of children and their parents commented about how great this sounded so I was pleased with the eventual outcome.&lt;/p&gt;
&lt;p&gt;Here&#39;s a video of it running in my study, the speakers are hidden behind the curtains with the windows open so it could be heard from the street.&lt;/p&gt;
&lt;p&gt;https://www.youtube.com/watch?v=77df-OO72U0&lt;/p&gt;
&lt;h2 id=&quot;updating-the-sound-effects&quot;&gt;Updating the sound effects&lt;/h2&gt;&lt;p&gt;If you want to update the sound effects that are being played then it&#39;s really simple. First, you need to add the MP3 files to the &lt;code&gt;/assets&lt;/code&gt; directory. Secondly, you need to update the config file which points at them. I chose to be explicit in what sound effects I wanted playing rather than just playing every file it finds in the assets directory. Just update the &lt;code&gt;/config/config.js&lt;/code&gt; array of paths to the sound effects and add your new effects, or remove any that you don&#39;t want.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;soundFiles&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/cuckoo-clock.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/foghorn-doorbell.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/ghost-scream.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/evil-laugh.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/female-scream.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/funeral-bells.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/howling-wolf.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/cat-scream.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/woman-shrill-scream.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/thriller-laugh.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/glados-hello-friend.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/glados-hello-where-are-you.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/glados-i-see-you.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/glados-laugh.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/glados-come-over-here.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;./assets/dalek-exterminate.mp3&#39;&lt;/span&gt;
       	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope this guide helps you to easily set up your own Halloween Raspberry Pi spooky sound player. If you have any questions then post them below and I&#39;ll answer them, or raise and bugs as GitHub issues and I can get them fixed.&lt;/p&gt;
</content>
    
  </entry>
  
  <entry>
    
    <title>How To Find Bugs Using Git Bisect With This Easy Guide</title>
    
    <link href="https://www.marclittlemore.com/how-to-find-bugs-using-git-bisect-with-this-easy-guide/"/>
    <published>2016-09-29T00:00:00Z</published>
    
    <updated>2016-09-29T00:00:00Z</updated>
    
    <id>https://www.marclittlemore.com/how-to-find-bugs-using-git-bisect-with-this-easy-guide/</id>
    <author>
      <name>Marc Littlemore</name>
    </author>
    <content type="html">&lt;p&gt;A source control system is an essential tool if you&#39;re writing software, even for your own side projects. Although I&#39;ve used many version control software over the past 20+ years of development, I&#39;ve only been using &lt;code&gt;git&lt;/code&gt; for the past 3 or 4 years. However it&#39;s quickly become my version control of choice due to its ability to quickly create branches, and its distributed nature. It&#39;s easy to master the basics of &lt;code&gt;git&lt;/code&gt; but, as with most pieces of software, there&#39;s a lot of power available if you choose to go beyond them.&lt;/p&gt;
&lt;p&gt;One &lt;code&gt;git&lt;/code&gt; command that you might not have seen or used before is
&lt;code&gt;git bisect&lt;/code&gt;, which is something I&#39;ve only recently started using too. It&#39;s a great tool for quickly tracking down bugs in your project so let&#39;s take a look at how we can use it.&lt;/p&gt;
&lt;h2 id=&quot;a-wild-bug-appears&quot;&gt;A wild bug appears&lt;/h2&gt;&lt;p&gt;I&#39;m currently working on some high-traffic Node.js projects for work and, as I normally do at the start of a new task, I ran &lt;code&gt;git pull&lt;/code&gt; to ensure I had the latest code from our master branch, and then ran &lt;code&gt;npm install&lt;/code&gt; to update any dependencies. I ran the tests to check everything was ok but an odd thing happened: our test suite hung when it finished although all of the tests still passed. &lt;strong&gt;WHAT?&lt;/strong&gt; Time to investigate.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.marclittlemore.com/images/posts/a-wild-bug-appeared.jpg&quot; alt=&quot;A wild bug appears&quot; title=&quot;A wild bug appears!&quot;&gt;&lt;/p&gt;
&lt;p&gt;I had no idea why this bug had crept into the project, nor when it first appeared in the master branch. My initial thought was that it was the last pull request that had been merged, so I checked out the previous merge instead but again, the tests were still hanging. This felt like a perfect opportunity to try out &lt;code&gt;git bisect&lt;/code&gt; for the first time in order to quickly work out which commit had caused the error.&lt;/p&gt;
&lt;h2 id=&quot;what-does-the-git-bisect-command-do&quot;&gt;What does the &amp;quot;git bisect&amp;quot; command do?&lt;/h2&gt;&lt;p&gt;Bisect performs a &lt;a href=&quot;https://en.wikipedia.org/wiki/Binary_search_algorithm&quot;&gt;binary search&lt;/a&gt; to quickly find out the revision that caused a bug in your project&#39;s history. You do this by telling it which was the &amp;quot;bad&amp;quot; commit, the one which contains the bug, and which was a &amp;quot;good&amp;quot; commit, one in which you think the bug wasn&#39;t there. You can then use &lt;code&gt;git bisect&lt;/code&gt; to pick a commit which is halfway between the two endpoints and you determine whether that one is bad or good. This can be repeated by choosing the revision between these two points until you determine the specific commit which caused the bug. It&#39;s then up to you to look at the code to see what has changed and is probably the cause.&lt;/p&gt;
&lt;h2 id=&quot;the-git-bisect-process&quot;&gt;The &amp;quot;git bisect&amp;quot; process&lt;/h2&gt;&lt;p&gt;The first thing we need to do is tell &lt;code&gt;git&lt;/code&gt; that we&#39;re about to start using bisect by using the &lt;code&gt;git bisect start&lt;/code&gt; command. Before you do this, you should make sure you&#39;ve committed or stashed any local changes to avoid losing any work as it will checkout each revision as we attempt to find the version containing the bug.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is to determine when the bug first occurred and when we think that the repository was ok, and tell &lt;code&gt;git bisect&lt;/code&gt; which commit versions these were. This allows it to determine the range of revisions to search through.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Use this if the bug occurs in the current HEAD revision&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect bad HEAD

&lt;span class=&quot;token comment&quot;&gt;# But if the bug occurs in a specific revision then use its SHA hash&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect bad 62c5fa0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the good revision, you can choose an arbitrary place in the past but the more commits that have happened between the two endpoints, the more potential revisions &lt;code&gt;git bisect&lt;/code&gt; will have to check. This will depend on the speed of development in your project but it&#39;s probably wise to not choose a revision in time that was not too far away.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Choose your good revision&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect good da5e24d&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you&#39;ve chosen your two endpoints, &lt;code&gt;git bisect&lt;/code&gt; will choose a revision that&#39;s in the middle of these two and tell you how many more revisions it will need to check from this point.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect good da5e24d
Bisecting: &lt;span class=&quot;token number&quot;&gt;54&lt;/span&gt; revisions left to &lt;span class=&quot;token builtin class-name&quot;&gt;test&lt;/span&gt; after this &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;roughly &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; steps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;dac781864e62c49b279c122c42d447ed26ad16e2&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Merge pull request &lt;span class=&quot;token comment&quot;&gt;#100 from features/my-feature&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&#39;s now a process of elimination to determine which specific commit caused the bug. You do this by running your test suite or using your project to see if the bug is still present in the current revision. At this point &lt;code&gt;git bisect&lt;/code&gt; wants to know if this commit is good or bad so you answer &lt;code&gt;git bisect good&lt;/code&gt; or &lt;code&gt;git bisect bad&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect bad
Bisecting: &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt; revisions left to &lt;span class=&quot;token builtin class-name&quot;&gt;test&lt;/span&gt; after this &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;roughly &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; steps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;4f455b6e42487657e0492305f025dd82021735d5&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; some commit message from this revision&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my case, the &lt;code&gt;gulp&lt;/code&gt; task was still hanging so I answered that the commit was bad. The revisions are then split into two again and you need to continue to answer whether the revisions are good or bad until you reach the last revision and find the guilty commit. At this point &lt;code&gt;git bisect&lt;/code&gt; will show you which commit caused the problem and you can check the code in this revision to determine what has changed.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;02ce44a7491e9b0151169647115f9a073513e0ce is the first bad commit
commit 02ce44a7491e9b0151169647115f9a073513e0ce
Author: some-author &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;some-author@gmail.com&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
Date:   Mon Sep &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;:59:20 &lt;span class=&quot;token number&quot;&gt;2016&lt;/span&gt; +0100

    Added some code that might break&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; :D&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you&#39;ve determined the cause, you&#39;ll want to finish your current &lt;code&gt;git bisect&lt;/code&gt; session. For this you just use the &lt;code&gt;reset&lt;/code&gt; command and it will reset to your &lt;strong&gt;HEAD&lt;/strong&gt; to where you were before you started.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect reset&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure you reset otherwise you will find your local working directory in a strange state, having checked out out a previous revision of the project.&lt;/p&gt;
&lt;h2 id=&quot;pro-tip-bisect-automation&quot;&gt;Pro-tip: Bisect automation&lt;/h2&gt;&lt;p&gt;Manually finding the bugs in your code is helped by using &lt;code&gt;git bisect&lt;/code&gt; but you can make things even faster by automating the process. You simply have to pass a script to be executed by &lt;code&gt;git bisect run&lt;/code&gt;. Doing this will run the script against each bisected commit in order to determine which one causes the script to fail.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Set up our start and endpoints to check against&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect start
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect bad 62c5fa0
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect good da5e24d

&lt;span class=&quot;token comment&quot;&gt;# Run our gulp task which executes our&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# test suite for each bisected commit&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; bisect run gulp &lt;span class=&quot;token builtin class-name&quot;&gt;test&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&#39;ll have to ensure the script that you&#39;re using fails with a non-zero return value for this to work. For the code that fails in my above discussion, our test suite hung so it wasn&#39;t as easy to use automation here.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-use-git-bisect&quot;&gt;Conclusion: Use &amp;quot;git bisect&amp;quot;&lt;/h2&gt;&lt;p&gt;As you can see, using &lt;code&gt;git bisect&lt;/code&gt; is an easy way to quickly determine where defects have crept into a &lt;code&gt;git&lt;/code&gt; project. You should start using it as its an effective tool to understand. For more information have a read of this great git SCM article on &lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Tools-Debugging-with-Git&quot;&gt;debugging with git&lt;/a&gt;. Also, take some time to look at the &lt;a href=&quot;https://git-scm.com/docs/git-bisect&quot;&gt;git bisect documentation&lt;/a&gt; too.&lt;/p&gt;
</content>
    
  </entry>  
</feed>
