<?xml version="1.0" encoding="UTF-8" standalone="no"?><!--Generated by Site-Server v@build.version@ (http://www.squarespace.com) on Wed, 06 May 2026 14:58:45 GMT
--><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:media="http://www.rssboard.org/media-rss" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0"><channel><title>Bitfield Consulting</title><link>https://bitfieldconsulting.com/posts/</link><lastBuildDate>Wed, 06 May 2026 14:35:43 +0000</lastBuildDate><language>en-GB</language><generator>Site-Server v@build.version@ (http://www.squarespace.com)</generator><description>Go, Rust, and tech careers</description><item><title>It's a lock: sync.Mutex in Go</title><dc:creator>John Arundel</dc:creator><pubDate>Sun, 26 Apr 2026 10:20:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/sync-mutex</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:68bd6a6e7389475758ca5824</guid><description><![CDATA[When two goroutines go to war, a data race is all that you can score. To 
prevent that, Go’s sync.Mutex type can help.]]></description><content:encoded><![CDATA[<p class=""><em>From </em><a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg" data-image-dimensions="1280x960" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=1000w" width="1280" height="960" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/800294fd-f924-4c1b-acf7-8f6684612106/gopher-obtain-token.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <blockquote>
<p><em>NIGEL: Don’t touch it!</em><br />
<em>MARTY: I wasn’t going to touch it, I was just pointing at
it.</em><br />
<em>NIGEL: Well, don’t <strong>point</strong>, even.</em><br />
—<a href="https://amzn.to/3QEBXFh">“This is Spinal Tap”</a> </p>
</blockquote>
<p>We saw in <a href="https://bitfieldconsulting.com/posts/racing-with-disaster">Racing with
disaster</a> that when two goroutines go to war, a data race is all that
you can score. That is, when your program concurrently reads
<em>and</em> writes some piece of shared data, the results are
non-deterministic, and that’s generally unacceptable in distributed
systems.</p>
<p>Here’s the problematic program:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    copies <span class="op">:=</span> <span class="dv">1</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> <span class="kw">func</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>            copies<span class="op">--</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>            fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Customer A got the book&quot;</span><span class="op">)</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> <span class="kw">func</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>            copies<span class="op">--</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>            fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Customer B got the book&quot;</span><span class="op">)</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>    time<span class="op">.</span>Sleep<span class="op">(</span>time<span class="op">.</span>Second<span class="op">)</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/love/blob/main/copies/1/main.go">Listing
<code>copies/1</code></a>)</p>
<p>Goroutines A and B both read and try to decrement the shared
<code>copies</code> variable, and the result is that we can’t predict
which one will get there first, and thus which customer will get the
book. In a distributed system where the <code>copies</code> variable is
held on a remote computer, we could easily end up with a situation where
<em>both</em> customers think they’ve got the last copy of the book.</p>
<p>For example, suppose that “goroutine A” is actually Amina, a Happy
Fun Books customer happiness operative who’s trying to put through a
purchase at her sales terminal. Concurrently, the part of “goroutine B”
is played by Bhavana, who’s also trying to process purchasing the same
book at her own terminal.</p>
<p>How can Amina and Bhavana co-ordinate to avoid a data race?</p>
<p>One very simple, and low-tech, idea would be to have Amina shout out
“Hey, y’all, I’m about to modify book <code>abc</code>. Don’t do
anything with <code>abc</code> until I say it’s okay!” And then later:
“All good, I’m done with <code>abc</code> now”.</p>
<p>An audio-based protocol like this would work, sort of. It would mean
a lot of shouting, though, and that’s not really ideal: in the best
bookstores, a quiet, studious atmosphere should prevail.</p>
<p>A better scheme might be to have a big whiteboard in the middle of
the store where everyone can see it. When Amina wants to make a change
to book <code>abc</code>, she looks at the board to see if “abc” is
already up there. If not, she writes it on the board. Then she makes her
changes, and when she’s done, she rubs “abc” off the board so other
users know it’s once again safe to modify <code>abc</code>.</p>
<p>What if Bhavana wants to make a change to <code>abc</code> while
Amina still has it “locked” like this? Well, she has to wait. Yes, it
might be slightly annoying for other users to wait until Amina releases
the lock, but it’s better than the catalog data getting corrupted: that
would annoy <em>everybody</em>.</p>
<p>And under normal circumstances, no one will need to lock a book for
very long, so most books will spend most of the time unlocked. It’s
unlikely that anyone will have to wait for the lock on a given book, and
even if they do, the waiting time won’t be significant. We don’t expect
much <em>contention</em> for these locks, in the jargon.</p>
<p>It’s a workable scheme, with some caveats. For example, Amina has to
be careful to remember to <em>obtain</em> the lock—that is, to check the
board to see if the book’s ID is up there, and add it herself if not. If
she forgets to do this before modifying the book information, there’ll
be a data race.</p>
<p>Also, she has to remember to <em>release</em> the lock afterwards: to
rub the ID off the board. Otherwise, Bhavana will be stuck waiting for
the lock forever, and more and more users will gradually get stuck
waiting on the same lock over time, until eventually nobody can do
anything at all (this is called a <em>deadlock</em>).</p>
<p>Despite these potential problems, locking protocols like this are the
simplest way to prevent data races in real-life concurrent environments.
They don’t usually involve a big whiteboard, but the principle is the
same.</p>
<p>In my book <a href="https://bitfieldconsulting.com/books/deeper">The Deeper Love of Go</a>, we’ll
use Happy Fun Books as an example application to help us learn Go
programming from scratch. Starting with fundamental concepts like
functions, data, and variables, we’ll gradually build up a suite of Go
software to store and manage book information. Along the way we’ll talk
about writing command-line tools, HTTP servers and clients, designing
REST APIs, storing and retrieving data as JSON, and all sorts of other
exciting topics. I hope you’ll take a look!</p>
<h3 id="mutexes-in-go">Mutexes in Go</h3>
<p>A lock system like the one we’ve described ensures mutually exclusive
access to some data value, such as book <code>abc</code>. In other
words, if I have access to it, that excludes you having access to it,
and vice versa. In the jargon, then, the lock object that ensures this
mutual exclusion is called a <em>mutex</em>.</p>
<p>Go’s standard library includes one of these, as part of the
<code>sync</code> package: it’s called <code>sync.Mutex</code>. Here’s
an example of how it’s commonly used:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> mutex sync<span class="op">.</span>Mutex</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>mutex<span class="op">.</span>Lock<span class="op">()</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co">// do something with data</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>mutex<span class="op">.</span>Unlock<span class="op">()</span></span></code></pre>
<p>We needn’t worry at all about how the mutex works internally. We can
treat it as a black box with two methods: <code>Lock</code> and
<code>Unlock</code>. We call <code>Lock</code> to get the lock, we do
whatever we need to do with the protected data, and then we call
<code>Unlock</code> to release the lock when we’re finished.</p>
<p>So what happens if someone else already has the lock when we call
<code>Lock</code>? The answer is that <code>Lock</code> will wait until
the lock becomes available, and then obtain it for us. This can take an
arbitrary amount of time (indeed, the lock might <em>never</em> be
released), but if or when <code>Lock</code> <em>does</em> return, it
guarantees that we now have the lock.</p>
<p>When someone holds the lock on the mutex, it blocks everybody else
from accessing the protected data: that’s a mutex’s job, after all. So
we should only acquire the lock right before we need it, and release it
as soon as possible afterwards. We should also avoid doing anything too
time-consuming during this period, to avoid unnecessarily blocking the
lock for other users.</p>
<h3 id="both-reads-and-writes-need-to-be-lock-aware">Both reads and
writes need to be lock-aware</h3>
<p>It sounds like a mutex will help us to ensure safe concurrent access
to the catalog, but before we do that, there’s one more point we need to
talk about. <em>When</em> do we need to acquire the mutex lock? Is it
only when we want to change the data, or is it also when we’re reading
it?</p>
<p>You might be tempted to say that it’s only when we’re changing the
data. After all, if we’re just reading, we’re just reading! Why would we
need to get a lock on something that we’re not changing?</p>
<p>But that idea doesn’t work, because even though <em>we’re</em> just
reading, someone else might concurrently be writing. While someone has
the “write lock” on the data, it’s not safe for anyone else to read it.
By “not safe” I mean that the data we’re looking at it might be in the
middle of being changed, and we could see partial or invalid results
because of this.</p>
<p>In other words, a data race exists when two or more users (or
different parts of the same program) are accessing the same piece of
data concurrently, and <em>at least one of those accesses is a
write</em>. So all accesses to mutex-protected data need to be
lock-aware, even reads.</p>
<p>Here’s an updated version of our example program, this time with a
<code>sync.Mutex</code> which we’ll use to protect the
<code>copies</code> variable from concurrent access:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">var</span> mu sync<span class="op">.</span>Mutex</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    copies <span class="op">:=</span> <span class="dv">1</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> <span class="kw">func</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>        mu<span class="op">.</span>Lock<span class="op">()</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">defer</span> mu<span class="op">.</span>Unlock<span class="op">()</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>            copies<span class="op">--</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>            fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Customer A got the book&quot;</span><span class="op">)</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> <span class="kw">func</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>        mu<span class="op">.</span>Lock<span class="op">()</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>        <span class="cf">defer</span> mu<span class="op">.</span>Unlock<span class="op">()</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>            copies<span class="op">--</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>            fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Customer B got the book&quot;</span><span class="op">)</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a>    time<span class="op">.</span>Sleep<span class="op">(</span>time<span class="op">.</span>Second<span class="op">)</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/love/blob/main/copies/2/main.go">Listing
<code>copies/2</code></a>)</p>
<h3 id="the-lock-defer-unlock-pattern">The “lock, defer unlock”
pattern</h3>
<p>Everything is the same as before except that now we have an extra
variable <code>mu</code>, the mutex. Here’s the customer A goroutine
(customer B’s is identical except for the printed message):</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>mu<span class="op">.</span>Lock<span class="op">()</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="cf">defer</span> mu<span class="op">.</span>Unlock<span class="op">()</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    copies<span class="op">--</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Customer A got the book&quot;</span><span class="op">)</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>If the mutex scheme is going to work, then each goroutine has to
obtain the lock before trying to read the <code>copies</code> variable.
That’s what <code>mu.Lock()</code> does.</p>
<p>If the lock is available, great: we’ll obtain it and proceed straight
away. If it’s in use by someone else, the call to <code>mu.Lock()</code>
will wait until it’s released (that is, the calling goroutine will move
to the blocked queue until the lock becomes available).</p>
<p>Once the goroutine has got the lock (and made it back to the front of
the ready queue, if it had to wait in the meantime), it can proceed. We
then <code>defer</code> the call to <code>mu.Unlock()</code>, ensuring
that whatever happens in this goroutine, we release the lock when the
function exits.</p>
<h3 id="taking-the-token">Taking the token</h3>
<p>The sequence of events for each goroutine will be:</p>
<ol type="1">
<li>Obtain lock (blocking if necessary).</li>
<li>Read <code>copies</code>.</li>
<li>If the book is still available, set <code>copies</code> to
zero.</li>
<li>Release lock.</li>
</ol>
<p>This time, at step 3, the book can only be available to exactly one
of the goroutines. The two critical sections (steps 2-3 in each
goroutine) can no longer overlap: that’s what the mutex prevents.</p>
<p>It’s a little like a single-track railway line, where only one train
can safely occupy a given section of line at a time. If two trains try
to share it, especially when they’re travelling in opposite directions,
it’s usually a bad day for everyone concerned. To prevent this, we need
a way to enforce mutual exclusion of trains in each <em>block</em> (the
equivalent of our critical section).</p>
<p>One typical scheme relies on a physical token that the train driver
has to obtain before entering the block. On reaching the other end of
the block, the driver hands the token back to a signaller, so that it
can be used by the next train, and so on. Mechanical interlocking
prevents the points and signals being set for any train movement until
the corresponding token is in place.</p>
<p>The mutex in our example program is the digital equivalent of the
token: each goroutine must wait until the token is available, signalling
that it can enter the critical section. Once it leaves, it gives up the
token so that other goroutines can use it to enter their own critical
sections.</p>
<h3 id="a-race-free-program-with-locking">A race-free program with
locking</h3>
<p>In our book example, the critical section starts where the goroutine
reads <code>copies</code>, and ends after it (optionally) writes to the
same variable. So, before the goroutine enters this section, it must
obtain the “token”, by calling <code>mu.Lock()</code>. After it leaves,
it gives up the token by calling <code>mu.Unlock()</code> (via a
<code>defer</code> statement).</p>
<p>One of two things has to happen, then: <em>either</em> the customer A
routine gets the lock first and updates <code>copies</code>, meaning
that customer B misses out, <em>or</em> the customer B goroutine gets
the lock first, and customer A misses out.</p>
<p>Access to the <code>copies</code> variable is guaranteed by the lock
to be mutually exclusive, eliminating the data race. Let’s see if the
race detector agrees:</p>
<p><strong><code>go run -race main.go</code></strong></p>
<pre><code>Customer B got the book</code></pre>
<p>Well, that’s great news for customer B, and it’s good news for us,
too: we no longer have a data race. Sadly, this is little comfort to
customer A, who missed out. Maybe they’ll come back tomorrow.</p>]]></content:encoded><media:content height="835" isDefault="true" medium="image" type="image/png" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1757244217219-X647RAQ7VMX2NRFENJ7Z/gopher-obtain-token+square.png?format=1500w" width="835"><media:title type="plain">It's a lock: sync.Mutex in Go</media:title></media:content></item><item><title>Zero Cost to Love</title><dc:creator>John Arundel</dc:creator><pubDate>Wed, 01 Apr 2026 08:33:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/zero-cost-to-love</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:69b4123c88b82536671a27fc</guid><description><![CDATA[She’s an internationally famous high-performance computing architect. He’s 
a rumpled, handsome, yet socially awkward basement coder. They fall in 
love—blazing fast.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp" data-image-dimensions="1024x1024" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=1000w" width="1024" height="1024" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/8bcdc61e-9563-4fbd-baf6-184ef58c5d27/Zero+Cost+to+Love.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class=""><em>Cover by Cady Galleta</em></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p>I’m thrilled to reveal the cover for my new Rust romance novel
“<em>Zero Cost to Love</em>”, on sale April 1st.</p>
<p>She’s an internationally famous high-performance computing architect.
He’s a rumpled, handsome, yet socially awkward basement coder. When they
meet-cute by collaborating on a PR to the Rust compiler, they fall in
love—<em>blazing fast</em>.</p>
<p>But despite their shared passion for strict typing and zero-cost
abstractions, it seems that their love can never be: he favours
<strong>tabs</strong> while she’s a <strong>spaces</strong> girl.</p>
<p>💖💖💖💖💖 “Readers will <code>cargo run</code> to buy this
tempestuous tale of lust and longing frustrated by irreconcilable
differences over whitespace. You’ll be moved, not copied.”
(<em>Publishers Weekly</em>)</p>
<p>In a world of undefined behaviour, their hearts refuse to segfault.
Enjoy the emotional beats:</p>
<ul>
<li><p>The meet-cute: their pull request review spirals into a
47-comment debate about lifetime annotations.</p></li>
<li><p>The first spark: she whispers, “Your borrow checker insight… it’s
beautiful.”</p></li>
<li><p>The dark moment: she opens his editor and sees the tabs. The
horror. The betrayal.</p></li>
</ul>
<blockquote>
<p>“How long have you been hiding this from me?”<br />
“It’s configurable per project, Clara!”<br />
“Don’t gaslight me with config, Daniel!”<br />
</p>
</blockquote>
<p>Then the reconciliation at the end when they realise
<code>rustfmt</code> can save them. True love, auto-formatted.</p>
<p>Don’t miss my other, marginally less romantic <a href="https://bitfieldconsulting.com/books?category=Rust">Rust books</a>.</p>]]></content:encoded><media:content height="1024" isDefault="true" medium="image" type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1773409609048-RWB3XICXUGSQKUIZG2BA/Zero+Cost+to+Love.jpg?format=1500w" width="1024"><media:title type="plain">Zero Cost to Love</media:title></media:content></item><item><title>The Batman trap: when helping isn't helping</title><dc:creator>John Arundel</dc:creator><pubDate>Mon, 30 Mar 2026 16:44:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/batman-trap</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:68ee7df0d014624344c38c91</guid><description><![CDATA[What does a consultant actually do? Sometimes the right answer, 
surprisingly, is “nothing”.]]></description><content:encoded><![CDATA[<p class=""><em>From </em><a href="https://bitfieldconsulting.com/books/master" target="_blank"><em>Master of Your Domain</em></a></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg" data-image-dimensions="800x450" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=1000w" width="800" height="450" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/d119c592-500a-41bb-a3c0-3a53e22e4187/bat-signal.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <blockquote>
<p><em>Your ideal form of influence is first to help people see their
world more clearly, and then to let them decide what to do
next.</em><br />
—Jerry Weinberg, <a href="https://amzn.to/3uzcGE0">“Secrets of
Consulting”</a></p>
</blockquote>
<p>Let’s suppose you’re a <a href="https://bitfieldconsulting.com/posts/self-driving-people">self-driving person</a>, and you want
to run your own independent business. You’ve practised the <a href="https://bitfieldconsulting.com/posts/secret-consultant">secret consultant</a>’s mindset as an
employee, and now you’re ready to consultate for real. What does that
look like?</p>
<h3 id="what-is-consulting">What is consulting?</h3>
<p>As the name suggests, being a consultant means your primary role is
to give advice. You may also do some hands-on work for the client, such
as programming, but that’s an optional extra. What you’re there for is
to <em>know</em> things, not necessarily to do things.</p>
<p>This distinguishes a consultant from a contractor, whose primary task
<em>is</em> to do hands-on work. Generally speaking, the client tells a
contractor what to do, whereas a consultant tells the client what to
do.</p>
<p>But it’s a blurry line, and you may find yourself on both sides of it
with the same client. Indeed, what <em>you</em> think a consultant does
is much less relevant than what the client expects a consultant to
do.</p>
<h3 id="four-ways-to-help">Four ways to help</h3>
<p>As I write in my new guide to consulting and independent working, <a href="https://bitfieldconsulting.com/books/master">Master of Your Domain</a>, clients may approach you
looking for one or more of the following kinds of help:</p>
<ul>
<li><p><strong>System builder</strong>. The client has a requirement for
a specific system, application, or service. They want you to build it,
train them on how to use it, and perhaps maintain and support it over
time.</p></li>
<li><p><strong>Problem solver</strong>. The client has a specific issue
that they don’t have the knowledge or resources to fix, and that’s where
you come in. You may be fixing the problem yourself, or training other
people to fix it, or both.</p></li>
<li><p><strong>Problem finder</strong>. The client knows they have
problems (that’s always true), but they don’t know exactly what they
are. Your job is to find out what’s wrong, and help fix it.</p></li>
<li><p><strong>Hired hand</strong>. The client knows what needs to be
done, they just don’t have the time or resources to do it. Your role is
to execute the tasks they give you.</p></li>
</ul>
<p>Understanding exactly which of these the client is looking for is
important, but it doesn’t mean that your engagement will be limited to
just that one aspect of consulting.</p>
<p>Instead, you’ll probably find that what most clients <em>need</em> is
some combination of these four things, whether they’re aware of the fact
or not. Whatever you’ve been asked to do, your work as a consultant is
partly to figure out what <em>else</em> the client really needs, and do
that, too.</p>
<h3 id="three-things-you-know">Three things you know</h3>
<p>Still, it’s worth remembering that your primary value to clients is
that <em>you know something they don’t</em>. If that’s not true, then
you can’t do much for them. Your valuable knowledge falls into three
main categories:</p>
<ul>
<li><p><strong>What to do</strong>. It’s very unlikely that the client
has a problem that no one’s ever seen before. It’s probably been seen
and solved many times, and you can guide them to the most appropriate
solution.</p></li>
<li><p><strong>What not to do</strong>. The client knows they are in
dangerous waters, but they don’t know what specifically to beware of.
With your help, the client can steer clear of the worst
hazards.</p></li>
<li><p><strong>Unknown unknowns</strong>. You can spot problems the
client may not even know they have, or trouble they might be
inadvertently storing up for the future.</p></li>
</ul>
<p>One important thing you <em>don’t</em> always know, however, is what
the client’s problem actually is. That’s because the problem they
initially present to you may not be the one that really needs
solving.</p>
<p>Always respond, not with an answer, but with a question: “What
problem are you really trying to solve?” If the answer still sounds like
a solution, instead of a problem, then keep asking.</p>
<p>The consultant’s job is not merely to give the client what they want,
but to convince them to <em>want</em> the right thing. You can only know
what that is once you understand what problem they’re really trying to
solve.</p>
<h3 id="listening">Listening</h3>
<p>The mistake most novice consultants make is to start by fixing the
problem. Anxious to show value, they give the client a huge info-dump
about everything they need to change, fix, discard, introduce, or
understand.</p>
<p>Your job, though, is to not to be the hero of the hour, bravely
pulling victims from the flaming wreckage. That might feel very
satisfying in the short term, but it won’t bring you much repeat
business.</p>
<p>Instead, aim to be the client’s <em>undercover success engineer</em>,
discreetly assisting them in just the right ways, and at the right
moments, and letting them take all the credit for the resulting
turnaround.</p>
<p>After all, they were smart enough to engage you in the first place,
so in a way they do deserve the credit. It takes a wise person to know
when to ask for help.</p>
<p>And there’s another reason to begin every engagement by listening
more than you talk: you might learn something. The client has a much
clearer idea of the problem than you do. After all, they’ve been
thinking about it for a lot longer.</p>
<p>If you try to start by explaining the problem to <em>them</em>, then,
the danger is that you’ll come across at best vague and woolly, and at
worst completely wrong.</p>
<p>Instead, be open and receptive, invite them to give you as much
context as possible about the situation, and turn your ears up to eleven
for as long as it takes for them to tell you what you need to know.</p>
<p>At this point the client will probably be asking “What should we do?”
Again, you’ll be tempted to jump right in and start dispensing your
hard-won wisdom. But a more thoughtful consultant would ask instead
“What have you tried up to now? And what happened?”</p>
<p>If the obvious answers had done the trick, you wouldn’t be here, so
it’ll save time to eliminate these in advance. The client has probably
already done a lot of work on the issue themselves, so use that as a
head start.</p>
<h3 id="let-them-get-there">Let them get there</h3>
<p>When you do present a solution, don’t drop it on the client
fully-formed like tablets of heaven. Show them the general direction,
and then they can come with you on the journey.</p>
<p>The best way to get someone to buy into an idea is to let them have
it themselves. In other words, lead the discussion gently up to a point
where what the client needs to do becomes so obvious, they don’t need
you to say it for them.</p>
<p>And when you let them be the one to put the idea into words, they’re
already halfway on board. You can move on to developing the idea along
the right sort of lines: “That sounds great, and then we could…”</p>
<p>Even if someone is against an idea to start with, if you can get them
to contribute a tiny bit to it, you may be able to turn them into an
enthusiastic advocate.</p>
<h3 id="underpromise-and-overdeliver">Underpromise and overdeliver</h3>
<p>Be pessimistic about timescales. In this business, “pessimist” is
simply what an optimist calls a realist. Everything takes longer than
expected, even when we expect it to, because of <a href="https://en.wikipedia.org/wiki/Hofstadter%27s_law">Hofstadter’s
Law</a>. That’s straightforward, but the danger is that the client
<em>wants</em> you to lie to them about how long the work will take.</p>
<p>I mean, they don’t want you to <em>lie</em> to them, but they’re
extremely motivated to hear an encouraging answer. If you say “three
weeks”, they might argue with you until you come up with something else.
If you say “a week”, they’ll probably accept it.</p>
<p>Like every other consultant, I grappled with this problem too. When I
told clients the work they wanted would take ten hours, they were
grumpy, but when I delivered it in only nine hours all was forgiven.
When I told them it would take five hours, but delivered it in nine,
they were <em>furious</em>—and didn’t use me again.</p>
<p>Telling people what they want to hear is easier than telling them the
truth, but it’s just setting yourself up for failure. Letting clients
talk you into unachievable timescales is like giving sugar to cranky
kids: it might keep them quiet for a little while, but sooner or later a
full-scale meltdown is inevitable.</p>
<h3 id="the-batman-trap">The Batman trap</h3>
<blockquote>
<p><em>Things are the way they are because they got that way.</em><br />
—Jerry Weinberg, <a href="https://amzn.to/3uzcGE0">“Secrets of
Consulting”</a> </p>
</blockquote>
<p>There’s a saying that amateurs fix the problem, while professionals
fix the <em>system</em>. Solving the immediate pain points is nice, but
they’ll probably recur in one form or another. Your real work is helping
the client figure out why these issues arose in the first place, and
prevent them from happening again.</p>
<p>If you find yourself fixing the same problem for the same client
twice in a row, then you’re evidently not addressing the underlying
issues. Figure out what the client is doing wrong that means they keep
having this problem, and help them build a better system.</p>
<p>The “system” you’re fixing includes the people who work there. A
consultant’s natural and creditable desire to be helpful can lead them
into the “Batman trap”. This is when people don’t ever actually acquire
the skills and knowledge they need to fix things that go wrong. Why
would they need to? The consultant always fixes everything!</p>
<p>If you find that your client’s staff regularly send up the bat-signal
to get your help dealing with low-level problems, this may also be a
signal that you’ve become Batman. A city that has a caped crusader
standing ready to deal with crime and disorder is a city that doesn’t
have the skills to fix itself. Don’t train your clients to become
Gotham.</p>
<p>Of course you want to be a useful consultant, and you will be, but
sometimes the highest form of consultancy is “helping by not helping”.
Give people the tools they need and show them what to do, but don’t keep
turning the wrench for them.</p>
<p>When things break and someone shouts for help, resist the strong
temptation to swoop in and save them. If Batman stops showing up for
every little incident, people will eventually figure out how to handle
them without him. And Batman can move to another city where they haven’t
figured that out yet.</p>
<h3 id="consulting-has-its-limits">Consulting has its limits</h3>
<p>Even when we trust and value someone very highly, we don’t always
agree with everything they say. There’s only so much influence you can
have with any given client, and only so much change that you can achieve
for them. It’s important to know when you’ve reached that limit.</p>
<p>One way or another, most consulting engagements come to an end
eventually, even when you’ve done everything right. There are always
useful lessons to learn, and it’s wise to reflect on what went well and
what you’d do differently if you had the chance. Indeed, we learn more
from our failures than from our successes. “When you fall down,” as the
saying goes, ”pick something up”.</p>
<p>If you’ve enjoyed this series, you might like my new book <a href="https://bitfieldconsulting.com/books/master">Master of Your Domain</a>. It’s all about how to
build and run your own business, whether that’s consulting, writing,
teaching, or whatever you do best. You’ll learn how to market yourself,
how to make and sell products, and how to find and delight customers.
Happy Independence Day!</p>]]></content:encoded><media:content height="356" isDefault="true" medium="image" type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1760460305764-6LXN3Z0OL7IHY8MOXQG6/bat-signal-square.jpg?format=1500w" width="445"><media:title type="plain">The Batman trap: when helping isn't helping</media:title></media:content></item><item><title>Aprende Rust: recursos en español</title><dc:creator>John Arundel</dc:creator><pubDate>Sat, 21 Mar 2026 17:05:49 +0000</pubDate><link>https://bitfieldconsulting.com/posts/aprende-rust</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:69becff2fed5585eafd7984f</guid><description><![CDATA[Enlaces y recursos en español para aprender el lenguaje de programación 
Rust.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp" data-image-dimensions="1408x1056" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=1000w" width="1408" height="1056" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/da2571c6-787c-422a-ac12-7ab959a5654b/ferris-aprende-rust.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class=""><em>Illustration by Cady Galleta</em></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p>Esta página tiene enlaces y recursos en español para aprender el
lenguaje de programación Rust.</p>
<h2 id="libros">Libros</h2>
<ul>
<li><a href="https://book.rustlang-es.org/">El Lenguaje de Programación
Rust</a> — por Steve Klabnik, Carol Nichols y Chris Krycho, con
contribuciones de la comunidad Rust</li>
<li><a href="https://embedded-book.rustlang-es.org/">Rust Embebido</a> —
un libro introductorio sobre del uso de Rust en sistemas embebidos</li>
<li><a href="https://go-book.rustlang-es.org/es/introduction">Rust para
Go devs</a> — una guía minimalista de Rust para desarrolladores de
Go</li>
<li><a href="https://dotnet-book.rustlang-es.org/">Rust para C#/.NET
devs</a> — una guía para desarrolladores de C# y .NET</li>
</ul>
<h2 id="artículos">Artículos</h2>
<ul>
<li><a href="https://bitfieldconsulting.com/posts/rust-vs-go-es">Rust vs Go: ¿cuál elegir?</a> — Rust
y Go comparten mucho, pero sus diferencias importan. ¿Cuál deberías
elegir?</li>
<li><a href="https://www.ait.mx/blog/rust-lenguaje-de-programacion/">Rust: El
lenguaje de programación del futuro</a> — examina qué hace que Rust sea
tan especial, sus ventajas y usos</li>
<li><a href="https://www.datacamp.com/es/blog/rust-vs-python">Rust vs
Python</a> — compara la sintaxis, el rendimiento y gestión de la
memoria</li>
</ul>
<h2 id="cursos">Cursos</h2>
<ul>
<li><a href="https://google.github.io/comprehensive-rust/es/index.html">Comprehensive
Rust</a> — un curso gratuito de Rust desarrollado por el equipo de
Android en Google</li>
</ul>
<h2 id="mentoría-y-capacitación">Mentoría y capacitación</h2>
<ul>
<li><a href="https://bitfieldconsulting.com/training/learn-rust">Perfecciona Rust conmigo</a> —
mentoría personalizada de Rust</li>
<li><a href="https://bitfieldconsulting.com/training/rust">Entrenamiento Rust de grupos</a> —
capacitación transformativa para su equipo</li>
</ul>
<h2 id="vídeos">Vídeos</h2>
<ul>
<li><a href="https://www.youtube.com/playlist?list=PLojDVPvSO1DjYj8bMcMOU3KzLbRww-3Eb">Rust
en español</a> — aprende Rust en español y a tu ritmo con estos vídeos
de YouTube de Roberto Huertas</li>
<li><a href="https://www.youtube.com/@rustyfullstack">Rusty Full
Stack</a> — un canal en el que aprenderemos a programar en el lenguaje
de programación Rust</li>
</ul>]]></content:encoded><media:content height="1056" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1774113397975-PTJ50EAZPJ2HP0BALTDM/ferris-aprende-rust-square.webp?format=1500w" width="1056"><media:title type="plain">Aprende Rust: recursos en español</media:title></media:content></item><item><title>My Rust dev setup in 2026</title><dc:creator>John Arundel</dc:creator><pubDate>Tue, 10 Mar 2026 11:53:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/rust-dev-tools</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:69a2ecd2ac244302cf167b6c</guid><description><![CDATA[You don’t need many tools to write Rust—just a decent editor, and maybe an 
AI assistant or two. Here’s my Rust dev setup in 2026.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp" data-image-dimensions="1536x1024" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=1000w" width="1536" height="1024" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ab5c56db-9bc5-4b62-b282-fdfe605adbf0/hero.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p data-rte-preserve-empty="true" class=""><em>Illustration by Cady Galleta</em></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p>Software engineers are ingenious people, and we’re particularly
clever at avoiding <em>real</em> work by fiddling with our tools, trying
new tools, bikeshedding other people’s tools, and having internet flame
wars about whether using the wrong editor makes you a bad person.</p>
<p>I’m a <a href="https://bitfieldconsulting.com/training/learn-rust">Rust teacher</a> and <a href="https://bitfieldconsulting.com/books?category=Rust">author</a>. Over 45 years of programming,
I’ve used a lot of different tools and done an incredible amount of
bikeshedding about them. Now, a little older, slightly wiser, and
considerably greyer, I don’t care so much about tools: I’m more
interested in actually building stuff. Whatever gets the job done is
fine by me.</p>
<p>That said, here’s what I mostly use for Rust development today, so
that you can berate me about my choices. I am unafraid.</p>
<h2 id="zed">1. Zed</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          <a class="
                sqs-block-image-link
                
          
        
              " href="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp" target="_blank"
          >
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp" data-image-dimensions="2085x1141" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=1000w" width="2085" height="1141" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b475f9b1-b393-4de2-9b04-a85b9cd7e08f/zed.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          </a>
        

        
      
        </figure>
      

    
  


  



  
    <p>I don’t want to spend a load of time configuring my editor,
installing plugins, tweaking settings, and obsessively maintaining a
config file. I just want something that looks nice, has great Rust
support, and works the way I want it to, right out of the box. That’s <a href="https://zed.dev/">Zed</a>.</p>
<p>If you’re familiar with VS Code or Cursor (and it’s hard to imagine
that you aren’t), then Zed looks and behaves exactly the way you’d
expect. Even the keyboard shortcuts are pretty much the same. It’s just
faster, lighter, less bloated, and more reliable.</p>
<p>Why Zed works for me:</p>
<ul>
<li><p><strong>AI integration</strong>. I use AI as an occasional
assistant or librarian, so it’s nice that Zed has full editor
integration: I can highlight some lines and prompt “Refactor this to a
method”, or “What’s wrong here?”. There’s also a chat panel to the side,
for more in-depth conversations (“Should I be thinking about
<code>par_iter</code> here, or just threads?”)</p>
<p>If AI’s not your jam, Zed also works perfectly well without it—a
single switch turns off all AI trappings. For me, though, it’s a handy
external brain pack.</p></li>
<li><p><strong>Rust support</strong>. With <code>rust-analyzer</code>
integration via LSP, I get syntax colouring, Rust-aware renaming and
refactoring, inline annotations and type hints, inline errors and
warnings, and useful code actions like auto-imports. There’s also an
integrated debugger, and though I rarely use it, I appreciate that it’s
there.</p></li>
<li><p><strong>Fast and responsive</strong>. Zed is a native Rust app
using GPU-accelerated rendering, so it feels snappy compared to JS-based
tools. Zed runs fine on low-end hardware, and <em>great</em> on anything
better.</p></li>
</ul>

  

&nbsp;
  
    
  

&nbsp;
  
    <h2 id="chatgpt">2. ChatGPT</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          <a class="
                sqs-block-image-link
                
          
        
              " href="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp" target="_blank"
          >
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp" data-image-dimensions="1431x912" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=1000w" width="1431" height="912" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/af913b16-c2b9-4287-9e9a-64f8ce2bcd6c/chatgpt.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          </a>
        

        
      
        </figure>
      

    
  


  



  
    <p>It’s fashionable in some quarters these days to cry “AI is the
devil’s work!” and refuse to adopt any kind of LLM-powered tools. “My
code is a holy temple that shall not be profaned by the taint of AI”,
say the Butlerian jihadists. That’s okay, and I’m neither a booster nor
a doomer—but what I am is pragmatic. If there’s a tool that helps me get
my work done better and quicker, I’ll use it.</p>
<p>I almost never ask AI to <em>generate</em> code; that’s the fun part,
so I like to reserve it for my own enjoyment. Instead, I’ll talk to <a href="https://chatgpt.com/">ChatGPT</a> about the problem I’m solving,
possible lines of approach, things to consider, and how to refine my
initial ideas. Then I write the code, and we’ll review and polish it
together.</p>
<p>The key ways I use AI for Rust development:</p>
<ul>
<li><p><strong>Helpful pair programmer</strong>. As I work, I’ll run
into problems and compile errors. Usually, a little thought is enough to
resolve these, but when it’s not, I’ll ask ChatGPT to explain to me
what’s really going on. Only when I’m confident I completely understand
the problem will I start changing code to try to solve it.</p></li>
<li><p><strong>Forensic code reviewer</strong>. When I think I’m done,
I’ll ask AI to review the code for possible bugs, issues, and edge cases
I might have overlooked. ChatGPT usually has some valuable improvements
to suggest; I would have figured these out myself eventually, perhaps,
but why wait?</p></li>
<li><p><strong>Patient, supportive teacher</strong>. I work with ChatGPT
all the time to deepen and extend my learning—about Rust and everything
else. I’ll ask it to generate quiz questions to test my understanding of
some concept, and we’ll review and refine my answers until I’m
completely happy that I’ve mastered the topic.</p>
<p>AI never gets tired or bored, it never gets impatient, and it never
belittles me for asking really dumb or obvious questions. It’s not
infallible, but who is? I’m learning a lot from AI about how to be a
better <a href="https://bitfieldconsulting.com/training">teacher</a>.</p></li>
</ul>
<p>I use the ChatGPT desktop app, which is faster and more functional
than the web version, and I almost exclusively use the voice input mode
(little microphone icon at the right of the prompt box), which is super
responsive and accurate. It’s also <em>way</em> quicker than typing.</p>
<p>Speaking of which…</p>
<h2 id="wispr-flow">3. Wispr Flow</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          <a class="
                sqs-block-image-link
                
          
        
              " href="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp" target="_blank"
          >
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp" data-image-dimensions="1462x962" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=1000w" width="1462" height="962" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ea9a2478-7918-4c45-af01-e8054691a825/wisprflow.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          </a>
        

        
      
        </figure>
      

    
  


  



  
    <p><a href="https://ref.wisprflow.ai/rust-dev-stack">Wispr Flow</a> is
“voice input everywhere”. It’s like the voice input in ChatGPT, and it
uses the same LLM-based speech-to-text API, but it works in any editor,
app, text box, or input area. Anywhere you can type, you can talk
instead. It feels weird at first, but it’s <em>so</em> much faster it
quickly becomes second nature.</p>
<p>I was surprised at how much faster using Wispr Flow is than typing on
the Mac, but it’s a real game-changer on mobile. Instead of pecking at
that horrible little iPhone keyboard and wrestling with autocorrect, I
can just switch to the Wispr Flow “keyboard” and speak the text instead.
This is how iOS should have been from day one.</p>
<p>I use Wispr Flow all day, every day, for:</p>
<ul>
<li><p><strong>Emails, chats, texts, WhatsApp, Slack, and
socials</strong>. I can put in Airpods, step away from the desk, and
wander round the house or garden while I catch up on all my daily comms
and admin work. Not only is it much quicker than typing, I actually find
I’m communicating better and more frequently because it’s
easier.</p></li>
<li><p><strong>Writing</strong>. I write <a href="https://bitfieldconsulting.com/books">books</a> for
a living, so I write hundreds of thousands of words a year. That’s a lot
of time and keystrokes I can save by just <em>saying</em> what I want to
say. As a result, my writing is easier, more fluent, and more
conversational. I can capture all my rambling thoughts on a subject with
Wispr Flow, then use ChatGPT to help me refine and organise them into a
working draft.</p></li>
<li><p><strong>AI interaction</strong>. I use ChatGPT’s built-in
speech-to-text feature a lot, but whenever I’m interacting with AI
somewhere that doesn’t have that, such as the Zed agent panel, I can use
Wispr Flow to speak my prompts. Now I wince when I see someone painfully
and slowly typing out
“w-h-a-t-d-o-e-s-t-h-i-s-e-r-r-o-r-m-e-s-s-a-g-e-m-e-a-n-?”. Just say
it, bro.</p></li>
</ul>
<p><a href="https://ref.wisprflow.ai/rust-dev-stack">Wispr Flow</a> is
the most impressive bit of software I’ve seen in years: they’ve really
gone to a lot of trouble to make it smooth, fast, responsive and almost
invisible. So much so that I’ve registered to be an affiliate—full
disclosure—and if you use my link to sign up, I’ll get a small
commission on your first year’s subscription. ’Preciate it.</p>
<h2 id="nushell">4. Nushell</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          <a class="
                sqs-block-image-link
                
          
        
              " href="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg" target="_blank"
          >
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg" data-image-dimensions="2471x1248" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=1000w" width="2471" height="1248" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ebdd4aa8-f5a9-42d5-8149-301709d19378/nu.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          </a>
        

        
      
        </figure>
      

    
  


  



  
    <p><a href="https://www.nushell.sh/">Nu</a> is the shell that Rust would
be. Calling it a “shell” buries the lede: it’s a <em>thinking
language</em> based on pipelines, structured data, and interactive
queries.</p>
<p>It’s like SQLite and Powershell had a baby, and then implanted JQ
into it at birth, and that baby was a rich, modern programming language
inspired by Rust (and the baby was written in Rust). And the baby was a
shell.</p>
<p>I can wrangle, slice, dice, and query data from JSON, TOML, CSV, and
HTTP APIs, write scripts and programs to automate my most common
workflows, and manage all my files using a shell that actually
understands data types, instead of just raw text.</p>
<p>Combined with <a href="https://atuin.sh/">Atuin</a>, the magical
shell history manager, so that I need never re-type a command line I’ve
used before, and running it all inside Zed’s embedded terminal, I’ve got
a dev environment that’s completely in the spirit of Unix, but also very
2026.</p>
<h2 id="thats-it">5. That’s it</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          <a class="
                sqs-block-image-link
                
          
        
              " href="https://www.apple.com/pixelmator-pro/" target="_blank"
          >
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp" data-image-dimensions="800x516" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=1000w" width="800" height="516" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/34c17c22-5816-4b75-b242-c8dedb5290af/pixelmator.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          </a>
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class=""><a href="https://www.apple.com/pixelmator-pro/" target="_blank">Pixelmator Pro</a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  













































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          <a class="
                sqs-block-image-link
                
          
        
              " href="https://brave.com/" target="_blank"
          >
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp" data-image-dimensions="800x600" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=1000w" width="800" height="600" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5469dfbe-7ec8-4d30-99d2-1d9739ff069f/brave.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          </a>
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class=""><a href="https://brave.com/" target="_blank">Brave</a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  













































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          <a class="
                sqs-block-image-link
                
          
        
              " href="https://www.omnigroup.com/omnifocus/" target="_blank"
          >
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp" data-image-dimensions="800x510" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=1000w" width="800" height="510" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c181e55-0066-4f58-a12b-a88e7a257aad/omnifocus.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          </a>
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class=""><a href="https://www.omnigroup.com/omnifocus/" target="_blank">OmniFocus</a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p>I don’t need much else. Rust already comes with a great set of
built-in tools:</p>
<ul>
<li><p><strong><code>rustup</code></strong> takes care of Rust
installation and updates.</p></li>
<li><p><strong>Cargo</strong> handles all my build and dependency
management tasks</p></li>
<li><p><strong><code>rust-analyzer</code></strong> gives me Rust power
tools in whatever editor I’m using</p></li>
</ul>
<p>Other than that, my Mac is pretty stock. I don’t even use Homebrew,
because I don’t want a million out-of-date and out-of-band packages
cluttering up my hard drive.</p>
<p>A few other bits and pieces that improve my quality of life:</p>
<ul>
<li><p><strong><a href="https://brave.com/">Brave</a></strong> is my
browser of choice, because I don’t like ads (the ability to watch
YouTube videos without the ads is life-changing). It supports Chrome
extensions, so I can drive it entirely from the keyboard with <a href="https://vimium.github.io/">Vimium</a>.</p></li>
<li><p><strong><a href="https://www.apple.com/pixelmator-pro/">Pixelmator Pro</a></strong>
is the one-stop graphics, layout, drawing, and photo editing tool that
Photoshop used to be. I appreciate being able to pay for a piece of
software once, and own it, rather than renting it by the month.</p></li>
<li><p><strong><a href="https://www.omnigroup.com/omnifocus/">OmniFocus</a></strong> is
the all-singing, all-dancing task manager I’ve used for decades. It
syncs across all my devices. I run my life, business, books, and
software projects on it. It’s one of the best-value things I’ve ever
bought. Not affiliated; I just like it.</p></li>
</ul>]]></content:encoded><media:content height="1024" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1772288050292-MMMM7O75P28X4Y9MK2X8/hero-square.webp?format=1500w" width="1024"><media:title type="plain">My Rust dev setup in 2026</media:title></media:content></item><item><title>Go the right way: the Zen of Go coding</title><dc:creator>John Arundel</dc:creator><pubDate>Tue, 17 Feb 2026 11:24:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/go-right-way</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:690e0254175d027594095d2b</guid><description><![CDATA[I finally found the one serene, omnicompetent guru who writes perfect code! 
Here are her ten secret mantras of Go excellence.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp" data-image-dimensions="1280x720" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=1000w" width="1280" height="720" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/446b0ee7-8254-4dcf-a0d0-f66a3f2d4ae7/10x-commandments.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p data-rte-preserve-empty="true"><em>Illustration courtesy of JetBrains</em></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p>Ever wondered if there’s a software engineer, somewhere, who actually
knows what they’re doing? Well, I finally found the one serene,
omnicompetent guru who writes perfect code. I can’t disclose the
location of her mountain hermitage, but I <em>can</em> share her ten
mantras of Go excellence. Let’s meditate on them together.</p>
<p><strong>Warning:</strong> This is not medical advice. Side effects
may include higher code quality, reduced stress levels, and increased
salary.</p>
<h2 id="write-packages-not-programs">1. Write packages, not
programs</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg" data-image-dimensions="600x390" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=1000w" width="600" height="390" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b54bc6de-ca13-40ca-9047-80fbe9c96b8a/morpheus.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>What if I told you there was a library of over a million Go packages
to do just about anything you could ever want? How much would it speed
up your development if you could just import the package that solves
your problem, instead of painfully re-inventing it from scratch every
time?</p>
<p>The <a href="https://pkg.go.dev/std">standard library</a> is great,
but the <a href="https://pkg.go.dev/"><em>universal</em> library</a> of
free, open-source software is Go’s biggest asset. Return the favour by
writing not just programs, but <a href="https://bitfieldconsulting.com/posts/packages">packages</a> that
others can use too.</p>
<p>Apparently we <em>can</em> have nice things, so it’s only fair that
if you develop some useful Go code yourself, you should contribute it
back to the universal library, right? That means writing your code not
merely as a one-off program for your own use case, but as a reliable,
reusable, importable software component published with an open-source
licence.</p>
<p>Writing packages, not programs, has some design implications, too.
Keep your <code>main</code> function minimal: its only job is to process
flags and arguments, figure out what the user asked for, and call into
your “engine” package to do the actual work.</p>
<p>Your package shouldn’t print anything; instead, it should return the
data. Leave it up to the consumer of your package to decide what to do
with it. Similarly, don’t call <code>panic</code> or
<code>os.Exit</code> in your package; return errors instead. Don’t
recover panics from your dependencies either: this can mask problems
your consumer needs to know about.</p>
<p>Keep your <a href="https://go.dev/doc/modules/layout">module
structure</a> simple: ideally, a single package. Complex trees of
sub-packages make it difficult for users to find what they need, and
you’ll give yourself import cycle headaches too. Instead, keep the
structure flat, and limit your package to just two files: one for the
implementation, and one for the tests.</p>
<h2 id="test-everything">2. Test everything</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp" data-image-dimensions="600x504" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=1000w" width="600" height="504" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/153d8e3c-a131-4f57-b28a-530255aa991d/tests-failing.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Speaking of tests, my Go guru assures me that they’re the only true
path to saintly software. When I mentor new Go programmers myself, I
sometimes sense their hearts sinking a bit at the mention of tests.
There’s a perception that they’re like healthy exercise: undoubtedly a
good habit, but one that we all struggle to maintain. “Write tests” is
like “Go to the gym“, in other words: good advice, but hard to act
on.</p>
<p>On the contrary, I think it’s more like saying “Eat chocolate!”
That’s the kind of advice we all love to hear, and it doesn’t take much
willpower to apply.</p>
<p>Tests are great, and when you approach them the right way they can be
fun to write. They’re a useful design tool, because writing a test makes
<em>you</em> the first user of your own function. If it’s awkwardly
named, or has too many dependencies, or returns the wrong kind of
result, you’ll notice right away. If the thing you’re testing is easy to
write a test for, it’ll be easy to use in real programs—and if it’s not,
fix it so it is.</p>
<p>Writing tests helps you <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">dogfood</a>
your packages: awkward names and inconvenient APIs are obvious when you
use them yourself.</p>
<p>Make your tests small and granular, focused on one small piece of
logic—maybe a single method or function—and use your package’s public
API instead of sneaking behind the curtain to look at implementation
details. Those might change, whereas the behaviours your users care
about shouldn’t. Check your test coverage to make sure you’ve tested all
the code that matters (and it all matters).</p>
<ul>
<li><p><a href="https://bitfieldconsulting.com/posts/test-names">Test
names should be sentences</a></p></li>
<li><p>Focus tests on small units of user-visible behaviour</p></li>
<li><p>Add <a href="https://en.wikipedia.org/wiki/Integration_testing">integration
tests</a> for end-to-end checks</p></li>
<li><p>Test binaries with <a href="https://bitfieldconsulting.com/posts/test-scripts"><code>testscript</code></a></p></li>
</ul>
<h2 id="write-code-for-reading">3. Write code for reading</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp" data-image-dimensions="600x312" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=1000w" width="600" height="312" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/722ca203-45c5-414c-9271-3885c43cf5be/reading-code.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>The best way to make sure you’re writing readable code is to
<em>read</em> it. Put yourself in the mindset of someone who doesn’t
already know what the code does, and go through it line by line. Is it
easy to follow what’s happening? Is the purpose clear? Are the names of
functions and variables well-chosen to convey what they represent? How
much <a href="https://medium.com/@egonelbre/psychology-of-code-readability-d23b1ff1258a">cognitive
load</a> are you asking them to lift?</p>
<p>Read other people’s programs too; as soon as you spot something you
don’t understand, ask yourself why not. If the meaning of a certain line
is not obvious, ask what change would <em>make</em> it obvious? Don’t
rely on comments; these are often wrong, out of date, or merely
unhelpful.</p>
<p>In your own code, use comments sparingly, and as a last resort. Focus
on explaining <em>why</em> this code is here, not <em>what</em> it
does—if that needs explanation, refactor the code to clarify it.</p>
<p>Good names make code read naturally. <a href="https://www.youtube.com/watch?v=PAAkCSZUG1c&amp;t=1099s">Design
the architecture, name the components, document the details</a>.
Simplify wordy functions by moving low-level “paperwork” into smaller
functions with informative names (<code>createRequest</code>,
<code>parseResponse</code>). Keep each function at roughly the same
level of abstraction.</p>
<p>Use <a href="https://go.dev/talks/2014/names.slide#1">consistent
naming</a> to maximise <a href="https://jarosz.dev/code/do-not-overload-your-brain-go-function-tips/">glanceability</a>:</p>
<ul>
<li><code>err</code> for errors</li>
<li><code>data</code> for arbitrary <code>[]byte</code>s</li>
<li><code>buf</code> for buffers</li>
<li><code>file</code> for <code>*os.File</code> pointers</li>
<li><code>path</code> for pathnames</li>
<li><code>i</code> for index values</li>
<li><code>req</code> for requests</li>
<li><code>resp</code> for responses</li>
<li><code>ctx</code> for contexts</li>
</ul>
<p>Don’t be afraid to refactor and re-work your programs ruthlessly
until they’re as clear and simple and focused as you can possibly make
them. Check this by showing the code to someone else and asking them to
talk you through it, line by line. Watching where they stumble will show
you the speed-bumps in your code: keep refactoring until you’ve
flattened them out.</p>
<h2 id="be-safe-by-default">4. Be safe by default</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp" data-image-dimensions="600x548" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=1000w" width="600" height="548" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/536a53a5-053d-4efa-8678-4e06f2ddca6b/im-in-danger.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Use “always valid values” in your programs, and design types so that
users can’t accidentally create values that won’t work. <a href="https://go-proverbs.github.io/">Make the zero value useful</a>;
this lets users create literals of your type with minimal paperwork. For
example, <code>bool</code> fields will default to <code>false</code>, so
make that make sense for your type.</p>
<p>Don’t create useless “config” structs; use fields on the object
itself to configure its behaviour. If these fields can be invalid, don’t
let users write them directly: instead, make them unexported, and
provide validating methods to get and set their values.</p>
<p>If your object has sensible defaults, write a constructor method that
returns a valid, default object ready to use. Add configuration using
“WithX” methods:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>widget <span class="op">:=</span> NewWidget<span class="op">().</span>WithTimeout<span class="op">(</span>time<span class="op">.</span>Second<span class="op">)</span></span></code></pre>
<p>Use named <a href="https://go.dev/blog/constants">constants</a>
instead of magic values. <a href="https://pkg.go.dev/net/http#pkg-constants"><code>http.StatusOK</code></a>
is self-explanatory; <code>200</code> isn’t. Define your own constants
so IDEs like GoLand can auto-complete them, preventing typos. Use <a href="https://bitfieldconsulting.com/posts/iota"><code>iota</code></a>
to auto-assign arbitrary values:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> <span class="op">(</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    Planet <span class="op">=</span> <span class="ot">iota</span> <span class="co">// 0</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    Star          <span class="co">// 1</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    Comet         <span class="co">// 2</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span></code></pre>
<p>Prevent security holes by using <a href="https://pkg.go.dev/os#Root"><code>os.Root</code></a> instead of
<code>os.Open</code>, eliminating <a href="https://go.dev/blog/osroot">path traversal attacks</a>:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>root<span class="op">,</span> err <span class="op">:=</span> os<span class="op">.</span>OpenRoot<span class="op">(</span><span class="st">&quot;/var/www/assets&quot;</span><span class="op">)</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> err <span class="op">!=</span> <span class="ot">nil</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> err</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="cf">defer</span> root<span class="op">.</span>Close<span class="op">()</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>file<span class="op">,</span> err <span class="op">:=</span> root<span class="op">.</span>Open<span class="op">(</span><span class="st">&quot;../../../etc/passwd&quot;</span><span class="op">)</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Error: &#39;openat ../../../etc/passwd: path escapes from parent&#39;</span></span></code></pre>
<p>Don’t require your program to run as <code>root</code> or in <a href="https://en.wikipedia.org/wiki/Setuid"><code>setuid</code></a>
mode; let users configure the minimal permissions and capabilities they
need.</p>
<h2 id="wrap-errors-dont-flatten">5. Wrap errors, don’t flatten</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp" data-image-dimensions="600x458" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=1000w" width="600" height="458" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e565cc23-070b-400a-8c16-1595689e99a6/error.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Don’t type-assert errors or <a href="https://bitfieldconsulting.com/posts/comparing-errors">compare
error values directly</a> with <code>==</code>, define named “sentinel”
values that users can match errors against:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ErrOutOfCheese <span class="op">=</span> errors<span class="op">.</span>New<span class="op">(</span><span class="st">&quot;++?????++ Out of Cheese Error. Redo From Start.&quot;</span><span class="op">)</span></span></code></pre>
<p>Don’t inspect the string values of errors to find out what they are;
this is fragile. Instead, use <a href="https://pkg.go.dev/errors#Is"><code>errors.Is</code></a>:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> errors<span class="op">.</span>Is<span class="op">(</span>err<span class="op">,</span> ErrOutOfCheese<span class="op">)</span> <span class="op">{</span></span></code></pre>
<p>To add run-time information or context to an error, don’t flatten it
into a string. Use the <code>%w</code> verb with <code>fmt.Errorf</code>
to create a <a href="https://bitfieldconsulting.com/posts/wrapping-errors"><em>wrapped</em></a>
error:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="cf">return</span> fmt<span class="op">.</span>Errorf<span class="op">(</span><span class="st">&quot;GNU Terry Pratchett: %w&quot;</span><span class="op">,</span> ErrOutOfCheese<span class="op">)</span></span></code></pre>
<p>This way, <code>errors.Is</code> can still match the wrapped error
against your sentinel value, even though it contains extra
information.</p>

  

&nbsp;
  
    
  

&nbsp;
  
    <h2 id="avoid-mutable-global-state">6. Avoid mutable global state</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp" data-image-dimensions="600x338" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=1000w" width="600" height="338" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f2190f87-f398-41c8-8229-a0199ca6edf7/global-variable.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Even if your package doesn’t create goroutines, your users might
<em>use</em> it concurrently. Package-level variables can cause <a href="https://bitfieldconsulting.com/posts/racing-with-disaster">data races</a>: reading a variable
from one goroutine while writing it from another can crash your
program.</p>
<p>Instead, use a <code>sync.Mutex</code> to prevent concurrent access,
or allow access to the data only in a single “guard” goroutine that
takes read or write requests via a channel.</p>
<p>Don’t use global objects like <a href="https://pkg.go.dev/net/http#DefaultServeMux"><code>http.DefaultServeMux</code></a>
or <a href="https://pkg.go.dev/net/http#DefaultClient"><code>DefaultClient</code></a>;
packages you import might invisibly change these objects, maliciously or
otherwise.</p>
<p>Instead, create a new instance with <a href="https://pkg.go.dev/net/http#NewServeMux"><code>http.NewServeMux</code></a>
(for example) so that you own it exclusively, and then configure it how
you want.</p>
<h2 id="use-structured-concurrency-sparingly">7. Use (structured)
concurrency sparingly</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp" data-image-dimensions="600x398" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=1000w" width="600" height="398" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fbac7ea7-cec9-4f46-b35d-7cc5f38451b5/multithreading.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Concurrent programming is a minefield: it’s easy to trigger crashes
or race conditions. Don’t introduce concurrency to a program unless it’s
unavoidable.</p>
<p>When you do use <a href="https://bitfieldconsulting.com/posts/goroutines">goroutines</a>
and channels, keep them strictly confined: once they escape the scope
where they’re created, it’s hard to follow the flow of control. “Global”
goroutines, like global variables, can lead to hard-to-find bugs.</p>
<p>Make sure any goroutines you create will terminate before the
enclosing function exits, using a <a href="https://go.dev/blog/context">context</a> or <a href="https://pkg.go.dev/sync#WaitGroup">waitgroup</a>:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> wg sync<span class="op">.</span>WaitGroup</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>wg<span class="op">.</span>Go<span class="op">(</span>task1<span class="op">)</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>wg<span class="op">.</span>Go<span class="op">(</span>task2<span class="op">)</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>wg<span class="op">.</span>Wait<span class="op">()</span></span></code></pre>
<p>The <code>Wait</code> call ensures that both tasks have completed
before we move on, making control flow easy to understand, and
preventing resource leaks.</p>
<p>Use <a href="https://pkg.go.dev/golang.org/x/sync/errgroup">errgroups</a> to
catch the first error from a number of parallel tasks, and <a href="https://go.dev/play/p/2YOGE29kYsE">terminate</a> all the
others:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> eg errgroup<span class="op">.</span>Group</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>eg<span class="op">.</span>Go<span class="op">(</span>task1<span class="op">)</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>eg<span class="op">.</span>Go<span class="op">(</span>task2<span class="op">)</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>err <span class="op">:=</span> eg<span class="op">.</span>Wait<span class="op">()</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> err <span class="op">!=</span> <span class="ot">nil</span> <span class="op">{</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    fmt<span class="op">.</span>Printf<span class="op">(</span><span class="st">&quot;error %v: all other tasks cancelled&quot;</span><span class="op">,</span> err<span class="op">)</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;all tasks completed successfully&quot;</span><span class="op">)</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>When you take a <a href="https://go.dev/ref/spec#ChannelType">channel</a> as the parameter
to a function, take either its send or receive aspect, but not both.
This prevents a common kind of deadlock where the function tries to send
<em>and</em> receive on the same channel concurrently.</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> produce<span class="op">(</span>ch <span class="kw">chan</span><span class="op">&lt;-</span> Event<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// can send on `ch` but not receive</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> consume<span class="op">(</span>ch <span class="op">&lt;-</span><span class="kw">chan</span> Event<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">// can receive on `ch` but not send</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<h2 id="decouple-code-from-environment">8. Decouple code from
environment</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp" data-image-dimensions="600x338" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=1000w" width="600" height="338" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/03ccd475-b7b6-4ff8-ba7c-ba8f555136e2/connected.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>We all know that good software avoids excessive <em>coupling</em>
between packages or components, but many programs are too tightly
coupled to the operating system or environment where they run.</p>
<p>Don’t depend on OS or environment-specific details. Don’t use <a href="https://pkg.go.dev/os#Getenv"><code>os.Getenv</code></a> or <a href="https://pkg.go.dev/os#Args"><code>os.Args</code></a> deep in your
package: only <code>main</code> should access environment variables or
command-line arguments.</p>
<p>Instead of taking choices away from users of your package, let them
configure it however they want. Be agnostic about how you’re configured.
Let users decide whether they want to inject settings via the
environment, flags, config files, API calls, or some other way.</p>
<p>Single binaries are easier for users to install, update, and manage;
don’t distribute config files. If necessary, create your config file at
run time using defaults.</p>
<p>Use <a href="https://pkg.go.dev/embed"><code>go:embed</code></a> to
bundle static data, such as images or certificates, into your
binary:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> _ <span class="st">&quot;embed&quot;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">//go:embed hello.txt</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> s <span class="dt">string</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>fmt<span class="op">.</span>Println<span class="op">(</span>s<span class="op">)</span> <span class="co">// `s` now has the contents of &#39;hello.txt&#39;</span></span></code></pre>
<p>Use <a href="https://pkg.go.dev/github.com/adrg/xdg"><code>xdg</code></a>
instead of hard-coding paths. Don’t assume <code>$HOME</code> exists.
Don’t assume <em>any</em> disk storage exists, or is writable.</p>
<p>Go is popular in constrained environments, so be frugal with memory.
Don’t read all your data at once; handle one chunk at a time, re-using
the same buffer. This will keep your memory footprint small and reduce
<a href="https://go.dev/doc/gc-guide">garbage collection</a> cycles.</p>
<h2 id="design-for-errors">9. Design for errors</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp" data-image-dimensions="600x337" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=1000w" width="600" height="337" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/82af2616-de2b-4962-8e39-41d86e5a5c7e/error-handling.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Always check errors, and handle them if possible, retrying where
appropriate. Report run-time errors to the user and exit gracefully,
reserving <code>panic</code> for <a href="https://www.alexedwards.net/blog/when-is-it-ok-to-panic-in-go">internal
program errors</a>. Don’t ignore errors using <code>_</code>: this leads
to obscure bugs. Assume that anything that can error <em>will</em>
error, and handle it appropriately.</p>
<p>Retry on transient errors if that makes sense. Don’t let the program
panic on predictable run-time errors, such as failing to read a file: a
stack trace won’t help the user figure out what’s wrong. Reserve
<code>panic</code> for unrecoverable internal program bugs only.</p>
<p>Don’t make the user rely on documentation to be able to run your
program. Make their first-run experience a pleasant one: instead of
nasty error messages, show usage hints and examples. Don’t try to
interact with users by prompting them via the console or dialog boxes.
Let them automate your program and run it headlessly, using flags or
config files to customise its behaviour.</p>
<h2 id="log-only-actionable-information">10. Log only actionable
information</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp" data-image-dimensions="600x408" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=1000w" width="600" height="408" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac2ea5e2-fb4b-4260-804a-011f1d081397/logs.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>If you use logging, don’t spam the user with pointless info messages:
if nothing needs saying, say nothing. <a href="https://en.wikipedia.org/wiki/Logorrhea_(psychology)">Logorrhea</a>
is irritating, so don’t spam the user with trivia.</p>
<p>If you log at all, log only <em>actionable</em> errors that someone
needs to fix. Don’t use fancy loggers, just print to the console, and
let users redirect that output where they need it. Never log <a href="https://pkg.go.dev/log/slog#example-LogValuer-Secret">secrets</a>
or personal data.</p>
<p>Use <a href="https://pkg.go.dev/log/slog"><code>slog</code></a> to
generate machine-readable JSON:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>logger <span class="op">:=</span> slog<span class="op">.</span>New<span class="op">(</span>slog<span class="op">.</span>NewJSONHandler<span class="op">(</span>os<span class="op">.</span>Stdout<span class="op">,</span> <span class="ot">nil</span><span class="op">))</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>logger<span class="op">.</span>Error<span class="op">(</span><span class="st">&quot;oh no&quot;</span><span class="op">,</span> <span class="st">&quot;user&quot;</span><span class="op">,</span> os<span class="op">.</span>Getenv<span class="op">(</span><span class="st">&quot;USER&quot;</span><span class="op">))</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Output:</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co">// {&quot;time&quot;:&quot;...&quot;,&quot;level&quot;:&quot;ERROR&quot;,&quot;msg&quot;:&quot;oh no&quot;,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co">// &quot;user&quot;:&quot;bitfield&quot;}</span></span></code></pre>
<p>Logging is not for request-scoped troubleshooting: use <a href="https://opentelemetry.io/docs/concepts/signals/traces/">tracing</a>
instead. Don’t log performance data or statistics: that’s what <a href="https://opentelemetry.io/docs/concepts/signals/metrics/">metrics</a>
are for.</p>
<h2 id="guru-meditation">Guru meditation</h2>

  












































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp" data-image-dimensions="500x281" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=1000w" width="500" height="281" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/f8f60b36-a526-4f97-a314-54fd2116dd6c/code-quality.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Let’s be real: no program is perfect, and the same applies to
programmers. We won’t always achieve all the goals set out here, or not
at first. It’s more important to get the program working first, get it
in front of users early, and only once it does what it’s supposed to
should we worry about making the code nicer.</p>
<p>My mountain-dwelling guru also says, “Make it work first, then make
it right. Draft a quick <a href="https://wiki.c2.com/?WalkingSkeleton">walking skeleton</a>, using
<a href="https://bitfieldconsulting.com/posts/tdd-shameless-green">shameless
green</a>, and try it out on real users. Solve their problems first, and
only <em>then</em> focus on code quality.”</p>
<p>Equally, though, it’s a mistake not to <em>care</em> about code
quality. You never know whether your Go package will be used in
something like a <a href="https://hackaday.com/2015/10/26/killed-by-a-machine-the-therac-25/">medical
X-ray</a> machine or a <a href="https://www.simscale.com/blog/nasa-mars-climate-orbiter-metric/">spacecraft</a>
control system. All software is critical to somebody.</p>
<p>Software takes more time to maintain than it does to write, so invest
an extra 10% effort in refactoring, simplifying, and improving code
while you still remember how it works.</p>]]></content:encoded><media:content height="686" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1762526051860-VHHVWLPOXW5PBJ37GU8J/10x-commandments-square.webp?format=1500w" width="686"><media:title type="plain">Go the right way: the Zen of Go coding</media:title></media:content></item><item><title>Rust vs Go: ¿cuál elegir?</title><dc:creator>John Arundel</dc:creator><pubDate>Sun, 15 Feb 2026 13:30:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/rust-vs-go-es</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:6991c04d12159d6f2f48b758</guid><description><![CDATA[Un resumen de Rust y Go que destaca sus ventajas, señala lo que comparten y 
aclara sus diferencias clave. ¿Cuál deberías elegir?]]></description><content:encoded><![CDATA[<p class=""><a href="https://bitfieldconsulting.com/posts/rust-vs-go" target="_blank"><em>English version</em></a></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp" data-image-dimensions="1091x513" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=1000w" width="1091" height="513" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class=""><strong>Rust y Go comparten mucho, pero sus diferencias importan. ¿Cuál deberías elegir?</strong></p>





















  
  




  
    <p><strong>Rust</strong> es un lenguaje <a href="https://bitfieldconsulting.com/posts/why-rust">potente, rico y gratificante</a> que prioriza la
seguridad y la corrección, sin sacrificar el poder ni la eficiencia. Es
ideal para crear software que no solo <em>funcione</em>, sino que siga
siendo fiable con el tiempo.</p>
<p><strong>Go</strong> es un lenguaje pequeño, fácil de aprender y
rápido de escribir. Con Go, los desarrolladores pueden crear software
con rapidez y adaptarse con facilidad.</p>
<p>En un eslogan:</p>
<blockquote>
<p><em>Rust para alto riesgo, Go para bajo costo.</em></p>
</blockquote>
<p>Aquí está un resumen de ambos lenguajes que destaca sus ventajas,
señala lo que comparten y aclara sus diferencias clave.</p>
<h2 id="metas-compartidas">Metas compartidas</h2>
<p>Rust y Go son lenguajes relativamente modernos que se benefician de
las muchas lecciones que hemos aprendido a lo largo de las últimas
décadas de la ingeniería de software. Priorizan:</p>
<ul>
<li><p><strong>Seguridad de memoria</strong>. Rust y Go eliminan errores
graves de seguridad presentes en lenguajes como C y C++, incluidos los
<em>buffer overflows</em> (desbordamientos de búfer), los <em>null
pointers</em> (punteros nulos) y las <em>data races</em> (carreras de
datos).</p></li>
<li><p><strong>Herramientas modernas</strong>. Ambos tienen
<em>toolchains</em> (cadenas de herramientas) rápidas, potentes y
unificadas; con ellos, los desarrolladores pueden escribir, probar y
implementar el software con facilidad.</p></li>
<li><p><strong>Rendimiento y escalabilidad</strong>. Go y Rust se
dirigen explícitamente a los proyectos de gran envergadura,
desarrollados por equipos grandes, que tienen que funcionar de forma
fiable a gran escala y con alto rendimiento.</p></li>
</ul>
<h2 id="diferencias-importantes">Diferencias importantes</h2>
<p>Rust y Go tienen mucho en común, particularmente en comparación con
lenguajes heredados como C/C++, Java, Python y Ruby. Pero tienen también
filosofías de diseño radicalmente diferentes.</p>
<p>Las prioridades clave de diseño de Go son:</p>
<ul>
<li><p><strong>Simplicidad</strong>. Es un lenguaje pequeño, fácil de
aprender y rápido de escribir, particularmente en proyectos complejos.
Deja fuera o simplifica muchas funciones avanzadas de otros
lenguajes.</p></li>
<li><p><strong>Abstracción</strong>. Go proporciona servicios de alto
nivel incluso de concurrencia y <em>garbage collection</em> (recolección
de basura), para que el desarrollador pueda enfocarse en resolver el
problema en vez de estar ahogado en papeleo.</p></li>
<li><p><strong>Estabilidad</strong>. Go enfatiza fuertemente la
retrocompatibilidad, lo que facilita el mantenimiento del software a
largo plazo. Rara vez introduce funciones nuevas; prefiere realizar
mejoras pequeñas e incrementales en rendimiento y calidad de
vida.</p></li>
</ul>
<p>Rust, por el contrario, se trata de:</p>
<ul>
<li><p><strong>Corrección</strong>. Rust se orienta a aplicaciones
críticas para la seguridad—industriales, médicas o
aeroespaciales—mediante un análisis estático avanzado que elimina muchos
errores en tiempo de compilación.</p></li>
<li><p><strong>Rendimiento</strong>. El compilador de Rust genera código
optimizado que se ejecuta tan rápido como lo permite el hardware, con un
rendimiento comparable al de C++ o incluso al lenguaje
ensamblador.</p></li>
<li><p><strong>Control</strong>. Para obtener el máximo de las CPU
modernos, hay que programar a bajo nivel. Rust ofrece control estricto y
interoperabilidad excelente con bibliotecas de C/C++.</p></li>
</ul>
<h2 id="cuándo-elegir-go">Cuándo elegir Go</h2>
<p>Go es un lenguaje ideal cuando la situación exige:</p>
<ul>
<li><p><strong>Incorporación rápida</strong>. Go tiene muy poca
sintaxis, pocas palabras clave y solo las funciones esenciales. Los
desarrolladores pueden aprender los fundamentos y ser productivos
rápidamente.</p></li>
<li><p><strong>Creación rápido de prototipos</strong>. Go se adapta a
desarrollo ágil porque los equipos pueden crear y evaluar rápidamente
prototipos.</p></li>
<li><p><strong>Costes mínimos de desarrollo</strong>. Go es ideal para
proyectos grandes y equipos porque es más fácil formar a los
desarrolladores, y requieran menos experiencia.</p></li>
</ul>
<h2 id="los-dominios-ideales-de-go">Los dominios ideales de Go</h2>
<p>Gracias a su enfoque en simplicidad y velocidad, Go es una elección
popular para aplicaciones como las siguientes:</p>
<ul>
<li><p><strong>Servicios web, redes y nube</strong>. Go está orientado a
microservicios pequeños y ligeros, backends web y sistemas basados en
contenedores, diseñados para operar a gran escala.</p></li>
<li><p><strong>Software de línea de negocio</strong>. Herramientas y
flujos de trabajo a medida, gestión de datos, aplicaciones CRM / ERP y
automatismos de negocio funcionan bien con el estilo de desarrollo
rápido de Go.</p></li>
<li><p><strong>Infraestructura, SRE y monitoreo</strong>. Go es ideal
para la ingeniería de plataformas y facilita la creación de software
confiable y escalable para monitoreo, automatización, despliegue y
gestión de configuración</p></li>
</ul>
<h2 id="cuándo-elegir-rust">Cuándo elegir Rust</h2>
<p>Rust es una buena elección cuando sus prioridades son:</p>
<ul>
<li><p><strong>Fiabilidad y resiliencia</strong>. Las funciones de
seguridad de Rust son ideales cuando el código debe funcionar
correctamente siempre y seguir haciéndolo durante años o incluso
décadas.</p></li>
<li><p><strong>Alto rendimiento</strong>. Para <em>operating
systems</em>, sistemas en tiempo real, aplicaciones de baja latencia,
videojuegos y cargas de trabajo de procesamiento intensivo, Rust se
ejecuta con gran rapidez y aprovecha al máximo el hardware
subyacente.</p></li>
<li><p><strong>Uso eficiente de recursos</strong>. La frugalidad y
eficiencia de Rust se adaptan bien a hardware limitado o de bajo consumo
o bajo coste como dispositivos integrados, IoT, satélites, vehículos y
aeronaves autónomas y sistemas militares.</p></li>
</ul>
<h2 id="los-dominios-ideales-de-rust">Los dominios ideales de Rust</h2>
<p>Los puntos fuertes de Rust—seguridad, corrección y fiabilidad—lo
hacen la mejor opción para:</p>
<ul>
<li><p><strong>Automatización industrial y robótica</strong>. Rust es
ideal para sistemas en tiempo real y telemática, como el control de
plantas industriales, máquinas herramienta y la gestión de dispositivos
concurrentes.</p></li>
<li><p><strong>Automotriz, aeroespacial y militar</strong>. Estos
entornos exigentes aprovechan las funciones críticas de seguridad y el
control de bajo nivel del hardware que ofrece Rust.</p></li>
<li><p><strong>Medicina y salud</strong>. Las cualidades de rendimiento
y seguridad de Rust son muy adecuadas para aplicaciones intensivas en
datos, como imagen médica y diagnóstico, herramientas quirúrgicas
robóticas, dispositivos conectados—incluidos marcapasos y monitores de
salud—y la automatización de laboratorios.</p></li>
</ul>
<h2 id="conclusión">Conclusión</h2>
<p>Rust y Go son ambos elecciones excelentes para el desarrollo de
software de propósito general y ofrecen una combinación de funciones y
rendimiento que los sitúa por encima de competidores tradicionales como
Java, C/C++, Python, Ruby, JavaScript / TypeScript, C#, PHP, Scala y
Swift.</p>
<p>Go prioriza la simplicidad, la escalabilidad y la <strong>velocidad
de desarrollo</strong>, por lo que es la mejor opción para equipos y
aplicaciones que necesitan lanzar rápidamente y mantener los costes al
mínimo.</p>
<p>Rust, por otro lado, está optimizado para el software <strong>crítico
para la seguridad</strong> que exige el máximo rendimiento; es la
elección lógica cuando la fiabilidad prima sobre otros factores.</p>
<p>Bitfield Consulting ofrece capacitación y recursos de aprendizaje
efectivos y de gran calidad para Rust, Go y habilidades generales de
desarrollo de software:</p>
<table>
<colgroup>
<col />
<col />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>Tutoriales</th>
<th>Estudio individual</th>
<th>Mentoría individual</th>
<th>Entrenamiento de grupos</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitfieldconsulting.com/posts/tag/Rust">Tutoriales
Rust</a></td>
<td><a href="https://bitfieldconsulting.com/books?category=Rust+books">Libros Rust</a></td>
<td><a href="https://bitfieldconsulting.com/training/learn-rust">Mentoría
Rust</a></td>
<td><a href="https://bitfieldconsulting.com/training/rust">Entrenamiento
Rust de grupos</a></td>
</tr>
<tr>
<td><a href="https://bitfieldconsulting.com/posts/tag/Go">Tutoriales
Go</a></td>
<td><a href="https://bitfieldconsulting.com/books?category=Go+books">Libros
Go</a></td>
<td><a href="https://bitfieldconsulting.com/training/learn-go">Mentoría
Go</a></td>
<td><a href="https://bitfieldconsulting.com/training/go">Entrenamiento Go de
grupos</a></td>
</tr>
<tr>
<td><a href="https://bitfieldconsulting.com/posts">Tutorials habilidades de
desarrollo</a></td>
<td><a href="https://bitfieldconsulting.com/books?category=Careers+books">Libros habilidades de
desarrollo</a></td>
<td><a href="https://bitfieldconsulting.com/training/skills">Mentoría
habilidades de desarrollo</a></td>
<td><a href="https://bitfieldconsulting.com/training/ai-tools">Entrenamiento
habilidades de desarrollo de grupos</a></td>
</tr>
</tbody>
</table>
<p>Vea también <a href="https://bitfieldconsulting.com/posts/aprende-rust">Aprende Rust: recursos en
español</a>.</p>]]></content:encoded><media:content height="513" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1771159739421-JO0YM9RN8J76Y36TZMJD/rust-vs-go.webp?format=1500w" width="1091"><media:title type="plain">Rust vs Go: ¿cuál elegir?</media:title></media:content></item><item><title>Rust vs Go</title><dc:creator>John Arundel</dc:creator><pubDate>Wed, 11 Feb 2026 09:04:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/rust-vs-go</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:5fa52db75dfc746ea899bfdc</guid><description><![CDATA[Which is a better choice, Rust or Go? Which language should you choose for 
your next project, and why? How do the two compare in areas like 
performance, simplicity, safety, features, scale, and concurrency?]]></description><content:encoded><![CDATA[<blockquote><p class=""><a href="https://bitfieldconsulting.com/posts/rust-vs-go-es" target="_blank">Versión en español</a></p></blockquote>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png" data-image-dimensions="744x350" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=1000w" width="744" height="350" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1632583991063-DG67R13E0S7XK244CSVE/rust-vs-go.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class=""><strong>Rust and Go have many similarities, but also important differences. Which is right for you?</strong></p>





















  
  




  
    <p><strong>Rust</strong> is a <a href="https://bitfieldconsulting.com/posts/why-rust">powerful, rich,
and rewarding</a> language that prioritises safety and correctness
without sacrificing power or efficiency. It’s ideal for building
software that <em>has</em> to work and keep working.</p>
<p><strong>Go</strong> is a small, elegant language that’s easy to learn
and quick to write. Go lets developers move fast, while staying
flexible.</p>
<p>In a bumper sticker:</p>
<blockquote>
<p><em>Rust for high stakes, Go for low costs.</em></p>
</blockquote>
<p>Here’s a quick overview of both languages, highlighting the important
advantages of each, pointing out what they have in common, and showing
where they differ in important ways.</p>
<p>If you’re a developer wondering which language would be better for
you to learn, or a business considering which language to adopt from a
strategic point of view, read on.</p>
<h2 id="common-goals">Common goals</h2>
<p>Both Rust and Go are relatively modern languages, incorporating many
of the lessons we’ve learned from decades of software engineering. They
both prioritise:</p>
<ul>
<li><p><strong>Memory safety</strong>. Rust and Go are both designed to
eliminate the kind of correctness and security bugs that plague older
languages such as C and C++, including buffer overflows, null pointers,
and data races.</p></li>
<li><p><strong>Modern tooling</strong>. Both languages have fast,
powerful, unified toolchains that make it easy for developers to build,
test, and deploy their software.</p></li>
<li><p><strong>Performance and scale</strong>. Go and Rust both
explicitly target large projects, developed by large teams, that must
operate reliably at global scale in high-throughput
applications.</p></li>
</ul>
<h2 id="important-differences">Important differences</h2>
<p>While Rust and Go have much in common, especially when compared to
legacy languages such as C/C++, Java, Python, and Ruby, they also have
radically different design philosophies.</p>
<p>The key design priorities for Go are:</p>
<ul>
<li><p><strong>Simplicity</strong>. It’s a small language, intended to
be easy to learn and quick to compile, especially in large and complex
projects. It leaves out or simplifies many advanced features found in
other languages.</p></li>
<li><p><strong>Abstraction</strong>. Go provides high-level facilities
such as concurrency and garbage collection, letting the programmer focus
on solving their problem instead of getting bogged down with
admin.</p></li>
<li><p><strong>Stability</strong>. Go has a strong emphasis on backwards
compatibility, making software easier to maintain over the long term. It
rarely introduces new features, preferring small, incremental
performance and quality-of-life improvements.</p></li>
</ul>
<p>Rust, on the other hand, is all about:</p>
<ul>
<li><p><strong>Correctness</strong>. Rust targets safety-critical
applications such as industrial, medical, and aerospace, using
state-of-the-art static analysis that eliminates many common bugs at
compile time.</p></li>
<li><p><strong>Performance</strong>. Rust’s clever compiler produces
optimised code that runs as fast as the underlying hardware will allow,
equalling the performance of C++ or assembly language programs without
sacrificing memory safety.</p></li>
<li><p><strong>Control</strong>. To get the most out of modern CPUs,
programmers need to get “close to the metal”, and Rust offers low-level
control and excellent interoperability with C/C++ libraries.</p></li>
</ul>
<h2 id="when-to-choose-go">When to choose Go</h2>
<p>Go is an ideal language when the situation demands:</p>
<ul>
<li><p><strong>Rapid onboarding</strong>. Go has very little syntax, few
keywords, and the bare minimum of features. Programmers can learn the
fundamentals and be productive in the language very quickly.</p></li>
<li><p><strong>Fast prototyping</strong>. Agile development suits Go,
because teams can get a skeleton product up and running quickly for
evaluation.</p></li>
<li><p><strong>Minimal development costs</strong>. Because programmers
are easier to train, and need less experience to be effective, Go works
well with bigger projects and teams that need to control cost.</p></li>
</ul>
<h2 id="gos-ideal-domains">Go’s ideal domains</h2>
<p>Because of its focus on simplicity and speed, Go is a popular choice
for applications such as:</p>
<ul>
<li><p><strong>Web, cloud, or network services</strong>. Small,
lightweight, high-scale microservices, web application backends, and
container-based systems all favour Go.</p></li>
<li><p><strong>Line-of-business tools</strong>. Bespoke, site-specific
tools and workflows, data management, CRM/ERP applications, and business
automations work well with Go’s rapid development style.</p></li>
<li><p><strong>Infrastructure, SRE, and monitoring</strong>. Go is an
ideal platform engineering language, making it easy to build reliable
and scalable monitoring, automation, deployment, and configuration
management software.</p></li>
</ul>

  

&nbsp;
  
    
  

&nbsp;
  
    <h2 id="when-to-choose-rust">When to choose Rust</h2>
<p>Picking Rust makes sense when the priorities are:</p>
<ul>
<li><p><strong>Reliability and resilience</strong>. The safety features
of Rust are ideal when code has to work correctly every time and stay
working for years or even decades with minimal maintenance.</p></li>
<li><p><strong>High performance</strong>. For system kernels and
drivers, real-time systems, low-latency applications, gaming, and
compute-intensive workloads like AI, Rust code runs fast and takes
maximum advantage of the available hardware.</p></li>
<li><p><strong>Efficient use of resources</strong>. Rust’s frugality and
efficiency suits small, low-power, or low-cost hardware targets such as
embedded devices, IoT, satellites, autonomous vehicles and aircraft, and
military systems.</p></li>
</ul>
<h2 id="rusts-ideal-domains">Rust’s ideal domains</h2>
<p>Rust’s core strengths of safety, correctness, and reliability make it
a top choice for:</p>
<ul>
<li><p><strong>Industrial automation and robotics</strong>. Rust is
ideal for real-time systems and telematics, controlling complex
industrial plant or machine tools, and concurrent handling of many
devices at once.</p></li>
<li><p><strong>Automotive, aerospace, and military</strong>. These
challenging environments make the most of Rust’s safety-critical
features and low-level hardware control.</p></li>
<li><p><strong>Medical and healthcare</strong>. Rust’s performance and
safety qualities suit data-intensive imaging and diagnostic
applications, robotic surgical tools, connected devices such as
pacemakers and home health monitors, and lab automation.</p></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Both Rust and Go are excellent choices for general-purpose software
development, and offer a combination of features and performance that
lift them above traditional competitors such as Java, C/C++, Python,
Ruby, JavaScript / TypeScript, C#, PHP, Scala, and Swift.</p>
<p>Go prioritises simplicity, scale, and <strong>speed of
development</strong>, so it’s the best option for teams and applications
that need to ship fast, yet keep costs to a minimum.</p>
<p>Rust, on the other hand, is optimised for
<strong>safety-critical</strong> software demanding maximum performance;
it’s the logical choice when reliability trumps all other concerns.</p>
<p>Bitfield Consulting offers high-quality, effective training and
learning resources for Rust, Go, and general software development
skills:</p>
<table>
<colgroup>
<col />
<col />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>Tutorials</th>
<th>Self-study</th>
<th>Individual mentoring</th>
<th>Team training</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitfieldconsulting.com/posts/tag/Rust">Rust
tutorials</a></td>
<td><a href="https://bitfieldconsulting.com/books?category=Rust+books">Rust
books</a></td>
<td><a href="https://bitfieldconsulting.com/training/learn-rust">Rust
mentoring</a></td>
<td><a href="https://bitfieldconsulting.com/training/rust">Rust team
training</a></td>
</tr>
<tr>
<td><a href="https://bitfieldconsulting.com/posts/tag/Go">Go
tutorials</a></td>
<td><a href="https://bitfieldconsulting.com/books?category=Go+books">Go
books</a></td>
<td><a href="https://bitfieldconsulting.com/training/learn-go">Go
mentoring</a></td>
<td><a href="https://bitfieldconsulting.com/training/go">Go team
training</a></td>
</tr>
<tr>
<td><a href="https://bitfieldconsulting.com/posts">Dev skills
tutorials</a></td>
<td><a href="https://bitfieldconsulting.com/books?category=Careers+books">Dev skills books</a></td>
<td><a href="https://bitfieldconsulting.com/training/skills">Dev skills
mentoring</a></td>
<td><a href="https://bitfieldconsulting.com/training/ai-tools">Dev skills
training</a></td>
</tr>
</tbody>
</table>]]></content:encoded><media:content height="1091" isDefault="true" medium="image" type="image/png" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1631444995097-A7JOBWN2X0IMO7EO2QYW/rust-vs-go-square.png?format=1500w" width="1091"><media:title type="plain">Rust vs Go</media:title></media:content></item><item><title>What's so great about Rust?</title><dc:creator>John Arundel, Cady Galleta</dc:creator><pubDate>Sun, 01 Feb 2026 12:46:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/why-rust</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:697260c8a6e5fa00836f10fc</guid><description><![CDATA[If you’ve heard about Rust and you’re wondering about the facts behind the 
hype, this is for you: no sales pitch, just a clear, point-by-point outline 
of why Rust is worth considering.]]></description><content:encoded><![CDATA[<p class=""><em>This article is for anyone who’s heard about Rust, and is curious to know what makes it worth looking at. No hype, just clear, pragmatic arguments to help you make an informed choice.</em></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp" data-image-dimensions="800x655" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=1000w" width="800" height="655" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c5730fb9-e6a4-4717-8ba0-6dc3c2330937/worth-your-time.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p data-rte-preserve-empty="true">Artwork by Cady Galleta</p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p><strong>What’s so great about Rust, then?</strong> It’s a fair
question, and one that can be surprisingly difficult to answer—at least,
when someone puts you on the spot. It’s a bit like suddenly being asked
“Why do you love your wife?”. If you feel a little tongue-tied, it’s not
because you can’t think of any good answers. Rather, so many reasons may
come to mind that you’re not sure what to say <em>first</em>.</p>
<p>And when people ask me “Why Rust?” I know it’s not out of skepticism,
but from genuine curiosity. They haven’t yet heard a clear signal
through all the noise. They’re really asking “What makes Rust special,
and why should <em>I</em> care?”</p>
<p>I’ll try to answer that without tripping over my tongue, or sounding
like a shiny-eyed cult member. And when we’re done, if you’re not
interested in accepting Rust as your personal saviour, I want you to
know that I am okay with that. Go in peace, my friend. Be well.</p>
<p>But first, hear me out.</p>
<h2 id="the-core-value-proposition">The core value proposition</h2>
<p>Rust is a truly excellent programming language. Perhaps the best we
have, by any reasonable definition: powerful, portable, and
productive.</p>
<p>It’s modern enough to incorporate the lessons we’ve learned from
eighty-odd years of software engineering. Yet it doesn’t spend too many
innovation tokens: Rust is founded on solid, well-proven principles that
have worked in other languages for decades.</p>
<h3 id="correctness-and-safety">Correctness and safety</h3>
<p>Rust leans hard into helping us write correct programs, catching as
many problems as possible at compile time, and catching any that remain
at run time.</p>
<p>Not <em>all</em> software is safety-critical, but when lives really
are at stake—in medical, industrial, military, automotive, and aerospace
applications—we need a language that puts reliability front and
centre.</p>
<p>Here’s what that looks like in practice:</p>
<ul>
<li><p><strong>Memory safety</strong>. Rust automatically eliminates
many of the correctness and security problems that plague other
languages, such as <em>buffer overflows</em>.</p></li>
<li><p><strong>Data safety</strong>. Rust prevents accidental corruption
or mutation of data by making sure that only one program thread or task
at a time can have write access to values, preventing <em>data
races</em> and other mutability issues.</p></li>
<li><p><strong>Referential safety</strong>. Rust guarantees that
references to data will always be valid, with extensive compile-time
checking: <em>null pointers</em> can’t exist in Rust, removing a common
source of bugs and crashes.</p></li>
</ul>
<p>If this doesn’t mean much to you yet, don’t worry. You don’t need to
be a computer science guru to get the benefit of Rust’s safety features.
Long story short, Rust protects the programmer by <em>making invalid
states unrepresentable</em>.</p>
<h3 id="low-level-control-and-performance">Low-level control and
performance</h3>
<p>Safety is great, but it’s nothing without control. Critical software
such as OS kernels and drivers, or real-time and high-frequency systems,
need unfettered access to the underlying hardware.</p>
<p>This is where Rust really shines, providing:</p>
<ul>
<li><p><strong>Hardware-level access to memory and registers</strong>.
Rust supports you, but it doesn’t get in your way. When every byte and
nanosecond counts, you have full control of the machine.</p></li>
<li><p><strong>Ability to target CPU and GPU-specific features</strong>.
With broad architecture support, conditional compilation, and inline
assembly language, your Rust programs can get the most from modern
hardware.</p></li>
<li><p><strong>Controlled bypass of safety checks</strong>. Rust lets
you temporarily disable the guardrails for only those sections of code
where it’s necessary. A special keyword identifies these “manual
override” sections, reducing the attack surface for potential bugs and
vulnerabilities.</p></li>
<li><p><strong>Fast, predictable performance</strong>. Rust compiles to
native machine code, giving you the speed and efficiency of C++ or
hand-tuned assembler. Rust doesn’t need a garbage collector, so there’s
no memory reclamation overhead and no stop-the-world pauses.</p></li>
<li><p><strong>Interoperability with C/C++</strong>. Rust programs can
talk directly to kernel or SDK code written in C, C++, or any other
language, using Rust’s excellent <em>Foreign Function Interface</em>
(FFI) support. You can call C libraries from Rust, or mix and match C++
components with Rust packages.</p></li>
</ul>
<p>Overall, Rust sits neatly in the “best of both worlds” space between
<em>garbage-collected</em> memory-safe languages like <a href="https://bitfieldconsulting.com/posts/rust-vs-go">Go</a>, and high-performance,
<em>close-to-the-metal</em> languages like C++.</p>
<h2 id="happy-productive-developers">Happy, productive developers</h2>
<p>Okay, Rust sounds good on the technical side, but what’s it like to
use in practice? Well, Rust routinely tops polls of the world’s
<em>most-loved</em> languages, so I think the answer must be “pretty
great”. Here are a few reasons why:</p>
<ul>
<li><p><strong>Super-helpful compiler</strong>. The Rust compiler is not
only smart and efficient, it has amazing error messages: not just what’s
wrong, but <em>why</em> it’s wrong, and what you need to do to fix
it.</p></li>
<li><p><strong>Powerful build system</strong>. The Cargo tool manages
your dependencies, runs your tests, builds your code, generates your
documentation, and publishes your package to Rust’s open-source
repository.</p></li>
<li><p><strong>Smart code advisor</strong>. Clippy, the Rust linter, not
only warns you about problematic code, it even suggests safer, faster,
or more idiomatic alternatives.</p></li>
<li><p><strong>Rich, expressive language</strong>. Rust gives
programmers powerful, state-of-the-art tools for problem-solving:
iterators, threads, closures, pattern-matching, sum types, generics,
async, and traits.</p></li>
<li><p><strong>Peace of mind</strong>. If your Rust program compiles, it
probably works. No more stressing about hidden bugs, or whether your
code will blow up in production.</p></li>
</ul>


  

&nbsp;
  
    
  

&nbsp;
  
    <h2 id="fast-growing-friendly-community">Fast-growing, friendly
community</h2>
<p>Rust adoption is growing rapidly across the entire tech industry,
making Rust a logical pick for businesses today:</p>
<ul>
<li><p><strong>Future-proof investment</strong>. Rust is a smart and
forward-looking choice: there are millions of Rust users and the
community is growing all the time. Rust is stable and mature today, and
it’ll be around for decades to come; that makes it a reliable long-term
bet for your technology stack.</p></li>
<li><p><strong>Gradual migration</strong>. You don’t have to throw away
your existing codebases. Rust has great interoperability with C, C++,
and Java-based languages, as well as Python, meaning that you can adopt
Rust gradually into your products, component by component, service by
service. Rust supports all modern architectures and operating systems,
including WebAssembly for browser-based apps.</p></li>
</ul>
<p>For developers looking to build a secure, rewarding career, Rust’s
advantages make it a safe bet:</p>
<ul>
<li><p><strong>Valuable skill</strong>. Even though demand is growing
fast, there aren’t yet all that many experienced Rust engineers out
there. New jobs are opening up all the time; you’ll have less
competition for those vacancies, a wider choice of employers, and better
compensation and job security wherever you decide to land.</p></li>
<li><p><strong>Covers all the bases</strong>. Rust is versatile and used
across all kinds of industry sectors and applications. You won’t find
yourself siloed by your choice of language; instead, Rust keeps your
options open.</p></li>
</ul>
<p>Happy developers make for a happy community, and the Rust community
has a friendly and welcoming atmosphere. Newbies feel supported, and
there’s a creative diversity of opinions and backgrounds. Governance is
democratic and by consent; Rust is owned by its users, and no big-tech
company can take it over or slap a licence fee on it.</p>
<p>Rust programmers take documentation seriously: the language has great
facilities for writing detailed hypertext docs with live, interactive
examples, Rust itself has all the docs you’ll ever need, and most Rust
crates are incredibly well-documented. After all, Rustaceans know that
it doesn’t matter how great your software is if no one can figure out
how to use it.</p>
<h2 id="meets-you-where-you-are">Meets you where you are</h2>
<p>Rust is attracting developers from all kinds of different backgrounds
and language communities, including complete beginners. Here’s what Rust
looks like from each of these perspectives:</p>
<ul>
<li><p><strong>For beginners, it’s a great first language</strong>. Not
only will Rust teach you most of the important software concepts that
apply to all languages, it’ll also help you build a safe, thoughtful,
and disciplined approach to programming.</p></li>
<li><p><strong>For C/C++ programmers, Rust offers safety plus
interoperability</strong>. You get the high-level abstractions and
low-level control you’re used to, but without the paperwork. Rust takes
over the burden of memory management and correctness checks, and
provides seamless integration with existing C/C++ libraries and
apps.</p></li>
<li><p><strong>For Python users, Rust brings amazing
performance</strong>. There’s no interpreter or VM overhead, so Rust
programs run at the native speed of the hardware. Give your Python
codebase a speed boost by using Rust to implement Python modules, or
build on your existing investment by calling into Python libraries from
your Rust programs.</p></li>
<li><p><strong>For embedded developers, Rust provides low-level control
with zero dependencies</strong>. It’s no surprise, given its strong
focus on correctness, that Rust is now a key component of Linux,
Windows, iOS, macOS, Android, and other operating systems. It’s also
ideal for constrained environments like microcontrollers and embedded
devices: Rust can produce bare-metal binaries needing no runtime, no
operating system, and no SDK.</p></li>
</ul>
<h2 id="learn-once-use-everywhere">Learn once, use everywhere</h2>
<p>Good engineers are T-shaped, as the saying goes: they have a broad
knowledge of different technologies, but they also have deep expertise
with at least one. So, which language should you spend your valuable
time on really mastering?</p>
<p>Rust is:</p>
<ul>
<li><p><strong>versatile</strong> enough to write every kind of
software</p></li>
<li><p><strong>mature</strong> enough to have world-class tools and
libraries available</p></li>
<li><p><strong>modern</strong> enough that your Rust skills will be
valuable for decades to come</p></li>
</ul>
<p>Yet it’s really not so radically different to the languages you
already know. Take your existing software design and problem-solving
skills and bring them to Rust; you’ll find everything works just the way
you’d expect.</p>
<h2 id="from-rust-with-love">From Rust, with love</h2>
<p>Ultimately, software engineering is not just a job, it’s a
<em>craft</em>, where if you bring care and skill to what you do, you’ll
get satisfaction and fulfilment in return.</p>
<p>For me, Rust is more than just a tool. Learning Rust is stimulating,
writing Rust is liberating, and teaching Rust is rewarding. I’ll let the
technical arguments speak for themselves, but in the end perhaps the
most persuasive reason I can offer you for considering Rust is this:</p>
<ul>
<li>There must be something special about a language that so many people
fall in love with. And maybe love doesn’t always need a reason.</li>
</ul>]]></content:encoded><media:content height="655" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1769949888053-JQE3X14Y3B4JN89RH0CJ/worth-your-time.webp?format=1500w" width="800"><media:title type="plain">What's so great about Rust?</media:title></media:content></item><item><title>The secret consultant: your first steps to independence</title><dc:creator>John Arundel</dc:creator><pubDate>Tue, 20 Jan 2026 11:46:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/secret-consultant</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:68ac7cf6b3f7b33f7b361d7d</guid><description><![CDATA[At work, nobody need know you’re a secret consultant. As well as preparing 
you to run your own business someday, it might also make you a better 
employee.]]></description><content:encoded><![CDATA[<p class=""><em>From </em><a href="https://bitfieldconsulting.com/books/master"><em>Master of Your Domain</em></a></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg" data-image-dimensions="1200x900" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=1000w" width="1200" height="900" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/13cd2395-c283-4bfd-868a-e27a9c9e777e/woman-eye.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class="">Photo by <a href="https://unsplash.com/@ernestonorman?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Ernesto Norman</a> on <a href="https://unsplash.com/photos/closeup-photo-of-a-womans-red-eye-R5CX8XDQLV0?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p>You roll over lazily in bed and eye the clock. Somehow or other you
don’t <em>quite</em> fancy going to work. How strange! “Hey, boss,” you
murmur intimately. “Okay with you if I have the day off? I’m not sick, I
just don’t feel like getting out of bed today.” “Sure thing,” your boss
responds. “Sleep in, treat yourself to a leisurely late breakfast, and
spend the day lazing on the sofa, pottering about, or taking a walk,
with my blessing. Do a little light work later if you want to, but if
you’re just not feeling it, use the time for self-care instead.”</p>
<p>Sounds good, doesn’t it? You’re not sleeping with the boss, of
course; that would be most unethical. <em>You’re</em> the boss.</p>
<p>As we saw in <a href="https://bitfieldconsulting.com/posts/self-driving-people">Self-driving
people</a>, many of us dream of one day working for ourselves, but just
aren’t exactly sure how to get started. The trouble with dreams of
independence is that it’s easy for them to remain just that. Without a
clear timetable and plan of action, you’re unlikely to achieve much. As
with most things, the hardest part of building your independent career
is taking the first step.</p>
<p>The best time to start, then, is before you start. Don’t quit your
job yet: there’s some ground to prepare before that time comes. It’s
wise to start planning and working towards independence long before you
take any irrevocable steps:</p>
<ul>
<li><p>You can figure out how to start getting revenue for side hustles,
and project when it’ll enable you to go to work for yourself
full-time</p></li>
<li><p>You can polish up the skills you’ll need to go solo
<em>before</em> you quit, while you’re still drawing that sweet, sweet
salary</p></li>
<li><p>You can play a little game I like to call “the secret
consultant”; it’s a new way to think about your work, by asking how what
you’re doing <em>actually</em> creates value for the person who’s paying
for it. Your employer won’t be mad. On the contrary, they’ll be
delighted.</p></li>
</ul>
<p>Let’s talk about how to start moving the needle towards freedom.</p>
<h3 id="setting-a-deadline">Setting a deadline</h3>
<p>While you shouldn’t try to launch the rocket before you’ve built it,
you <em>do</em> need some kind of time-bounded plan. Vague deadlines
like “in a few years” are too nebulous to spur you into any definite
action. Instead, they keep receding as time goes on, always remaining
just a little way off, like the end of the rainbow.</p>
<p>So I think it’s important to pick some specific date as your
Independence Day: the day you’ll be ready to support yourself entirely
with the revenue from your own business. Once you have a deadline,
things will start to fall into place.</p>
<p>You’ll be able to identify the specific skills and experience that
you need to get between now and then. It’ll give you a head start on the
networking, brand-building, and business knowledge you’ll want to have
in place when the time comes. And you can figure out how much money
you’ll need to be making for independence to become sustainable.</p>
<p>Once you have a reasonable estimate of what your business needs to
bring in just to keep your head above water, start thinking about how
long it might take you to reach that level of <em>reliable</em> monthly
revenue.</p>
<p>Your salary is fairly predictable, but the same won’t be true of your
independent business earnings, especially at the start. It may take you
months to get your first customer. Even once you’re established, you
might make a decent amount one month, but nothing at all the next.</p>
<p>So you can, and should, start working independently long before your
official Independence Day, if at all possible. (One thing to check,
though, is that your contract of employment actually allows you to do
this: some may not.)</p>
<h3 id="sharpening-your-skills">Sharpening your skills</h3>
<p>You need to be pretty good at something to make a business out of
doing that thing. You can’t learn on the job—or rather, you can and will
learn a great deal, but you’ll also need to have decent skills
<em>before</em> you start.</p>
<p>The best way to get them is on your employer’s dime, of course. So
even the most independent-minded people should figure on spending at
least a few years in employment before launching their own solo
career.</p>
<p>And it makes sense to start with something you’re already good at,
something that you can sell, and that most people can’t do for
themselves. Remember, you don’t have to be the world’s expert at
whatever it is. You only need to be <em>able</em> to do it.</p>
<p>Whatever the skill, ask yourself: are you good enough yet that paying
customers should be happy with your work? If not, why not? What aspects
do you need to improve to bring your skill up to commercial
standard?</p>
<h3 id="you-know-more-than-you-think">You know more than you think</h3>
<p>Remember that everyone suffers from impostor syndrome to some extent.
Actually, you know a lot more than you realise. That’s because before
you know how to do something, it seems complicated and impossible, and
after you’ve learned how to do it, it seems trivial and obvious.</p>
<p>A good way to compensate for this cognitive bias is to try teaching
somebody else to do the skill. You’ll be amazed at how much you know
that most people don’t know. This will bolster your self-confidence, and
also help you deepen your own knowledge. When you understand something
well enough to explain it to somebody else, you really understand
it.</p>
<p>Bear in mind, though, that any <em>specific</em> knowledge,
especially about technology, will inevitably go out of date. The more
immediately marketable the skill, the more limited its shelf life. That
means you’ll need to keep learning, even when you’re working
independently.</p>
<p>All good companies invest in research and development, and yours
should be no exception. When you’re not fully booked with paying work,
use your spare time to read, study, and practise new skills. Even when
you <em>are</em> fully booked, make sure you still regularly carve out
time to top up your knowledge tank and keep abreast of new
developments.</p>
<p>The most valuable skills, though, are those that never become
obsolete: problem solving, time management, self-discipline. Patience,
determination, methodical working, keeping good notes and records.
Absorbing new information, listening to people and understanding their
requirements, writing clearly and precisely, explaining complicated
technical issues, communicating with non-technical people; all these
will stand you in good stead throughout your independent career.</p>
<p>Make a list of these and all the other skills you can think of that
would possibly be useful in your future solo business. Grade yourself on
all of them, and identify where you think you need to improve. Start
working on those areas right now.</p>
<h3 id="learn-on-your-employers-dime">Learn on your employer’s dime</h3>
<p>While you’re on salary, you have more leeway for this kind of
learning and experiment. If it takes you slightly longer to do something
because you’re learning new techniques, that’s okay; if you screw it up
and have to start again, it’s not the end of the world. Failure
<em>is</em> an option.</p>
<p>Use every opportunity you have to bolster the skills and experience
you’ve decided you’ll trade on when you go solo. Your employer can
hardly object to you taking the initiative on self-training: it makes
you more valuable to them as well as to yourself.</p>
<p>With that in mind, it’s a good idea to start <em>thinking</em> of
yourself like an independent worker, even before you technically become
one.</p>
<p>Let’s play a game called “the secret consultant”. In this game, you
role-play as a consultant who’s been brought in to your company to solve
some kind of specific problem.</p>
<h3 id="the-secret-consultant">The secret consultant</h3>
<p>When you’re a consultant, even a pretend one, it fundamentally shifts
the way you think about what you’re doing. Ask yourself questions like
“What does the client actually need me to do here? Am I listening and
understanding their requirements properly? What am I doing specifically
that delivers value to the client? Am I communicating that value to the
right people, in the right way?”</p>
<p>When you’re asking these questions, and getting the right answers,
you’re winning the secret consultant game. This will build valuable
experience and confidence, and you may even find that it makes you
better at your job. Your employer may think so, too.</p>
<p>If you feel stressed, anxious, or under pressure in your current
position, being a secret consultant can help. After all, a consultant’s
engagement is time-limited: whether it works out well or not, they’ll
eventually move on to new clients and new problems. It’s easier to
maintain a healthy degree of emotional detachment when you know
that.</p>
<p>One major source of work stress for employees is the knowledge that
mistakes can have long-term consequences: if your boss doesn’t view you
favourably, or you have a difficult relationship, it can seriously
damage your career prospects. As a consultant, though, that’s not the
case. If you screw up an engagement with one client, that’s not ideal,
but don’t worry too much about it: there will always be other
clients.</p>
<p>When you play the secret consultant, it helps you take a more relaxed
attitude to your real job, too. Loosen up a bit, be more experimental,
and get less involved in arguments about how to do things. The stakes
might be high for the company, but they’re not so high for <em>you</em>,
since you won’t be here forever. So chill out, and take a more objective
view of things.</p>
<h3 id="diversifying-your-client-base">Diversifying your client
base</h3>
<p>One other interesting thing you’ll notice as a secret consultant is
that your client base suddenly looks rather over-concentrated. One
hundred percent of your revenue comes from a single company, and if
anything happens to it, your income will drop to zero right away.</p>
<p>No sensible business owner would let themselves get into such a
dangerous position. They wouldn’t want any single client to account for
more than, say, a quarter of their total revenue. So they would
diversify: they’d look for new clients, learn new skills, open up new
areas of business, and spread their risk exposure.</p>
<p>But that’s the one thing you can’t do when you’re a full-time
employee. So, if you’re not actively working towards independence
already, you’re living on borrowed time. I don’t say this to scare you,
but as soon as you start thinking about yourself as a business,
full-time employment suddenly seems rather risky, doesn’t it?</p>
<p>On your independence day, and maybe well before it, start getting
yourself into a safer and more sustainable position by taking on
multiple clients. While the prospect of going it alone may seem scary,
it’s actually less risky than the position you’re in right now. You
never know when you’re going to lose a client; it could be tomorrow.</p>
<h3 id="are-you-ready-to-be-master-of-your-domain">Are you ready to be…
<em>Master of Your Domain</em>?</h3>
<p>So start planning your independent future today, because it might
arrive sooner than you expect. My new book <a href="https://bitfieldconsulting.com/books/master"><em>Master of Your Domain</em></a> is all about how
to do that, and how to build the skills you’ll need when you’re ready to
go to work for yourself.</p>
<p>You’ll learn how to monetize the knowledge and experience you’ve
built up over a lifetime, and how to turn them into a real, functioning
business with its own letterhead and IRS number.</p>
<p>I’ve packed the book full of everything I know about being a
consultant, getting clients, making deals, marketing yourself, making
and selling products, producing great online content, teaching,
mastering your craft, and even writing and publishing your own
books.</p>
<p>If you’re a self-driving person and you’ve been looking for a way to
get more agency and autonomy, as well as a better work/life balance,
then I wrote <em>Master of Your Domain</em> just for you. I hope you’ll
enjoy it, and before too long, you’ll be sleeping with the boss every
night. Sweet dreams!</p>]]></content:encoded><media:content height="753" isDefault="true" medium="image" type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1768913744467-1KJR4AALZUEG7E7WNJF0/woman-eye-square.jpg?format=1500w" width="753"><media:title type="plain">The secret consultant: your first steps to independence</media:title></media:content></item><item><title>The best Rust books for 2026, reviewed</title><dc:creator>John Arundel</dc:creator><pubDate>Tue, 06 Jan 2026 10:36:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/best-rust-books</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:6688212a33b85e6f7dfbacd8</guid><description><![CDATA[There are many Rust books, but these are my favourites—and I think you’ll 
like them too. Here are my reviews of what I think are the truly essential 
Rust books available today.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg" data-image-dimensions="540x540" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=1000w" width="540" height="540" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p><a href="https://bitfieldconsulting.com/posts/why-rust">Rust</a> is a popular, modern, memory-safe
programming language focused on safety, correctness, and expressiveness,
without sacrificing performance.</p>
<p>Here are some of the best books for learning and mastering Rust, as
recommended by the Rust community.</p>
<h2 id="the-rust-programming-language-the-rust-book">The Rust
Programming Language (“The Rust Book”)</h2>

  

&nbsp;










































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp" data-image-dimensions="400x533" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=1000w" width="400" height="533" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/145ad563-10bc-454c-b253-1f95a0f57227/rust-prog-lang.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  


&nbsp;
  
    <p><em>The Rust Programming Language</em> is the consensus
recommendation in the Rust community for the first book every new
programmer should read—it’s colloquially known as “<em>the</em> book”.
It’s the official introduction to Rust, and available for free online,
or in a paper edition from No Starch Press.</p>
<h3 id="the-authors">The authors</h3>
<p>Authors <strong>Steve Klabnik</strong>, <strong>Carol
Nichols</strong>, and <strong>Chris Krycho</strong>, are well-known Rust
experts and contributors. In addition, the book has received many
contributions from the Rust community itself.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers say it’s comprehensive, and explains fundamental concepts
step by step. However, they also note that while <em>The Rust
Programming Language</em> is a good reference work, it’s not necessary
to read the entire book at first. Beginners are recommended to read the
first few chapters to get an understanding of the basics, and save the
more advanced material for later.</p>
<p>The first five or six chapters are enough to give learners a basic
understanding of the language, and it’s recommended at that point that
they try writing some Rust programs themselves, and perhaps move on to a
more application-focused book such as <a href="https://bitfieldconsulting.com/books/rust-tools"><em>The Secrets of Rust: Tools</em></a>.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>It’s more comprehensive than most books, though <em>Programming
Rust</em> goes a little deeper on some topics such as the standard
library. Unlike <em>The Secrets of Rust: Tools</em>, it doesn’t walk
readers through developing realistic Rust projects, or present large
chunks of working code like <em>Zero to Production in Rust</em>.
However, it’s free, and widely regarded as the standard reference
work.</p>
<h2 id="the-secrets-of-rust-tools">The Secrets of Rust: Tools</h2>

  

&nbsp;










  
  
    
      
        



  
    

  
  
    
    <a href="https://bitfieldconsulting.com/books/rust-tools">
      

      
      
      
      
      
      





<img alt="" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg" data-image-dimensions="1000x1500" data-image-focal-point="0.5,0.5" data-load="false" elementtiming="system-product" class="sqs-product-block-main-image" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=1000w" width="1000" height="1500" sizes="auto" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/390add91-3648-43c8-9e72-da4043ec8df5/The+Secrets+of+Rust+Tools.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

    </a>
    <a href="https://bitfieldconsulting.com/books/rust-tools" class="product-status-link">
      


  


    </a>
    
  



        
          <a href="https://bitfieldconsulting.com/books/rust-tools" class="product-title">The Secrets of Rust: Tools</a>

          
  
    
      
        
          $29.95
        
      
    
  


          


          <p data-rte-preserve-empty="true">Master the world’s favourite programming language with my friendly guide to building durable Rust software.</p>

          
            



          
          
          
              
            
            



  
    <button
      class="sqs-add-to-cart-button sqs-suppress-edit-mode sqs-editable-button sqs-button-element--primary
"
      tabindex="0"
    >
      
        <span class="add-to-cart-text">
          
            
              
                Add to cart
              
            
          
        </span>
        <span class="cart-loader"></span>
        <span class="cart-added-text"></span>
      
    </button>
  

          
          





        
      
    
  
  
  

  








&nbsp;
  
    <p><a href="https://bitfieldconsulting.com/books/rust-tools"><em>The Secrets of Rust: Tools</em></a>
is designed as a companion and follow-up to <em>The Rust Programming
Language</em>. It focuses on teaching idiomatic Rust, best-practice
design patterns, and test-driven development. Readers are guided through
building a series of practical, useful tools in Rust, including a memo
manager, a weather client, and a Cargo plugin.</p>
<h3 id="the-author">The author</h3>
<p>Author <strong>John Arundel</strong> is a respected Rust teacher and
writer, with long and deep experience in the software industry and an
impressive backlist of books on programming, including <a href="https://bitfieldconsulting.com/books/deeper">The Deeper Love of Go</a> and <a href="https://bitfieldconsulting.com/books/cloud-native-devops-with-kubernetes">Cloud Native DevOps
with Kubernetes</a>.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers say <em>The Secrets of Rust: Tools</em> stands out from other
Rust books for its beginner-friendly simplicity and focus on clean code.
Others commented that the project-based style helps to show how Rust’s
features come together into real programs. The clear, witty style met
with approval from many, who found it more fun to read than some other
books.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>Unlike other more advanced books such as <em>Rust for Rustaceans</em>
and <em>Zero to Production in Rust</em>, <em>The Secrets of Rust:
Tools</em> assumes no previous knowledge of programming, making it ideal
for someone learning Rust as a first language. It doesn’t attempt to
rehash the material from <em>The Rust Programming Language</em>,
focusing instead on how developers can <em>apply</em> their knowledge to
solving practical problems.</p>
<h2 id="rust-for-rustaceans">Rust for Rustaceans</h2>

  

&nbsp;










































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp" data-image-dimensions="400x529" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=1000w" width="400" height="529" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9d42f184-0af0-47b4-9b2f-4ad642ba86bf/rust-for-rustaceans.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  


&nbsp;
  
    <p><em>Rust for Rustaceans</em> was published in 2021, meaning that it’s
not necessarily up to date with the latest features of Rust, but it’s
still a well-respected book that dives deep into the technicalities of
the language, and gives readers a thorough analytical grounding in
topics like foreign function interfaces, procedural macros, and
object.</p>
<h3 id="the-author">The author</h3>
<p><strong>Jon Gjengset</strong> is a well-known figure in the Rust
community for his educational videos and livestreams, and he’s also a
frequent conference speaker.</p>
<h3 id="readers-say">Readers say</h3>
<p><em>Rust for Rustaceans</em> is often described as a good choice for
intermediate Rust developers who are reasonably confident in their
skills but want to deepen their knowledge. Readers appreciated the
technical focus, but some found it “confusing” and hard to follow,
saying that in their opinion the book was really for experts rather than
intermediate developers.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p><em>Rust for Rustaceans</em> isn’t as beginner-friendly or as clearly
written as something like <em>The Secrets of Rust: Tools</em>, but it’s
deeper and more analytical, covering topics like unsafe code, memory
ordering, and covariant types. Like <em>Effective Rust</em>, it offers
many tips on idiomatic Rust usages, though it doesn’t have such broad
coverage of the standard library as <em>Programming Rust</em>.</p>
<h2 id="the-rust-spellbook">The Rust Spellbook</h2>

  

&nbsp;










  
  
    
      
        



  
    
  
  


  
  
    
    <a href="https://bitfieldconsulting.com/books/rust-spellbook">
      

      
      
      
      
      
      





<img alt="" data-image-focal-point="0.5,0.5" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg" data-image-dimensions="1000x1500" data-load="false" elementtiming="system-product" class="sqs-product-block-main-image" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=1000w" width="1000" height="1500" sizes="auto" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/9e467d9b-1918-498f-b49e-e33a3e942de7/The+Rust+Spellbook.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

    </a>
    <a href="https://bitfieldconsulting.com/books/rust-spellbook" class="product-status-link">
      


  


    </a>
    
  



        
          <a href="https://bitfieldconsulting.com/books/rust-spellbook" class="product-title">The Rust Spellbook (early access)</a>

          
  
    
      
        
          $29.95
        
      
    
  


          


          <p data-rte-preserve-empty="true">Deepen your wizardry with hundreds of tips, tricks, techniques, and tactics for writing better Rust programs. </p>

          
            



          
          
          
              
            
            



  
    <button
      class="sqs-add-to-cart-button sqs-suppress-edit-mode sqs-editable-button sqs-button-element--primary
"
      tabindex="0"
    >
      
        <span class="add-to-cart-text">
          
            
              
                Add to cart
              
            
          
        </span>
        <span class="cart-loader"></span>
        <span class="cart-added-text"></span>
      
    </button>
  

          
          





        
      
    
  
  
  
  

  








&nbsp;
  
    <p><a href="https://bitfieldconsulting.com/books/rust-spellbook"><em>The Rust Spellbook</em></a>,
published in 2026, is a compendium of hundreds of Rust tips, tricks, and
techniques, including useful and little-known features of the language,
the standard library, the Cargo tool, and the Rust compiler. It includes
many obscure, advanced, or recently-stabilised Rust features of interest
to any working programmer.</p>
<p>While many of the tips will be immediately useful, some are deeper,
more specialised, and aimed at building a broad, thorough understanding
of Rust’s power and features.</p>
<h3 id="the-author">The author</h3>
<p><strong>John Arundel</strong>, whose other books include <em>The
Secrets of Rust: Tools</em>, is a Rust teacher and author known for his
clear, accessible writing, beginner-friendly approach, and emphasis on
software engineering fundamentals.</p>
<h3 id="readers-say">Readers say</h3>
<p>Some readers describe <em>The Rust Spellbook</em> as “well written”,
and note that it contains much new information not found in other books
(“How did I not know this?”). Others praised the clear explanations of
technical topics such as performance analysis and memory alignment, and
felt that overall, while aimed at intermediate and advanced developers,
the book was still accessible and useful even to Rust beginners.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>Unlike <em>The Rust Programming Language</em> or <em>Programming
Rust</em>, <em>The Rust Spellbook</em> doesn’t aim to provide an
introduction to the language or even a complete guide. Instead, it’s an
anthology of Rust “spells”: lesser-known features, patterns, or
techniques that improve the quality of life for Rust programmers. It’s
longer, deeper, and more detailed than <em>Effective Rust</em>, though
doesn’t go into as much depth on any one topic as <em>Rust for
Rustaceans</em>. A useful companion to the other books on our list, with
almost no overlap in content.</p>

  

&nbsp;
  
    
  

&nbsp;
  
    <h2 id="effective-rust">Effective Rust</h2>

  

&nbsp;










































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp" data-image-dimensions="400x525" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=1000w" width="400" height="525" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/c9551420-5779-44a7-b87a-067da8c89958/effective-rust.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  


&nbsp;
  
    <p><em>Effective Rust</em> is a short, information-dense book on
idiomatic Rust patterns and APIs. It’s not a beginner book, but targets
more experienced programmers who want to fine-tune their knowledge of
Rust and learn about where the different language features can most
effectively be used.</p>
<h3 id="the-author">The author</h3>
<p>Author <strong>David Drysdale</strong> is a staff software engineer
at Google, and has many years experience in Rust development.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers say that the book is “packed with good advice”, and that it
offers good jumping-off points for further learning. However, some
criticised it as “shallow”, and noted that the book’s use of many
examples of <em>bad</em> code could be confusing.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>It’s not an introduction to the language, like <em>The Rust
Programming Language</em>, or a guide to designing real-world software
like <em>The Secrets of Rust: Tools</em>. Instead, <em>Effective
Rust</em> focuses on the micro-details: specific method and syntax
choices that optimise Rust programs to be idiomatic and efficient.</p>
<h2 id="programming-rust">Programming Rust</h2>

  

&nbsp;










































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp" data-image-dimensions="400x525" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=1000w" width="400" height="525" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/09eb3eb2-9983-423f-8eda-c7de60271838/programming-rust.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  


&nbsp;
  
    <p><em>Programming Rust</em> is a 2021 book which, like <em>Rust for
Rustaceans</em>, is no longer current with modern Rust, but still
contains much useful information. It’s especially notable for its
comprehensive coverage of all Rust’s built-in types, standard library
features, and lots of detailed code examples.</p>
<h3 id="the-authors">The authors</h3>
<p><strong>Jim Blandy</strong> is a software developer known for his
work on the Firefox web browser and the Emacs editor. <strong>Leonora
Tindall</strong> is a Rust engineer working in the healthcare field.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers generally describe <em>Programming Rust</em> as “broad and
deep”, praising its methodical approach to the nuts and bolts of Rust.
Some noted it was not an easy read, but found the writing style
relatively clear. Though there were a few complaints about typos and
small mistakes, the consensus view is that <em>Programming Rust</em> is
a solid work and, at over 700 pages, represents good value for
money.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>According to reviewers, there’s a fair amount of overlap between
<em>Programming Rust</em> and similar books such as <em>The Rust
Programming Language</em> and <em>Rust for Rustaceans</em>. While not
the most up-to-date book, it has some unique material that’s still worth
reading today. Unlike the more introductory works including <em>The
Secrets of Rust: Tools</em>, it’s aimed at experienced programmers,
especially those coming from a C++ background.</p>
<h2 id="rust-in-action">Rust in Action</h2>

  

&nbsp;










































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp" data-image-dimensions="400x501" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=1000w" width="400" height="501" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/7922301d-033d-4fdb-a896-077bbd29e2ce/rust-in-action.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  


&nbsp;
  
    <p><em>Rust in Action</em> aims to teach advanced Rust development
through a number of technically-challenging projects, including CPU
emulation, memory mapping, floating-point encoding, and raw network
packet handling. It’s squarely pitched at those with a computer science
and engineering background, rather than beginners, but would be suitable
for those using Rust in the systems programming domain.</p>
<h3 id="the-author">The author</h3>
<p><strong>Tim McNamara</strong> is a Rust consultant and YouTuber
specialising in natural language processing and data engineering.
<em>Rust in Action</em> is his first book.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers described this book as “good for experienced C++ coders”, but
warned that it’s “not for low-level language beginners”. Some found the
heavily-annotated code samples confusing, but noted that the material
was generally well explained.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>Like many of the other titles on this list, <em>Rust in Action</em>
is from 2021, so it’s not as up to date as more recent books like
<em>The Secrets of Rust: Tools</em>. Unlike <em>The Rust Programming
Language</em> or <em>Programming Rust</em>, it doesn’t attempt to be a
complete guide to the language, instead focusing on important but niche
topics such as systems programming and Windows. It’s strong on the
technical detail of things like networking, but unlike <em>Zero to
Production in Rust</em>, doesn’t cover popular topics such as web
services.</p>
<h2 id="zero-to-production-in-rust">Zero to Production in Rust</h2>

  

&nbsp;










































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp" data-image-dimensions="400x500" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=1000w" width="400" height="500" sizes="(max-width: 640px) 100vw, (max-width: 767px) 33.33333333333333vw, 33.33333333333333vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b46a9fe6-a87c-43c8-8037-506f202ba398/zero-to-prod.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  


&nbsp;
  
    <p><em>Zero to Production in Rust</em> is a deliberately opinionated
guide to backend web development, aiming to present a complete,
realistic application and guide readers through understanding its
intricacies step by step. It’s a little more recent than some books on
this list, dating from 2022, though its chosen web framework
(<code>actix-web</code>) has nowadays been largely displaced by the more
modern and popular <code>axum</code> library.</p>
<h3 id="the-author">The author</h3>
<p><strong>Luca Palmieri</strong> is a Rust developer and co-organiser
of the Rust London User Group.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers praised the book for its “clear, vivid explanations”, and
appreciated its focus on a single web stack, rather than trying to help
users choose between a bewildering variety of frameworks. However, some
criticised the quality of the print book, citing “cheap, curly paper”,
though they described the material itself as “comprehensive and
well-structured”.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p><em>Zero to Production in Rust</em>, unlike most other titles on this
list, focuses on a single topic: web services. It covers that area well,
but it’s neither a beginner-friendly introduction such as <em>The
Secrets of Rust: Tools</em>, nor a deep technical dive like <em>Rust for
Rustaceans</em>. It’s most suited to those who want to get web-based
Rust applications up and running quickly, giving readers a set of
pre-selected options that will work, without going into too much detail
on why those particular choices were made.</p>
<h2 id="the-best-rust-books-compared">The best Rust books compared</h2>
<p>Here’s a quick overview of the Rust books on this list showing their
various features, strengths, and weaknesses</p>
<table>
<colgroup>
<col />
<col />
<col />
<col />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Level</th>
<th>Strengths</th>
<th>Weaknesses</th>
<th>Best for</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitfieldconsulting.com/books/rust-tools"><em>The
Secrets of Rust: Tools</em></a></td>
<td>John Arundel</td>
<td>Beginner</td>
<td>Clear explanations</td>
<td>Less technical depth</td>
<td>Rust beginners and junior developers</td>
</tr>
<tr>
<td><em>The Rust Programming
Language</em></td>
<td>Steve Klabnik et al</td>
<td>Beginner to intermediate</td>
<td>Comprehensive</td>
<td>Steep difficulty curve</td>
<td>Experienced programmers new to Rust</td>
</tr>
<tr>
<td><a href="https://bitfieldconsulting.com/books/rust-spellbook"><em>The
Rust Spellbook</em></a></td>
<td>John Arundel</td>
<td>Intermediate to advanced</td>
<td>Lesser-known material</td>
<td>Rather eclectic</td>
<td>Those looking to deepen their
knowledge</td>
</tr>
<tr>
<td><em>Effective Rust</em></td>
<td>David Drysdale</td>
<td>Intermediate</td>
<td>Actionable tips</td>
<td>Rather brief</td>
<td>Experienced Rust devs</td>
</tr>
<tr>
<td><em>Rust for Rustaceans</em></td>
<td>Jon Gjengset</td>
<td>Advanced</td>
<td>Deep analysis</td>
<td>Can be hard going</td>
<td>Those with CS backgrounds</td>
</tr>
<tr>
<td><em>Programming Rust</em></td>
<td>Jim Blandy et al</td>
<td>Advanced</td>
<td>Breadth</td>
<td>Somewhat unfocused</td>
<td>Senior devs needing reference
material</td>
</tr>
<tr>
<td><em>Rust in Action</em></td>
<td>Tim McNamara</td>
<td>Advanced</td>
<td>Technical depth</td>
<td>Not beginner-friendly</td>
<td>Systems programmers</td>
</tr>
<tr>
<td><em>Zero to Production in Rust</em></td>
<td>Luca Palmieri</td>
<td>Intermediate</td>
<td>Realistic examples</td>
<td>Rather narrow focus</td>
<td>Web developers</td>
</tr>
</tbody>
</table>
<h2 id="top-picks">Top picks</h2>
<p>Taking into account factors like reviews, recommendations, and Rust
community consensus, these are the top three books for Rust developers
today.</p>
<h3 id="best-for-beginners-the-secrets-of-rust-tools">Best for
beginners: The Secrets of Rust: Tools</h3>
<p>For those new to Rust, or to programming in general, <a href="https://bitfieldconsulting.com/books/rust-tools"><em>The Secrets of Rust: Tools</em></a> is
highly praised as a readable, even fun introduction that builds readers’
confidence by guiding them through several practical projects in Rust.
It’s the ideal jumping-off point for a new developer’s Rust journey.</p>
<h3 id="best-for-intermediate-developers-the-rust-spellbook">Best for
intermediate developers: The Rust Spellbook</h3>
<p>John Arundel’s <a href="https://bitfieldconsulting.com/books/rust-spellbook"><em>The Rust
Spellbook</em></a> is packed with hundreds of useful, practical tips for
working Rust programmers, as well as a wealth of technical detail and
deep background that will help intermediate developers build a solid
understanding of the language.</p>
<h3 id="best-for-experienced-developers-rust-for-rustaceans">Best for
experienced developers: Rust for Rustaceans</h3>
<p><em>Rust for Rustaceans</em> can be dense and difficult reading, but
the material repays careful study with a more thorough understanding of
the <em>why</em> of Rust. Rust demands that programmers approach their
code in a fundamentally different way from most other languages, and Jon
Gjengset’s book explains how to think in Rust at a deep level.</p>]]></content:encoded><media:content height="540" isDefault="true" medium="image" type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/6b6c1ae5-44e7-4a06-8616-6bef7ee7ba20/reading.jpeg?format=1500w" width="540"><media:title type="plain">The best Rust books for 2026, reviewed</media:title></media:content></item><item><title>Racing with disaster: data races in Go</title><dc:creator>John Arundel</dc:creator><pubDate>Mon, 05 Jan 2026 11:35:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/racing-with-disaster</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:687e5e61ca76ff34f6dd5f03</guid><description><![CDATA[Writing concurrent programs is easy, but understanding why they don’t work 
is hard. In this post, we’ll talk about data races, why they’re a problem, 
and how they arise in Go programs.]]></description><content:encoded><![CDATA[<p class=""><em>From </em><a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png" data-image-dimensions="1344x700" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=1000w" width="1344" height="700" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/748a12d5-fed5-41dc-b0d9-55c9b18f8ef4/racing-gophers.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <blockquote>
<p>BERNARD: <em>I’m going to have to let you go.</em><br />
MANNY: <em>What? But I sold a lot of books. I got on well with all the
customers.</em><br />
BERNARD: <em>It’s not that kind of operation.</em><br />
—<a href="https://amzn.to/4sXjd74">Black Books</a></p>
</blockquote>
<p>Imagine you’re browsing in a bookstore: one of those delightful pokey
old ones with endless shelves, rooms leading to other rooms, and books
piled high on every available surface. Somewhere in the labyrinth, a
well-fed ginger cat is sleeping precariously on a stack of Agatha
Christies.</p>
<p>You spot something that looks interesting: a copy of Kip Thorne’s
<em>Black Holes and Time Warps</em>. As you bend down to look closer,
though, the title shimmers and changes. It’s now David Deutsch’s <em>The
Fabric of Reality</em>. It’s priced at $10, but as you watch, the label
distorts and the numbers flow before your eyes. It just went up to
$15.</p>
<p>Well, that’s weird! You notice that, where there was only one copy
before, there are now twelve huddled together on the shelf. You look
round to see if anyone else is noticing this odd behaviour, and when you
look back, you find the books are all gone. The one you were holding has
morphed into Douglas Hofstadter’s <em>Gödel, Escher, Bach</em>.</p>
<p>We’d be puzzled indeed if this sort of thing happened even in the
otherworldly surroundings of a vintage bookstore. But it’s the kind of
problem that we run into all the time in concurrent computer systems,
where seemingly stable things can change, fluctuate, or disappear when
you’re not looking at them, and even—disconcertingly—when you are. It’s
called a <em>data race</em>.</p>
<p>In <a href="https://bitfieldconsulting.com/posts/starving">Starving, sleeping, and yielding</a>, we
wrote a simple concurrent program in Go that prints interleaved “hello”
messages from two different goroutines:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">package</span> main</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="op">(</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;fmt&quot;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;time&quot;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> goroutineB<span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> i <span class="op">:=</span> <span class="kw">range</span> <span class="dv">10</span> <span class="op">{</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>        fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Hello from goroutine B!&quot;</span><span class="op">,</span> i<span class="op">)</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>        time<span class="op">.</span>Sleep<span class="op">(</span><span class="dv">10</span> <span class="op">*</span> time<span class="op">.</span>Millisecond<span class="op">)</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> goroutineB<span class="op">()</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> i <span class="op">:=</span> <span class="kw">range</span> <span class="dv">10</span> <span class="op">{</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>        fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Hello from goroutine A!&quot;</span><span class="op">,</span> i<span class="op">)</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>        time<span class="op">.</span>Sleep<span class="op">(</span><span class="dv">10</span> <span class="op">*</span> time<span class="op">.</span>Millisecond<span class="op">)</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/love/blob/main/hello/4/main.go">Listing
<code>hello/4</code></a>)</p>
<p>And we saw that because of the non-deterministic behaviour of the
scheduler (as explored in <a href="https://bitfieldconsulting.com/posts/goroutines">Go, go,
goroutines</a>), we can’t know in principle what the exact output of a
program like this will be. We can predict that we’ll see messages from
both goroutines, but we don’t know in exactly what order they’ll appear:
this uncertainty is characteristic of concurrent programs.</p>
<p>It’s not really a problem with this program, though, because the
goroutines are totally independent: they don’t share any data.
Unfortunately, being able to share data between goroutines is precisely
what makes Go’s concurrency so useful.</p>
<h3 id="a-shared-variable">A shared variable</h3>
<p>For example, let’s modify this program so that each goroutine prints
the value of a shared package-level variable <code>message</code>:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> message <span class="op">=</span> <span class="st">&quot;Hello&quot;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> goroutineB<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="kw">range</span> <span class="dv">10</span> <span class="op">{</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>        fmt<span class="op">.</span>Println<span class="op">(</span>message<span class="op">,</span> <span class="st">&quot;from goroutine B!&quot;</span><span class="op">)</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>        time<span class="op">.</span>Sleep<span class="op">(</span><span class="dv">10</span> <span class="op">*</span> time<span class="op">.</span>Millisecond<span class="op">)</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> goroutineB<span class="op">()</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="kw">range</span> <span class="dv">10</span> <span class="op">{</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>        fmt<span class="op">.</span>Println<span class="op">(</span>message<span class="op">,</span> <span class="st">&quot;from goroutine A!&quot;</span><span class="op">)</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>        time<span class="op">.</span>Sleep<span class="op">(</span><span class="dv">10</span> <span class="op">*</span> time<span class="op">.</span>Millisecond<span class="op">)</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/love/blob/main/hello/5/main.go">Listing
<code>hello/5</code></a>)</p>
<p>Here’s the output:</p>
<pre><code>Hello from goroutine A!
Hello from goroutine B!
Hello from goroutine B!
...
Hello from goroutine A!</code></pre>
<h3 id="racing-goroutines">Racing goroutines</h3>
<p>Does a data race exist in this program? We can decide that by asking
two questions. First, are multiple goroutines accessing the same
variable? Second, is at least one of those accesses a write?</p>
<p>If the answer to both questions is yes, then we have a data race. It
turns out that we’re safe for now, because even though the two
goroutines are sharing <code>message</code>, all their accesses are
reads; no writes.</p>
<p>Let’s live a little dangerously, then. If we have one of our
goroutines <em>modify</em> the <code>message</code> variable, then we
should have a data race, according to the rules. We’ll change goroutine
B as follows:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> goroutineB<span class="op">()</span> <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    message <span class="op">=</span> <span class="st">&quot;Goodbye&quot;</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> <span class="kw">range</span> <span class="dv">10</span> <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>        fmt<span class="op">.</span>Println<span class="op">(</span>message<span class="op">,</span> <span class="st">&quot;from goroutine B!&quot;</span><span class="op">)</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>        time<span class="op">.</span>Sleep<span class="op">(</span><span class="dv">10</span> <span class="op">*</span> time<span class="op">.</span>Millisecond<span class="op">)</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/love/blob/main/hello/6/main.go">Listing
<code>hello/6</code></a>)</p>
<p>So, when goroutine B runs for the first time, it will set
<code>message</code> to the string <code>"Goodbye"</code>. Let’s see
what the output looks like:</p>
<pre><code>Hello from goroutine A!
Goodbye from goroutine B!
Goodbye from goroutine B!
Goodbye from goroutine A!
...
Goodbye from goroutine B!</code></pre>
<p>Fair enough. For a given iteration of goroutine A, then, will it
print “Hello” or “Goodbye”? The answer is that <em>we can’t know in
advance</em>, and that’s the significance of a data race. The output of
goroutine A depends in this case on whether or not goroutine B has ever
executed, and that in turn depends on what goroutine A does.</p>
<h3 id="data-races-in-concurrent-systems">Data races in concurrent
systems</h3>
<p>What are the consequences of this for a more realistic concurrent
program? Suppose that our sleepy bookstore, Happy Fun Books, has two
point-of-sale terminals, both accessing the same book catalog
concurrently. And let’s suppose that two customers are both trying to
buy the last copy of <em>Black Holes and Time Warps</em>.</p>
<p>We haven’t written a program to do this yet, but let’s imagine
there’s a command called <code>buy</code> that, among other things,
checks the stock level of the given book and decreases it by 1. If you
try to <code>buy abc</code> when there are zero copies of book
<code>abc</code> in stock, clearly that can’t succeed, so you get an
error message saying “sorry, out of stock”.</p>
<p>So, if there’s initially one copy of <code>abc</code> in the catalog,
then what happens when customers A and B both concurrently try to buy
it? In theory, one of them must win the race: if customer A is a
microsecond ahead of customer B in placing their order, then they’ll get
the last copy, and the second attempt to <code>buy abc</code> will fail
because there are now zero copies left.</p>
<p>But in practice, computer systems store temporary copies of data in
<em>cache memory</em>. We can imagine that our bookstore terminals might
use a similar kind of cache arrangement, and in that case these multiple
copies of the book data can get out of sync, with alarming
consequences.</p>
<p>In my book <a href="https://bitfieldconsulting.com/books/deeper">The Deeper Love of Go</a>, we’ll
actually write the <code>buy</code> program, and everything else
required to run a bookstore, using a networked database system in Go.
It’s not only an introduction to programming for complete beginners, but
also a primer on concurrency, distributed systems, and locks for those
who missed out on (or snoozed through) the relevant CompSci classes. If
you’re enjoying this extract, check out the book!</p>
<h3 id="double-access-problems">Double access problems</h3>
<p>Here’s what could happen with our duelling customers:</p>
<ol type="1">
<li><p>Customer A runs <code>buy abc</code>, setting the number of
copies of <code>abc</code> in its local cache of the catalog to zero.
But customer B also has a cached version of the catalog that now
disagrees with customer A’s version, because it still says there is one
copy of <code>abc</code>, instead of zero.</p></li>
<li><p>Concurrently, customer B runs <code>buy abc</code>. This succeeds
because, as far as B’s cache is concerned, there’s still one copy of
<code>abc</code> left in the catalog.</p></li>
<li><p>Both customer A and customer B’s caches now agree again.
Specifically, they agree that there should be zero copies of
<code>abc</code> in the catalog, so there’s no conflict, and the system
doesn’t detect any problem.</p></li>
<li><p>But there <em>is</em> a problem! Only one of the two customers
can walk out of the store with the book. Yet they’ve both bought and
paid for it, and the bookstore system failed to prevent the same copy
from being sold twice.</p></li>
</ol>
<p>What can we do about this?</p>
<h2 id="detecting-races">Detecting races</h2>
<p>Let’s write a Go program that demonstrates a simple version of the
duelling-customers problem, and then we’ll see if we can detect and fix
it.</p>
<h3 id="clash-of-the-customers">Clash of the customers</h3>
<p>We’ll represent each customer by a goroutine, and each goroutine will
attempt to “buy” the same book by decrementing a shared
<code>copies</code> variable:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    copies <span class="op">:=</span> <span class="dv">1</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> <span class="kw">func</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>            copies<span class="op">--</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>            fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Customer A got the book&quot;</span><span class="op">)</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">go</span> <span class="kw">func</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>            copies<span class="op">--</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>            fmt<span class="op">.</span>Println<span class="op">(</span><span class="st">&quot;Customer B got the book&quot;</span><span class="op">)</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>    <span class="op">}()</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>    time<span class="op">.</span>Sleep<span class="op">(</span>time<span class="op">.</span>Second<span class="op">)</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/love/blob/main/copies/1/main.go">Listing
<code>copies/1</code></a>)</p>
<p>Here, we’re setting up a variable <code>copies</code>, representing
the number of copies of the book in stock. It starts at 1.</p>
<p>Next, we create two goroutines, representing customers A and B, each
of which does the same thing: it checks to see if <code>copies</code> is
greater than zero, and if so, it “buys” the book by setting
<code>copies</code> to zero and printing a message saying which customer
got it.</p>
<p>In total, then, we have <em>three</em> goroutines now, including the
main goroutine, which does nothing but call <code>time.Sleep</code>, to
give the two “customer” goroutines a chance to run, and to make sure the
program doesn’t end too soon.</p>
<h3 id="demonstrating-the-race">Demonstrating the race</h3>
<p>When this program runs, then, the two customer goroutines should both
get a chance to run, and they will race each other for access to
<code>copies</code>. Sometimes customer A will get there first, and at
other times, customer B: the result should be more or less random.</p>
<p>Let’s see what happens when we run this program ten times in a row,
then:</p>
<pre><code>Customer B got the book
Customer A got the book
Customer A got the book
Customer B got the book
Customer A got the book
Customer B got the book
Customer B got the book
Customer B got the book
Customer A got the book
Customer A got the book</code></pre>
<p>We already knew that a data race is present here in theory, because
there are multiple goroutines accessing the same variable, and at least
one of those accesses is a write. But now we’ve demonstrated the race in
practice: we can’t tell in advance whether customer A or B will get the
book. The outcome of the program is <em>non-deterministic</em>, in other
words.</p>
<p>And we know that in a more realistic simulation of a multi-user
system that includes cacheing, a data race means that we can get
undesirable results such as <em>both</em> customers apparently
purchasing the same copy.</p>
<p>Go has a useful tool to help us here, called the <em>race
detector</em>. If we use the <code>-race</code> flag with the
<code>go</code> command, the Go runtime will use the race detector to
find and report any data races in the program.</p>
<p>We feel it should report one here, so let’s find out:</p>
<p><strong><code>go run -race main.go</code></strong></p>
<pre><code>Customer B got the book
==================
WARNING: DATA RACE
Read at 0x00c000090038 by goroutine 6:
  main.main.func1()
      .../love/copies/1/main.go:11 +0x2c

Previous write at 0x00c000090038 by goroutine 7:
  main.main.func2()
      .../love/copies/1/main.go:18 +0x50

Goroutine 6 (running) created at:
  main.main()
      .../love/copies/1/main.go:10 +0xa0

Goroutine 7 (running) created at:
  main.main()
      .../love/copies/1/main.go:16 +0x104
==================
Found 1 data race(s)
exit status 66</code></pre>
<h3 id="the-race-detector-output">The race detector output</h3>
<p>We know from the definition of a data race that there must be at
least one write to the variable <em>and</em> at least one other
conflicting access, and indeed that’s the case. Here’s where the write
occurs:</p>
<pre><code>Previous write... by goroutine 7:
  main.main.func2()
      .../love/copies/1/main.go:18</code></pre>
<p>That’s referring to this line in the customer B goroutine (“goroutine
7”, according to the arbitrary numbering scheme used by the
runtime):</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>copies<span class="op">--</span></span></code></pre>
<p>Customer B is trying to decrement <code>copies</code>, thus updating
the value of the variable, but this is conflicting with a concurrent
read by the customer A goroutine (“goroutine 6”):</p>
<pre><code>Read... by goroutine 6:
  main.main.func1()
      .../love/copies/1/main.go:11</code></pre>
<p>That refers to this line in the customer A goroutine:</p>
<pre class="sourceCode go"><code class="sourceCode go"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> copies <span class="op">&gt;</span> <span class="dv">0</span> <span class="op">{</span></span></code></pre>
<p>So, customer A is trying to read the variable, while customer B is
concurrently trying to write to it. Go has successfully detected the
data race in this program. It’s a good idea to always use the
<code>-race</code> flag when you test programs with multiple goroutines,
just to make sure you haven’t missed any potential races.</p>
<p>If the race detector is so super-terrific, you might ask, then why
isn’t it just turned on all the time? The answer is that it works by
adding instrumentation to every piece of data in your program to check
if it’s being accessed concurrently. Those checks slow down the program
a lot, so we wouldn’t want them enabled when running the program for
real, but they remain very useful for testing.</p>
<p>Having proved a data race exists, though, how can we fix it? In the
next post, we’ll see how to use Go’s <a href="https://bitfieldconsulting.com/posts/sync-mutex"><code>sync.Mutex</code></a> type to prevent
conflicting reads and writes, eliminating the race condition.</p>]]></content:encoded><media:content height="599" isDefault="true" medium="image" type="image/png" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1752075724146-IRZV0WGXJ055H5E2BWUG/racing-gophers+square.png?format=1500w" width="599"><media:title type="plain">Racing with disaster: data races in Go</media:title></media:content></item><item><title>What are the best Go books in 2026?</title><dc:creator>John Arundel</dc:creator><pubDate>Sun, 04 Jan 2026 17:48:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/best-go-books</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:61d09404c554b6648a363309</guid><description><![CDATA[What are the best Go books this year? Read my (relatively) unbiased 
recommendations for the Go books you should absolutely buy and read right 
now, whether you’re a beginner or expert Gopher.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png" data-image-dimensions="360x360" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=1000w" width="360" height="360" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/ac085080-64a4-44b5-8353-cfdfa2754ada/books.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class=""><strong>What should you read first when you’re learning Go?</strong></p>





















  
  




  
    <p>Go, also known as Golang, is a modern programming language designed
to be easy to learn, quick to compile and run, and with concurrency as a
first-class citizen. Go is widely used in all kinds of software,
including network services, command-line tools, DevOps, and web
applications.</p>
<p>While it’s perfectly possible to get up to speed with Go by simply
following online tutorials and writing a few programs yourself, it can
really help to have a well-written book at hand which will explain the
fundamentals and what makes Go different.</p>
<p>You can’t learn to program from a book, to be sure, any more than you
can learn to fly from reading about birds. The best way to learn is
still by <em>doing</em> the thing (fortunately, writing software is
substantially easier than flying).</p>
<p>A good book on Go will:</p>
<ul>
<li>Support your learning</li>
<li>Help deepen your understanding</li>
<li>Introduce you to concepts and practices that you weren’t aware
of</li>
<li>Suggest projects and directions for your future learning</li>
</ul>
<p>Here are some of the best books to help you learn Go, as recommended
by the Go community.</p>
<h2 id="the-deeper-love-of-go">The Deeper Love of Go</h2>

  

&nbsp;










  
  
    
      
        



  
    

  
  
    
    <a href="https://bitfieldconsulting.com/books/deeper">
      

      
      
      
      
      
      





<img alt="" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp" data-image-dimensions="1000x1500" data-image-focal-point="0.5,0.5" data-load="false" elementtiming="system-product" class="sqs-product-block-main-image" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=1000w" width="1000" height="1500" sizes="auto" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1b4bcdaa-31a5-4ea0-948a-1d190c9ed1f3/The+Deeper+Love+of+Go.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

    </a>
    <a href="https://bitfieldconsulting.com/books/deeper" class="product-status-link">
      


  


    </a>
    
      
  <span role="button" tabindex="0" data-id="67b4890afe4eaf1462cf80bb" class="sqs-product-quick-view-button">
    Quick View
  </span>


    
  



        
          <a href="https://bitfieldconsulting.com/books/deeper" class="product-title">The Deeper Love of Go</a>

          
  
    
      
        
          $29.95
        
      
    
  


          


          <p data-rte-preserve-empty="true">Introduces the Go programming language for complete beginners, as well as those with experience programming in other languages.</p>

          
            



          
          
          
              
            
            



  
    <button
      class="sqs-add-to-cart-button sqs-suppress-edit-mode sqs-editable-button sqs-button-element--primary
"
      tabindex="0"
    >
      
        <span class="add-to-cart-text">
          
            
              
                Add to cart
              
            
          
        </span>
        <span class="cart-loader"></span>
        <span class="cart-added-text"></span>
      
    </button>
  

          
          





        
      
    
  
  
  
  

  








&nbsp;
  
    <p><a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a> is a new
introduction to Go suitable for complete beginners to programming. If
you have some experience with other languages, the book will also
explain what’s unique about Go, and how to write idiomatic code.</p>
<h3 id="the-author">The author</h3>
<p><strong>John Arundel</strong> is a well-known Go trainer and writer
with many years of experience in Go and many decades of experience in
software engineering. He is also the author of several books on Go for
readers from beginner to advanced level. <a href="https://bitfieldconsulting.com/books/deeper"><em>The
Deeper Love of Go</em></a> is his introductory book.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers praised the book for its beginner-friendly qualities, saying
that some other programming books have made them feel stuck early on,
whereas <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a> helps
unlock a lot of computer science fundamentals and programming ideas.
Most felt it was well written, with some saying that Arundel’s writing
was up there with the best O’Reilly authors.</p>
<p>Concepts such as variables, methods, and pointers are introduced
naturally as part of a realistic application managing a small bookstore.
Readers enjoyed the project-based approach and the opportunity to solve
multiple code challenges throughout the book.</p>
<p>Reviewers praised the book for its gentle yet uncompromising approach
to deeper topics such as concurrency and distributed systems. The
consensus view is that it’s a solid introduction to programming in
general and Go in particular.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p><a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a> is the
most beginner-friendly book on our list. It doesn’t assume any previous
knowledge of programming or of Go, and it’s often recommended to those
wanting to learn Go as their first language.</p>
<p>It’s not as comprehensive as something like <a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a> since it doesn’t
intend to cover all the features of the language. However, it does go
into much more detail about the fundamentals it does cover. Like <a href="https://bitfieldconsulting.com/books/tools"><em>The Power of Go: Tools</em></a>, it covers CLI
tools, APIs, and user interface design, and like <a href="https://amzn.to/3H5o8gr"><em>Concurrency in Go</em></a>, it
introduces readers to goroutines and the Go scheduler. Unlike advanced
books such as <a href="https://bitfieldconsulting.com/books/generics"><em>Know Go</em></a>, it doesn’t
cover generics.</p>
<h2 id="learning-go">Learning Go</h2>

  

&nbsp;





  

    
        
          <a href="https://www.amazon.com/dp/1098139291?tag=cribbcorne-20&amp;linkCode=osi&amp;th=1&amp;psc=1" target="new">
            <img elementtiming="system-amazon-block-image" src="https://m.media-amazon.com/images/I/41Mfa2nGftL._SL500_.jpg"/>
          </a>
        
    

    
      
      
      
      
        <a href="https://www.amazon.com/dp/1098139291?tag=cribbcorne-20&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener" class="buy-button" data-animation-role="button"
        >
          <span class="sqs-amazon-button sqs-system-button sqs-editable-button sqs-button-element--primary" value=""
          >
            Buy on Amazon
          </span>
        </a>
      

    

  


&nbsp;
  
    <p><a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a> is a
single-volume textbook on Go that aims to cover every feature of the
language, not necessarily in depth, but giving readers a good overview
of the basics. While this one-page-per-concept approach sometimes
results in rather terse coverage, especially of some advanced features,
<a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a> is a useful
reference for Go developers to have on their bookshelf. First published
in 2021, it doesn’t cover all of Go’s latest features, but is still a
solid introduction. <a href="https://bitfieldconsulting.com/posts/jon-bodner-learning-go">Read our
detailed review of <em>Learning Go</em></a>.</p>
<h3 id="the-author">The author</h3>
<p><strong>Jon Bodner</strong> is a software engineer and Go developer.
<a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a> is his only
book.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers generally praise this book, though noting it can be rather
dry to read, perhaps because it’s so comprehensive. There were some
complaints about confusing examples that referenced concepts which
hadn’t yet been introduced.</p>
<p>Many reviewers recommended the book, though they highlighted a number
of typos.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p><a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a> is
different from the other books on our list because it’s a single-volume
reference guide to the whole language. As such, it doesn’t have space to
go into great detail on any particular topic, but it does present a
useful overview. It’s aimed at people with a programming background, so
it’s not quite as beginner-friendly as <a href="https://bitfieldconsulting.com/books/deeper"><em>The
Deeper Love of Go</em></a>. And unlike <a href="https://amzn.to/3H5o8gr"><em>Concurrency in Go</em></a>, it
doesn’t attempt to do a deep dive into concurrent programming.</p>
<p>While not completely up to date, it’s still a great deal more modern
than older books such as <a href="https://amzn.to/47vcit2"><em>The Go
Programming Language</em></a>. The second edition does mention generics,
but it doesn’t cover them in anything like as much detail as an advanced
book like <a href="https://bitfieldconsulting.com/books/generics"><em>Know Go</em></a>.</p>
<h2 id="the-power-of-go-tools">The Power of Go: Tools</h2>

  

&nbsp;










  
  
    
      
        



  
    

  
  
    
    <a href="https://bitfieldconsulting.com/books/tools">
      

      
      
      
      
      
      





<img alt="" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp" data-image-dimensions="1000x1500" data-image-focal-point="0.5,0.5" data-load="false" elementtiming="system-product" class="sqs-product-block-main-image" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=1000w" width="1000" height="1500" sizes="auto" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2683b858-dbfb-462f-b270-3a60cf5845bb/The+Power+of+Go+Tools.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

    </a>
    <a href="https://bitfieldconsulting.com/books/tools" class="product-status-link">
      


  


    </a>
    
      
  <span role="button" tabindex="0" data-id="6197d15a223b214d2253dab8" class="sqs-product-quick-view-button">
    Quick View
  </span>


    
  



        
          <a href="https://bitfieldconsulting.com/books/tools" class="product-title">The Power of Go: Tools</a>

          
  
    
      
        
          $29.95
        
      
    
  


          


          <p data-rte-preserve-empty="true">Take your next step in Go, with my guide to building powerful, robust tools and packages that solve user problems. </p>

          
            



          
          
          
              
            
            



  
    <button
      class="sqs-add-to-cart-button sqs-suppress-edit-mode sqs-editable-button sqs-button-element--primary
"
      tabindex="0"
    >
      
        <span class="add-to-cart-text">
          
            
              
                Add to cart
              
            
          
        </span>
        <span class="cart-loader"></span>
        <span class="cart-added-text"></span>
      
    </button>
  

          
          





        
      
    
  
  
  
  

  








&nbsp;
  
    <p><a href="https://bitfieldconsulting.com/books/tools"><em>The Power of Go: Tools</em></a> by
<strong>John Arundel</strong> is designed as a follow-up to his
introductory book <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of
Go</em></a>. It focuses, not on the Go language as such, but on how to
apply it to building practical, useful programs that solve user
problems, especially command-line tools. For example, it covers topics
such as flags and arguments, dealing with files, JSON and YAML data,
HTTP clients and servers, and connecting to databases.</p>
<h3 id="readers-say">Readers say</h3>
<p>In general, readers described this book as useful, practical, and
helpful. Some praised its emphasis on testing and real-world examples,
while others enjoyed the simplicity-focused approach, with memorable
phrases such as “obviousness-oriented programming”.</p>
<p>Reviews were mostly positive, describing the book as clear, concise,
often amusing, and generally fun to read.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p><a href="https://bitfieldconsulting.com/books/tools"><em>The Power of Go Tools</em></a> is an
intermediate-level book aimed at those with some Go experience, so it’s
not as beginner-friendly as <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love
of Go</em></a>, which you should read first. It’s also not as
comprehensive as <a href="https://amzn.to/4nVsSbr"><em>Learning
Go</em></a> since it doesn’t aim to cover every aspect of the language,
focusing mainly on command-line tools such as DevOps utilities. John
Arundel’s books are regularly updated with every new release of Go, so
it’s much more current than older books like <a href="https://amzn.to/47vcit2"><em>The Go Programming Language</em></a>
or <a href="https://amzn.to/3H5o8gr"><em>Concurrency in Go</em></a>.</p>

  

&nbsp;
  
    
  

&nbsp;
  
    <h2 id="concurrency-in-go">Concurrency in Go</h2>

  

&nbsp;





  

    
        
          <a href="https://www.amazon.com/dp/1491941197?tag=cribbcorne-20&amp;linkCode=osi&amp;th=1&amp;psc=1" target="new">
            <img elementtiming="system-amazon-block-image" src="https://m.media-amazon.com/images/I/51A95VkLj+L._SL500_.jpg"/>
          </a>
        
    

    
      
      
      
      
        <a href="https://www.amazon.com/dp/1491941197?tag=cribbcorne-20&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener" class="buy-button" data-animation-role="button"
        >
          <span class="sqs-amazon-button sqs-system-button sqs-editable-button sqs-button-element--primary" value=""
          >
            Buy on Amazon
          </span>
        </a>
      

    

  


&nbsp;
  
    <p><a href="https://amzn.to/3H5o8gr"><em>Concurrency in Go</em></a> is a
book for advanced developers focusing on concurrent programming in Go.
Clearly it’s not for beginners, but it is packed with useful information
on patterns and practices for avoiding some of the most common mistakes
in concurrent programming.</p>
<h3 id="the-author">The author</h3>
<p>Katherine Cox-Buday is a computer scientist.</p>
<h3 id="readers-say">Readers say</h3>
<p>Some found the book difficult to read as it requires quite an
advanced background in computer science and distributed systems.
However, for people with that expertise, the book was recommended as a
valuable guide to best practice. Readers praised the book’s examples and
explanations of some difficult concepts, but some remarked that the book
seemed poorly written in places and that there were many editing
errors.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>Unlike the other books on our list, <a href="https://amzn.to/3H5o8gr"><em>Concurrency in Go</em></a> is focused
squarely on one niche topic. It covers that well, but it’s obviously not
intended to be an introduction to Go like <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a>, nor an overview
of the whole language like <a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a>. It’s a few
years old now, but almost everything in the book is still current with
modern Go, and it’s essential reading for anybody working on distributed
systems.</p>
<h2 id="know-go">Know Go</h2>

  

&nbsp;










  
  
    
      
        



  
    

  
  
    
    <a href="https://bitfieldconsulting.com/books/generics">
      

      
      
      
      
      
      





<img alt="" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp" data-image-dimensions="1000x1500" data-image-focal-point="0.5,0.5" data-load="false" elementtiming="system-product" class="sqs-product-block-main-image" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=1000w" width="1000" height="1500" sizes="auto" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/a09ece4c-2552-4a83-9f7b-573f9781bb39/Know+Go.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

    </a>
    <a href="https://bitfieldconsulting.com/books/generics" class="product-status-link">
      


  


    </a>
    
      
  <span role="button" tabindex="0" data-id="61f41b19b91c3b43500ccccd" class="sqs-product-quick-view-button">
    Quick View
  </span>


    
  



        
          <a href="https://bitfieldconsulting.com/books/generics" class="product-title">Know Go</a>

          
  
    
      
        
          $29.95
        
      
    
  


          


          <p data-rte-preserve-empty="true">Interfaces, generics, and iterators are key to modern Go. Learn all about them with this easy-to-read but comprehensive guide.</p>

          
            



          
          
          
              
            
            



  
    <button
      class="sqs-add-to-cart-button sqs-suppress-edit-mode sqs-editable-button sqs-button-element--primary
"
      tabindex="0"
    >
      
        <span class="add-to-cart-text">
          
            
              
                Add to cart
              
            
          
        </span>
        <span class="cart-loader"></span>
        <span class="cart-added-text"></span>
      
    </button>
  

          
          





        
      
    
  
  
  
  

  








&nbsp;
  
    <p><a href="https://bitfieldconsulting.com/books/generics"><em>Know Go</em></a> by <strong>John
Arundel</strong> is a book for intermediate or experienced Go
developers, focused mainly on modern features of Go such as generics and
iterators. It covers the new standard library APIs and shows many
examples of how to use generics in creating flexible and reusable
software packages.</p>
<p>It’s a very comprehensive guide to generics, including:</p>
<ul>
<li>Type parameters</li>
<li>Constraints</li>
<li>Type approximations</li>
<li>Generic functions and methods</li>
<li>Generic container types</li>
</ul>
<p>It also tackles many frequently asked questions, such as:</p>
<ul>
<li>The performance implications of generics</li>
<li>When you should and shouldn’t use generics in your API design</li>
</ul>
<h3 id="readers-say">Readers say</h3>
<p>Like John Arundel’s other books on our list, readers praised the book
as being fun and easy to read with clear explanations even of some
difficult material. Some readers said that they’d gone from being
apprehensive about using generics to being excited about the
possibilities. Readers liked the friendly tone and noted that the book
contained many useful and practical tips.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>Although <a href="https://bitfieldconsulting.com/books/generics"><em>Know Go</em></a> covers mostly
advanced topics, it’s quite accessible even to beginning programmers and
makes a good follow-up to <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of
Go</em></a> and <a href="https://bitfieldconsulting.com/books/tools"><em>The Power of Go
Tools</em></a>. Because it’s specifically about generics, it provides
much better coverage of this subject than a more general reference, such
as <a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a> or <a href="https://amzn.to/47vcit2"><em>The Go Programming
Language</em></a>.</p>
<h2 id="the-go-programming-language">The Go Programming Language</h2>

  

&nbsp;





  

    
        
          <a href="https://www.amazon.com/dp/0134190440?tag=cribbcorne-20&amp;linkCode=osi&amp;th=1&amp;psc=1" target="new">
            <img elementtiming="system-amazon-block-image" src="https://m.media-amazon.com/images/I/41wph+fnY-L._SL500_.jpg"/>
          </a>
        
    

    
      
      
      
      
        <a href="https://www.amazon.com/dp/0134190440?tag=cribbcorne-20&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener" class="buy-button" data-animation-role="button"
        >
          <span class="sqs-amazon-button sqs-system-button sqs-editable-button sqs-button-element--primary" value=""
          >
            Buy on Amazon
          </span>
        </a>
      

    

  


&nbsp;
  
    <p><a href="https://amzn.to/47vcit2"><em>The Go Programming
Language</em></a> was one of the first books written on Go, published in
2015, but it’s still regarded as authoritative. Everything it covers is
still more or less correct, though it’s missing important recent
additions to Go, such as generics and iterators.</p>
<p>It’s aimed at experienced programmers but doesn’t assume any previous
knowledge of Go specifically. It contains many working examples of Go
code and shows how to use various features of the language and standard
library.</p>
<h3 id="the-authors">The authors</h3>
<p><strong>Alan Donovan</strong> is a Google developer and a member of
the Go team. <strong>Brian W. Kernighan</strong> is a computer science
professor at Princeton, well known for his work on UNIX and for his many
books, including <em>The C Programming Language</em>.</p>
<h3 id="readers-say">Readers say</h3>
<p><a href="https://amzn.to/47vcit2"><em>The Go Programming
Language</em></a> attracts much praise from readers who described it as
well-written, thorough, and easy to understand, though some found it
dated.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p>This probably isn’t the first book you should read on Go; <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a> is a better
introduction, especially for beginning programmers. It’s also not as
up-to-date a reference as a book like <a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a>. And it doesn’t
have as in-depth coverage of concurrent programming as a specialist book
like <a href="https://amzn.to/3H5o8gr"><em>Concurrency in
Go</em></a>.</p>
<p>The biggest problem with this book is that it’s ten years old, and
much has changed in Go and software engineering in the meantime.
However, it’s still worth having on the shelf as a reference, and it’s
an acknowledged classic in the field.</p>
<h2 id="lets-go-lets-go-further">Let’s Go / Let’s Go Further</h2>

  

&nbsp;










































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png" data-image-dimensions="500x500" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=1000w" width="500" height="500" sizes="(max-width: 640px) 100vw, (max-width: 767px) 50vw, 50vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/2ee875be-0bb0-4549-b967-48f826026f80/cover-square+smaller.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  


&nbsp;
  
    <p><a href="https://lets-go.alexedwards.net/"><em>Let’s Go</em></a> and
<a href="https://lets-go-further.alexedwards.net/"><em>Let’s Go
Further</em></a> are specifically about writing production web
applications in Go. It’s not a tutorial in the language but it’s aimed
at those writing real-world Go applications in production and dealing
with things such as performance, authentication, and security.</p>
<p>Web applications and frameworks are complex with lots of moving
parts. Let’s Go essentially presents readers with a complete and working
codebase for them to read and use in their own programs.</p>
<p>It’s not aimed at beginners, and you will need a fair amount of
experience with Go programs and related topics such as JSON, HTTP, and
web technologies. <a href="https://bitfieldconsulting.com/posts/alex-edwards-lets-go-further">Read our
detailed review of <em>Let’s Go Further</em></a>.</p>
<h3 id="the-author">The author</h3>
<p><strong>Alex Edwards</strong> is an author and software engineer
living in Austria. He also works as a freelance consultant.</p>
<h3 id="readers-say">Readers say</h3>
<p>Readers liked the practical approach and found the book helpful for
designing production-ready web apps. Many praised its detailed and
thorough approach, though noted it was perhaps a book more for dipping
into the areas that interest you rather than reading from cover to
cover.</p>
<h3 id="compared-to-others">Compared to others</h3>
<p><a href="https://lets-go.alexedwards.net/"><em>Let’s Go</em></a>
makes a good companion to some of the more general books on our list,
such as <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a> and
<a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a>. It’s more
aimed at showing you <em>what</em> to do rather than explaining
<em>why</em> you need to do that. If you’re looking for a more
hand-holding approach, try <a href="https://bitfieldconsulting.com/books/tools"><em>The Power of Go
Tools</em></a>. If you’re looking for more coverage of modern Go, try <a href="https://bitfieldconsulting.com/books/generics"><em>Know Go</em></a>.</p>
<h2 id="the-best-go-books-compared">The best Go books compared</h2>
<p>Here’s a quick overview of the Go books on this list showing their
various features, strengths, and weaknesses:</p>
<table>
<colgroup>
<col />
<col />
<col />
<col />
<col />
<col />
</colgroup>
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Level</th>
<th>Strengths</th>
<th>Weaknesses</th>
<th>Best for</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper
Love of Go</em></a></td>
<td>John Arundel</td>
<td>Beginner to intermediate</td>
<td>Clear explanations</td>
<td>Not all language topics covered</td>
<td>Go beginners and those new to
programming</td>
</tr>
<tr>
<td><a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a></td>
<td>Jon Bodner</td>
<td>Intermediate</td>
<td>Comprehensive</td>
<td>Limited depth</td>
<td>Experienced programmers new to Go</td>
</tr>
<tr>
<td><a href="https://bitfieldconsulting.com/books/tools"><em>The Power of
Go: Tools</em></a></td>
<td>John Arundel</td>
<td>Beginner to intermediate</td>
<td>Practical projects</td>
<td>Doesn’t cover web apps</td>
<td>Those looking to apply Go to real
problems</td>
</tr>
<tr>
<td><a href="https://amzn.to/3H5o8gr"><em>Concurrency in Go</em></a></td>
<td>Katherine Cox-Buday</td>
<td>Advanced</td>
<td>Rigorous, academic approach</td>
<td>Can be hard going</td>
<td>Those with strong CS and programming
backgrounds</td>
</tr>
<tr>
<td><a href="https://bitfieldconsulting.com/books/generics"><em>Know
Go</em></a></td>
<td>John Arundel</td>
<td>Intermediate to advanced</td>
<td>Covers latest features of Go</td>
<td>Narrowly focused on generics</td>
<td>Senior devs and architects</td>
</tr>
<tr>
<td><a href="https://amzn.to/47vcit2"><em>The
Go Programming Language</em></a></td>
<td>Alan Donovan et al</td>
<td>Intermediate</td>
<td>Authoritative</td>
<td>Now rather dated</td>
<td>Those needing a detailed reference
work</td>
</tr>
<tr>
<td><a href="https://lets-go.alexedwards.net/"><em>Let’s Go / Let’s Go
Further</em></a></td>
<td>Alex Edwards</td>
<td>Intermediate to advanced</td>
<td>Best practices for web apps</td>
<td>Light on explanatory detail</td>
<td>Web service developers</td>
</tr>
</tbody>
</table>
<h2 id="top-picks">Top picks</h2>
<p>Here is a quick overview of the books on our list, including our
recommendations for which book you should read first at your level.</p>
<h3 id="best-for-beginners-the-deeper-love-of-go">Best for beginners: <a href="https://bitfieldconsulting.com/books/deeper">The Deeper Love of Go</a></h3>
<p>John Arundel has a well-earned reputation as a clear and accessible
explainer. <a href="https://bitfieldconsulting.com/books/deeper"><em>The Deeper Love of Go</em></a>
assumes no previous experience of programming and is an ideal
introduction to Go for those without a computer science background.</p>
<h3 id="best-one-volume-reference-learning-go">Best one-volume
reference: <a href="https://amzn.to/4nVsSbr">Learning Go</a></h3>
<p>If you are buying a Go book chiefly to serve as a reference to dip
into when you need to know more about some specific topic, <a href="https://amzn.to/4nVsSbr"><em>Learning Go</em></a> is ideal. It
covers the whole language—in breadth if not depth—and makes a good
standard textbook for Go learners.</p>
<h3 id="best-for-devops-and-tooling-the-power-of-go-tools">Best for
DevOps and tooling: <a href="https://bitfieldconsulting.com/books/tools">The Power of Go:
Tools</a></h3>
<p><a href="https://bitfieldconsulting.com/books/tools"><em>The Power of Go: Tools</em></a> is aimed
specifically at those who want to write tools for systems administration
tasks, platform engineering, API clients, file management, data
wrangling, and so on. It’s ideal for those who’ve learned the
fundamentals of Go and now want to know how to put that knowledge into
practice by solving real problems.</p>
<h3 id="best-for-web-services-lets-go-lets-go-further">Best for web
services: <a href="https://lets-go.alexedwards.net/">Let’s Go</a> / <a href="https://lets-go-further.alexedwards.net/">Let’s Go
Further</a></h3>
<p>If you’re writing or working on a web application in Go, Alex
Edwards’s <a href="https://lets-go.alexedwards.net/"><em>Let’s
Go</em></a> books are a great source of practical, working code. For
more in-depth explanations, though, you’ll need to look elsewhere.</p>]]></content:encoded><media:content height="360" isDefault="true" medium="image" type="image/png" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1641059684014-AL14YQXUONRLJW67G0RL/books.png?format=1500w" width="360"><media:title type="plain">What are the best Go books in 2026?</media:title></media:content></item><item><title>That mockingbird won't sing: a mock API server in Rust</title><dc:creator>John Arundel</dc:creator><pubDate>Mon, 22 Dec 2025 12:29:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/mockingbird-wont-sing</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:68443eca69bb8104cfa3abc3</guid><description><![CDATA[Faking it till you make it isn’t always the best strategy in life, but it 
can be a useful one in software engineering. Here’s an easy way to create a 
local HTTP server for testing Rust API clients.]]></description><content:encoded><![CDATA[<p class=""><em>From </em><a href="https://bitfieldconsulting.com/books/rust-tools"><em>The Secrets of Rust: Tools</em></a></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg" data-image-dimensions="4512x2528" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=1000w" width="4512" height="2528" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/3c310186-1cf9-4b7c-b83a-a9a35f1eb4fc/arturo-alvarez-a7QF-vYWQ0A-unsplash.jpg?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class=""><em>Photo by </em><a href="https://unsplash.com/@happyhour4ever?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash"><em>Arturo Alvarez</em></a><em> on </em><a href="https://unsplash.com/photos/white-and-black-bird-on-brown-wooden-surface-a7QF-vYWQ0A?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash"><em>Unsplash</em></a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <blockquote>
<p><em>It’s better to be a fake somebody than a real nobody.</em><br />
—<a href="https://amzn.to/44BK9PC">“The Talented Mr. Ripley”</a></p>
</blockquote>
<p>Faking it till you make it isn’t always the best strategy in life,
but it can be a useful one in software engineering. In <a href="https://bitfieldconsulting.com/posts/hard-rain-json-rust">A hard rain’s a-gonna fall</a>, we
wrote some Rust code that sends requests to the Weatherstack API, and
decodes its responses. So, how happy are we about the correctness of
that code?</p>
<p>We’ve put it all into a magic function named
<code>get_weather</code>, and we call it like this:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> main() <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>()<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> args <span class="op">=</span> <span class="pp">Args::</span>parse()<span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> location <span class="op">=</span> args<span class="op">.</span>location<span class="op">.</span>join(<span class="st">&quot; &quot;</span>)<span class="op">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> weather <span class="op">=</span> get_weather(<span class="op">&amp;</span>location<span class="op">,</span> <span class="op">&amp;</span>args<span class="op">.</span>api_key)<span class="op">?;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="pp">println!</span>(<span class="st">&quot;{weather}&quot;</span>)<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="cn">Ok</span>(())</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/tsr-tools/blob/main/weather_2/src/main.rs">Listing
<code>weather_2</code></a>)</p>
<p>It looks believable, sure, and it does work. But I sense you might
still be feeling a little bit unconvinced about the way we so glibly
finessed testing <code>get_weather</code>.</p>
<p>We said, if you remember, that we couldn’t test <em>that</em>
function because we don’t know what weather conditions the real API will
return: that depends on what the weather <em>is</em> when we run the
test!</p>
<p>So, in <a href="https://bitfieldconsulting.com/posts/elephants-for-breakfast">Elephants for
breakfast</a> we decided to break up <code>get_weather</code> into two
smaller functions, each with its own test. Then we rewrote the
<code>get_weather</code> function to use these two subcomponents.</p>
<p>On reflection, though, can we really be confident that
<code>get_weather</code> works properly? I mean, it <em>looks</em>
correct:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">fn</span> get_weather(</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    location<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    api_key<span class="op">:</span> <span class="op">&amp;</span><span class="dt">str</span><span class="op">,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="dt">Result</span><span class="op">&lt;</span>Weather<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> resp <span class="op">=</span> request(location<span class="op">,</span> api_key)<span class="op">.</span>send()<span class="op">?;</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> weather <span class="op">=</span> deserialize(<span class="op">&amp;</span>resp<span class="op">.</span>text()<span class="op">?</span>)<span class="op">?;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="cn">Ok</span>(weather)</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>(<a href="https://github.com/bitfield/tsr-tools/blob/main/weather_2/src/lib.rs">Listing
<code>weather_2</code></a>)</p>
<p>Also, when we run the real program, it does successfully call
Weatherstack and produce believable results. So there can’t be
<em>too</em> much wrong with <code>get_weather</code>. Can there?</p>
<p>If you’ve read my book <a href="https://bitfieldconsulting.com/books/rust-tools">The Secrets of
Rust: Tools</a>, you’ll have noticed that there’s a strong emphasis on
testing all the way through. We use tests as a thinking tool, a design
tool, and of course to check that our code works properly (and continues
to work, even when we fiddle with it over time).</p>
<p>It’s great to have tests, so doesn’t it feel like a bit of a step
backwards now to be writing a function without one? It does to me. Let’s
dig into that a little.</p>
<h3 id="what-could-be-wrong">What could be wrong?</h3>
<p>Sometimes manual testing and inspection is good enough, but it’s
worth taking a minute or two to think about what might still be missing
here.</p>
<p>When I’m wondering how to test something, I usually ask a question
like “What could be wrong?” What bugs could there be in
<code>get_weather</code> that we wouldn’t detect with the individual
tests for <code>request</code> and <code>deserialize</code>?</p>
<p>Well, one obvious one is that we might have forgotten to call
<code>request</code> or <code>deserialize</code>! Even the world’s
greatest and best-tested functions are no use if you don’t call
them.</p>
<p>Also, we could imagine ways in which we might call them wrongly. For
example, we could accidentally write:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> resp <span class="op">=</span> request(api_key<span class="op">,</span> location)<span class="op">.</span>send()<span class="op">?;</span></span></code></pre>
<p>Rust will absolutely not stop us doing this, because both parameters
are <code>&amp;str</code>, so it’s fine—syntactically—to swap them
around. We’ll end up with the wrong request, and the call to
Weatherstack certainly won’t work, but our existing unit tests won’t
detect this problem at all. After all, <em>they</em> call the functions
correctly, but they can’t validate that <em>other</em> functions do
so.</p>
<p>Similarly, we could call <code>deserialize</code> with the wrong
argument, or forget to return its result, or make any number of other
silly mistakes which nevertheless are bound to happen in a big enough
codebase, in a big enough team, or over a long enough span of time.</p>
<h3 id="why-dont-we-want-to-make-api-calls-in-tests">Why don’t we want
to make API calls in tests?</h3>
<p>So I completely sympathise with your unhappiness about the current
approach to testing <code>get_weather</code>. Can’t we do better?
Ideally, we’d like to test this function the same way we’d test any
other function: by calling it. So what’s the problem with that?</p>
<p>One problem we identified earlier is that we don’t know exactly what
weather conditions the API will return, but that’s not the only thing
stopping us. We also don’t really want to make HTTP calls to external
servers as part of our tests, because that’s awfully slow (relatively
speaking). Okay, one call won’t break the bank, but it’s not an approach
that would work well in large test suites with lots of dependent
services and endpoints.</p>
<p>We want our tests to be fast, but even more importantly, we want them
to be reliable, and the internet just isn’t reliable in general. We
might not be in range of a WiFi or cellular connection, or that
connection might be slow and flaky, or insecure (we’re passing our
secret API key over the wire, after all). Even if everything at our end
is working perfectly, Weatherstack <em>itself</em> might be down, or
slow, or faulty.</p>
<p>And it doesn’t make sense that our tests would fail in that case,
because as long as our <em>code</em> is correct, the tests should always
pass.</p>
<h3 id="a-fake-server-using-httpmock">A fake server using
<code>httpmock</code></h3>
<p>So we want to call <code>get_weather</code>, but we don’t want to
make a request to the real Weatherstack API. Thinking about it, though,
what are we really testing here? Is it critical that
<code>get_weather</code> makes its request to <em>Weatherstack</em>
specifically, or would any HTTP server do?</p>
<p>As long as it returns the kind of JSON data we expect, in fact, any
server would do. What’s to stop us setting up our <em>own</em> local
HTTP server, pointing <code>get_weather</code> at it, and having it
return some canned JSON we prepared earlier—for example, the data we’re
already using in the <code>deserialize</code> test?</p>
<p>There’s nothing at all to stop us doing this, and it sounds like a
good idea. In fact, there’s a crate for exactly this:</p>
<p><strong><code>cargo add --dev httpmock</code></strong></p>
<p>The <code>httpmock</code> crate makes it easy to set up a simple HTTP
server for tests, with a local URL on a random port, and configure it to
respond to various types of requests.</p>
<p>This pattern is sometimes called a test “double” or “fake”, though
it’s not, strictly speaking, a fake. It’s a real <em>server</em>: it’s
just not the Weatherstack server. It emulates, or “mocks” a subset of
the API server’s behaviour so that we can test our code against it.</p>
<p>We’ll also want the <code>http</code> crate, which gives us some
handy status code constants to test against, like
<code>StatusCode::OK</code>:</p>
<p><strong><code>cargo add --dev http</code></strong></p>
<h3 id="kicking-the-tyres">Kicking the tyres</h3>
<p>Let’s first see if we can use this to test a simple GET request, with
no parameters, and respond with a “hello” message:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">httpmock::</span><span class="op">{</span>Method<span class="op">,</span> MockServer<span class="op">};</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">http::</span>StatusCode<span class="op">;</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>test<span class="at">]</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> mock_server_responds_with_hello() <span class="op">{</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> server <span class="op">=</span> <span class="pp">MockServer::</span>start()<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    server<span class="op">.</span>mock(<span class="op">|</span>when<span class="op">,</span> then<span class="op">|</span> <span class="op">{</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>        when<span class="op">.</span>method(<span class="pp">Method::</span>GET)<span class="op">;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>        then<span class="op">.</span>status(<span class="pp">StatusCode::</span>OK)<span class="op">.</span>body(<span class="st">&quot;hello&quot;</span>)<span class="op">;</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>)<span class="op">;</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> resp <span class="op">=</span> <span class="pp">reqwest::blocking::Client::</span>new()</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>get(server<span class="op">.</span>base_url())</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>send()</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>unwrap()<span class="op">;</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>    <span class="pp">assert_eq!</span>(resp<span class="op">.</span>status()<span class="op">,</span> <span class="pp">StatusCode::</span>OK<span class="op">,</span> <span class="st">&quot;wrong status&quot;</span>)<span class="op">;</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>    <span class="pp">assert_eq!</span>(resp<span class="op">.</span>text()<span class="op">.</span>unwrap()<span class="op">,</span> <span class="st">&quot;hello&quot;</span><span class="op">,</span> <span class="st">&quot;wrong message&quot;</span>)<span class="op">;</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre>
<p>To create the test server, we call <code>MockServer::start()</code>.
We now have a working HTTP server listening on a random local port, but
it’s not very useful yet, because it responds to every request with “404
Not Found”.</p>
<h3 id="configuring-routes-on-the-mock-server">Configuring routes on the
mock server</h3>
<p>To fix that, we’ll tell the mock server how to respond to certain
requests:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>server<span class="op">.</span>mock(<span class="op">|</span>when<span class="op">,</span> then<span class="op">|</span> <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    when<span class="op">.</span>method(<span class="pp">Method::</span>GET)<span class="op">;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    then<span class="op">.</span>status(<span class="pp">StatusCode::</span>OK)<span class="op">.</span>body(<span class="st">&quot;hello&quot;</span>)<span class="op">;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>)<span class="op">;</span></span></code></pre>
<p>We’re saying <em>when</em> you receive any GET request, no matter
what the path, <em>then</em> send a response whose status is OK, and
whose body is the string “hello”. Ignore all other requests (or rather,
continue to respond to them with “404 Not Found”).</p>
<p>So, let’s make that request and see what happens:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> resp <span class="op">=</span> <span class="pp">reqwest::blocking::Client::</span>new()</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span>get(server<span class="op">.</span>base_url())</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span>send()</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span>unwrap()<span class="op">;</span></span></code></pre>
<p>How do we know what URL to make the request to? The server itself
tells us, when we call <code>server.base_url()</code>.</p>
<p>Now we have a response that we can check to see if it’s what it
should be:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="pp">assert_eq!</span>(resp<span class="op">.</span>status()<span class="op">,</span> <span class="pp">StatusCode::</span>OK<span class="op">,</span> <span class="st">&quot;wrong status&quot;</span>)<span class="op">;</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="pp">assert_eq!</span>(resp<span class="op">.</span>text()<span class="op">.</span>unwrap()<span class="op">,</span> <span class="st">&quot;hello&quot;</span><span class="op">,</span> <span class="st">&quot;wrong message&quot;</span>)<span class="op">;</span></span></code></pre>
<h3 id="mocking-the-real-api">Mocking the real API</h3>
<p>That was a good confidence builder. We think we should now be able to
extend this to a more complicated mock server that can check its query
parameters, and respond with data from a file instead of a string
literal. Here goes:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>test<span class="at">]</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> get_weather_fn_makes_correct_api_call() <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> server <span class="op">=</span> <span class="pp">MockServer::</span>start()<span class="op">;</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    server<span class="op">.</span>mock(<span class="op">|</span>when<span class="op">,</span> then<span class="op">|</span> <span class="op">{</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>        when<span class="op">.</span>method(<span class="pp">Method::</span>GET)</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>path(<span class="st">&quot;/current&quot;</span>)</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>query_param(<span class="st">&quot;query&quot;</span><span class="op">,</span> <span class="st">&quot;London,UK&quot;</span>)</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>query_param(<span class="st">&quot;access_key&quot;</span><span class="op">,</span> <span class="st">&quot;dummy api key&quot;</span>)<span class="op">;</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>        then<span class="op">.</span>status(<span class="pp">StatusCode::</span>OK)</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>header(<span class="st">&quot;content-type&quot;</span><span class="op">,</span> <span class="st">&quot;application/json&quot;</span>)</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>body_from_file(<span class="st">&quot;tests/data/ws.json&quot;</span>)<span class="op">;</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>)<span class="op">;</span></span></code></pre>
<p>Again, we start the mock server and use its <code>mock</code> method
to tell it what kind of request to expect (“GET /current”) and what
parameters the query should have.</p>
<p>When the server gets this request, it should respond with OK status,
a “Content-Type” header of <code>application/json</code> (meaning that
the body will contain JSON data), and the data itself from our test
file.</p>
<h3 id="injecting-the-base-url">Injecting the base URL</h3>
<p>This looks good, and now we’re ready to call
<code>get_weather</code>. But there’s a problem:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> weather <span class="op">=</span> get_weather(<span class="st">&quot;London,UK&quot;</span><span class="op">,</span> <span class="st">&quot;dummy api key&quot;</span>)<span class="op">.</span>unwrap()<span class="op">;</span></span></code></pre>
<p>Where do we pass in the mock server’s URL? I mean,
<code>get_weather</code> doesn’t take any other parameters, and if we
don’t do something it will just go ahead and call the real Weatherstack
API, which we don’t want. We need some way for the test to
<em>inject</em> this extra information into the client code.</p>
<p>What’s the right way to solve this problem? You might like to have a
think about it. Let’s look at one wrong answer first:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> weather <span class="op">=</span> get_weather(<span class="op">&amp;</span>server<span class="op">.</span>base_url()<span class="op">,</span> <span class="st">&quot;London,UK&quot;</span><span class="op">,</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;dummy api key&quot;</span>)<span class="op">.</span>unwrap()<span class="op">;</span></span></code></pre>
<p>I mean, no? This <code>get_weather</code> function is out of control
with all the paperwork we’re forced to pass to it. An abstraction is
supposed to conceal things, but the machinery is dangerously exposed
here.</p>
<p>We’ve made this function slightly easier to test by adding the URL
parameter, but as a result we’ve actually made it <em>worse</em> for
real users, who now have to write:</p>
<pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> weather <span class="op">=</span> get_weather(<span class="st">&quot;https://api.weatherstack.com/current&quot;</span><span class="op">,</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;London,UK&quot;</span><span class="op">,</span> <span class="st">&quot;real api key&quot;</span>)<span class="op">.</span>unwrap()<span class="op">;</span> <span class="co">// wat?</span></span></code></pre>
<p>Why should <em>users</em> have to tell us the Weatherstack API URL?
Shouldn’t we already know? This just seems weird and unnecessary, and
indeed it is.</p>
<p>I don’t mean to sound ungrateful to <code>httpmock</code>, which is
terrific. It’s very handy for us to be able to create a mock server to
test against. But if we had to make our API worse in order to use it,
that would be a shame.</p>
<p>What we’d <em>like</em> is for our <code>get_weather</code> function
to use the real API by default, so that users don’t have to pass the
Weatherstack URL when calling the function for real. But we’d also like
to be able to tell it the URL of our mock server when we call it in
tests. So how would <em>that</em> work?</p>
<p>As usual, this slightly contrived cliffhanger will be resolved in the
next post. Don’t miss it!</p>]]></content:encoded><media:content height="1500" isDefault="true" medium="image" type="image/jpeg" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1749303039995-XW8U1TWS1WS53SUCUO42/arturo-alvarez-a7QF-vYWQ0A-unsplash+square.jpg?format=1500w" width="1500"><media:title type="plain">That mockingbird won't sing: a mock API server in Rust</media:title></media:content></item><item><title>When doomed stubs attack: blockchain voting and proof of work</title><dc:creator>John Arundel</dc:creator><pubDate>Mon, 08 Dec 2025 11:04:00 +0000</pubDate><link>https://bitfieldconsulting.com/posts/when-doomed-stubs-attack</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:67e7fdec99ff6a3a470b52f3</guid><description><![CDATA[If cryptocurrencies aren’t managed by any central authority, what’s to stop 
anyone just printing themselves some money and spending it on pizza? The 
answer, excitingly, is “math”.]]></description><content:encoded><![CDATA[<p class="">From <a href="https://bitfieldconsulting.com/books/crypto"><em>Explore Go: Cryptography</em></a></p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png" data-image-dimensions="400x366" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=1000w" width="400" height="366" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  



  
    <p>Pizza or ice cream? It’s a question that we as a species have
grappled with since the dawn of time, and I think we’d all agree that
the only sensible answer is “Both!” If you have the means, of
course.</p>
<p>In <a href="https://bitfieldconsulting.com/posts/cryptocurrency">Bobcoin, blockchains, and
cryptocurrency</a> we ran into a related problem with our otherwise
delightful digital currency scheme: what’s to stop Sam the scammer from
spending the same Bobcoin twice, once on pizza, and again on ice
cream?</p>
<h2 id="digests-and-hashing">Digests and hashing</h2>
<p>One way is to use a mathematical function to compute a kind of unique
fingerprint of each transaction. The function takes a block of Bobcoin
data, such as the one containing sketchy Sam’s pizza purchase, and boils
its bits down to a single value. Anyone in the Bobcoin network can use
the same function independently to compute the fingerprint value, called
a <em>digest</em> of the block.</p>
<p>As the name suggests, a digest is usually shorter than the data it
summarises. In fact, a digest usually has some fixed length—for example,
256 bits—regardless of the size of the message it represents.</p>
<p>The problem of taking some arbitrarily long string of bits (a
message), and turning it into some representative fixed-length string is
called <em>hashing</em>, as in “corned-beef hash”, or “making a hash of
things”.</p>
<p>My book <a href="https://bitfieldconsulting.com/books/crypto">Explore Go: Cryptography</a> is all
about such mathematical magic, and in it you’ll learn how to keep your
messages secret, how to protect your digital currency from scammers, and
how to be truly random. We’ll implement lots of cryptographic code in Go
together, including various kinds of hash functions, and see just how
effective they are (or aren’t).</p>
<p>The specific hash function that we use doesn’t matter here, as long
as it satisfies some key properties. The most important of these is
that, while it’s straightforward to compute the hash of a message, it
should be very difficult to reconstruct the original message if you only
have the hash. Hash functions are sometimes called “one-way” functions
for this reason.</p>
<h2 id="the-blockchain">The blockchain</h2>
<p>And that’s what we use to stymie Sam. When each <em>node</em>—each
computer in the Bobcoin network—has received a block’s worth of
transactions, it starts trying to compute the next block. It hashes the
contents of the previous block together with the transaction data, and
the result is the next block, which it then sends out to the
network.</p>
<p>Now we have a way for nodes to verify the order of blocks: by
verifying their hashes. Since the hash of any block depends uniquely (or
very nearly uniquely) on the previous block, a given set of blocks can
only be verified in their true order.</p>
<p>This means we can order all transactions. When Sam spends their
Bobcoin on Pat’s pizza, and then tries to spend it again on Irene’s ice
cream, Irene can detect that double spending and reject the transaction.
Success!</p>
<p>Well, nearly. There’s still a problem: what if two nodes
independently compute and send out different versions of the “next”
block? Won’t the chain then split into two independently-evolving
versions?</p>
<p>To avoid that, a kind of consensus voting takes place. If a node
receives two different candidates for the “current” block, it keeps both
of them until it can decide which is the authoritative block.</p>
<h2 id="doomed-stubs">Doomed stubs</h2>
<p>How can the node decide which block wins this race? Well, sooner or
later it’ll receive <em>another</em> block from the rest of the network.
This will be “chained” to one or other of the two blocks.</p>
<p>In other words, the new block will be verifiable only against the
hash of either Block A or Block B. Whichever it is, that’s what the
network considers to be the authoritative block, and the node can safely
discard the other one.</p>
<p>This doomed “stub” version of the chain might still grow for another
block or two, depending on the speed of propagation, but eventually it
will be discarded. When faced with two competing versions of the chain,
nodes will always opt for the longer one, because it represents the
majority opinion that this is the “correct” chain. More work has gone
into it, so the network votes with its feet—or rather, with its
hashes.</p>
<p>Verifying a given transaction, then, is simply a matter of looking at
the block that contains it. The more valid blocks that follow this one,
the more confident we can be that the transaction has been accepted by
other nodes.</p>
<p>Since hashing takes some non-zero amount of computing power, it’s in
a node’s interest not to waste time, heat, and money by computing hashes
for a doomed stub. Thus, the network rapidly converges on a single
authoritative ordering of Bobcoin transactions.</p>
<p>To repay nodes for their hard work in keeping the blockchain going,
the node that successfully creates the next block earns a small fee,
made up of the leftover fractional bits of Bobcoin from each
transaction. Each node’s version of the next block includes a
transaction in which this fee is paid to their account. Thus, their
reward for winning the race is to have their fee-earning transaction
validated by the rest of the network.</p>
<h2 id="integrity">Integrity</h2>
<p>In a decentralised scheme like this where each node marks its own
homework, isn’t there a danger of fraud?</p>
<p>Surely all a malicious node has to do is compute the next block,
based on a bogus transaction (“Alice pays Mallory one million Bobcoin”,
let’s say), and then keep this stub chain going by computing all future
blocks based on it (plus the subsequent genuine transactions). What’s to
prevent this?</p>
<p>First, transactions are <em>signed</em> by the person who makes them:
only Alice can create a transaction where Alice pays money to someone
else, and anyone can independently verify it.</p>
<p>Second, hashing is hard work. It costs some amount of money to
compute the hash of each block, so the longer this “fake” chain is kept
up, the more it costs Mallory. Eventually it’ll cost them more than they
could steal with the fake transaction.</p>
<p>In effect, Mallory is fighting the combined hashing power of all the
other nodes in the network, which are collaborating to keep the genuine
chain going.</p>
<p>The genuine chain will rapidly become longer than the fake one,
because it simply has more hashing power behind it. Very soon, all nodes
will discard the shorter chain as invalid, and Mallory’s efforts will be
for nothing.</p>
<p>Sure, Mallory could recruit other nodes into their Bobcoin-stealing
gang, but they’ll still be defeated by the honest nodes. Only if Mallory
can commandeer more than half of the total <em>hashrate</em> in the
network can they hope to subvert the blockchain.</p>
<p>Such a <em>51% attack</em> is expensive and infeasible for large
networks, which means that the more nodes join a given blockchain
network, the more secure it is: safety in numbers.</p>
<h2 id="proof-of-work">Proof of work</h2>
<p>A third way in which malicious blocks are discouraged is to make the
hashing process as slow and expensive as possible. Usually, we want
hashing to be fast and efficient, but in this case the <em>point</em> is
that it should take a lot of time and effort.</p>
<p>In the case of the real-life Bitcoin network, for example, the
hashing problem is made harder by requiring <em>miners</em>—nodes that
want to produce blocks—to produce a hash value within a specific
range.</p>
<p>For example, suppose you’re given some transaction data and asked to
hash it so that the 256-bit hash value is less than, say, a million. Of
course you can only do this by mixing in some extra data of your
own—some <em>nonce</em>—to produce the required value. And since you
can’t reverse the SHA-256 algorithm to find the input number you need
(it’s <em>preimage-resistant</em>), the only way you can do it is by
guessing.</p>
<p>It’s like a kind of benign brute-force attack, or a version of the
“guess the number I’m thinking of” game for extremely patient players.
You just keep guessing a different value for the nonce and hashing the
block until, by pure chance, your hash value comes out as some number
less than 1,000,000.</p>
<p>That might take a while: given the size of the 256-bit hashspace,
your odds of producing an acceptable hash are about one in <span class="math inline">10<sup>71</sup></span> for each attempt. It
therefore costs quite a bit of money just to produce each block, making
attempted fraud less attractive. Mining fees are structured so as to
make it more rewarding to mine genuine blocks than fake ones.</p>
<p>In Bitcoin, the required <em>difficulty target</em> is a little
easier than our example: at the time of writing, the chances of
producing an acceptable hash are currently about one in <span class="math inline">10<sup>64</sup></span>. The difficulty target
gradually increases over time, to keep pace with the expected increase
in computing power.</p>
<h2 id="up-in-smoke">Up in smoke</h2>
<p>If I tell you that, despite this astronomical difficulty, a new
Bitcoin block is mined, on average, about every ten minutes, that gives
you some idea of the enormous computing resources devoted to the
network.</p>
<p>Tens of thousands of Bitcoin nodes are all hashing as fast as they
can, five hundred billion billion hashes a second, trying to guess a
nonce that will produce a valid hash, and only one node can win that
race. All the work done by the losing nodes is wasted, and simply goes
up in smoke. Literally, since most of the electricity they use comes
from burning fossil fuels.</p>
<p>This <em>proof of work</em> scheme, then, depends on wasting a
massive amount of precious energy. Bitcoin alone consumes over 120
terawatt-hours of electricity every year, which is more than many
countries.</p>
<p>In effect, Bitcoin adds another energy-hungry country to the world,
and we don’t really need another one of those.</p>
<p>It seems a shame that the most powerful distributed supercomputer in
human history is being wasted on polluting our atmosphere and heating up
the planet by solving math problems that are totally useless to
humanity.</p>
<h2 id="a-stake-in-the-future">A stake in the future</h2>
<p>Proof of work, then, is perhaps the worst idea ever, but fortunately
there are some alternatives in prospect.</p>
<p>One is so-called <em>proof of stake</em>. In this scheme, rather than
the most trusted nodes being the ones that have burnt up the most
energy, they are simply the ones who have the most <em>money</em>. These
nodes, the reasoning goes, have the biggest stake in the network, so
they’re least inclined to undermine it.</p>
<p>That’s true to <em>some</em> extent, but those who find the
democratising nature of cryptocurrency appealing may think that this is
just another way of rigging the system in favour of its richest
participants. And we have that already: it’s called “capitalism”.</p>
<p>Clearly we need a fair, efficient way of preventing fraud and
double-spending that doesn’t literally set fire to the planet, and we
don’t have that at the moment. Maybe someone will have a bright idea
about this soon. Maybe it’ll be you.</p>]]></content:encoded><media:content height="366" isDefault="true" medium="image" type="image/png" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/485fd41f-da17-480c-8a1b-24f90a75960a/treasure.png?format=1500w" width="400"><media:title type="plain">When doomed stubs attack: blockchain voting and proof of work</media:title></media:content></item><item><title>Dips and wiggles: Prometheus, Grafana, and Checkly</title><dc:creator>John Arundel</dc:creator><pubDate>Wed, 26 Nov 2025 10:24:22 +0000</pubDate><link>https://bitfieldconsulting.com/posts/dips-and-wiggles</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:6926d55bb39139679be3e63c</guid><description><![CDATA[Here’s how to connect your Checkly metrics to Prometheus and Grafana and 
turn them into useful dashboards.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png" data-image-dimensions="500x630" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=1000w" width="500" height="630" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">It's a data explosion. Servers, clusters, pods, microservices, response times, resource usage, web traffic, monitoring statuses: modern infrastructure generates a heck of a lot of data, and it all has to go somewhere.</p><h2>Metrics, metrics everywhere</h2><p class="">These days, that somewhere is usually Prometheus, or something very much like it. <a href="https://prometheus.io/"><strong>Prometheus</strong></a>&nbsp;is an open source database for collecting, storing, and querying metrics data, which looks like this:</p><pre><code># HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 1.175552e+08
</code></pre><p class="">The Prometheus server can collect (or 'scrape') data from a range of sources, and you can then use a standard query language (<a href="https://prometheus.io/docs/prometheus/latest/querying/basics/"><strong>PromQL</strong></a>) to ask questions about it, or use it to generate graphs and dashboards using the popular Grafana tool.</p><p class="">If you don't want to run your own Prometheus instance, there are lots of hosted services which can import and export Prometheus data. It's the de facto standard interchange format for metrics data.</p><h2>Checkly, meet Prometheus</h2><p class="">While the Checkly dashboard is great for focusing on your monitoring information and the status of your various checks, it can also be useful to send this data to Prometheus:</p><ul data-rte-list="default"><li><p class="">Perhaps you already have Grafana dashboards for your various services, and want to incorporate your Checkly data into these.</p></li><li><p class="">Maybe you have an alerting system set up for other Prometheus metrics, and you want to use it with Checkly too.</p></li><li><p class="">Maybe you want to do more sophisticated queries and alerts on Checkly data: for example, you might alert only if more than 10% of your checks are in a down state, or if response times rise above your service level objectives in more than two regions simultaneously.</p></li></ul><p class="">It's easy to connect Checkly to your existing Prometheus server, and you don't need any new infrastructure. In this tutorial, we'll learn how to use Checkly's Prometheus integration in just a few simple steps.</p><h2>Checkly metrics</h2><p class="">So what kind of Checkly data can we use in Prometheus? There are currently three metrics available:</p><ul data-rte-list="default"><li><p class=""><code>checkly_check_status</code> shows the up/down status for a given check</p></li><li><p class=""><code>checkly_check_degraded_status</code> indicates if a check is degraded (that is, responding slowly, but not so slowly that it's considered down)</p></li><li><p class=""><code>checkly_check_result</code> gives you the response time figures for each check, per region</p></li></ul><p class="">When you connect your Checkly account to Prometheus, these metrics will automatically be available for all your configured checks.</p><h2>Enabling the Prometheus integration</h2><p class="">Let's get started! Assuming you have a Prometheus server or are using a hosted service which can scrape Prometheus metrics, you'll also need a Checkly account. If you haven't got one yet,&nbsp;<a href="https://app.checklyhq.com/signup"><strong>sign up for a free trial</strong></a>.</p><p class="">You'll also need some monitoring checks configured.&nbsp;<a href="https://checklyhq.com/docs/browser-checks/"><strong>Follow the instructions</strong></a>&nbsp;to create a simple browser check, if you don't already have some set up. Alternatively, use the&nbsp;<a href="https://blog.checklyhq.com/managing-checkly-checks-with-terraform/"><strong>Checkly Terraform provider</strong></a>&nbsp;to create some checks using Terraform code.</p><p class="">Go to the&nbsp;<a href="https://app.checklyhq.com/account/integrations"><strong>integrations</strong></a>&nbsp;page in your account settings, and find the button that says 'Create Prometheus endpoint'.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png" data-image-dimensions="2184x354" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=1000w" width="2184" height="354" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/bac50770-0b7b-40ad-975c-ed3b9f7990cd/prometheus_step1.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">When you click this, Prometheus data will be enabled for your account, and you'll see two important pieces of information: the&nbsp;<em>API endpoint URL</em>, and the&nbsp;<em>bearer token</em>. The URL tells your Prometheus server where to look for data, and the bearer token is the password that gives access to it.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png" data-image-dimensions="2204x434" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=1000w" width="2204" height="434" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/87dad881-6a6a-46c0-bf2f-917c78b73a97/prometheus_step2.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h2>Configuring Prometheus</h2><p class="">You're now ready to configure your Prometheus server to scrape the new endpoint. To do this, add a stanza like the following to your&nbsp;<code>prometheus.yml</code>&nbsp;file:</p><pre><code># prometheus.yml
- job_name: 'checkly'
  scrape_interval: 30s
  metrics_path: '/accounts/993adb-8ac6-3432-9e80-cb43437bf263/prometheus/metrics'
  bearer_token: 'lSAYpOoLtdAa7ajasoNNS234'
  scheme: https
  static_configs:
  - targets: ['api.checklyhq.com']
</code></pre><p class="">Set the&nbsp;<code>metrics_path</code>&nbsp;to the API endpoint URL from your Checkly integration settings (everything from&nbsp;<code>/accounts/...</code>&nbsp;onwards). Set the&nbsp;<code>bearer_token</code>&nbsp;to your own bearer token value.</p><p class="">Apply the new configuration to your Prometheus server and within a minute or so, it should start collecting metrics. To check this, go to the Prometheus web interface and enter this query:</p><pre><code>checkly_check_status</code></pre><p class="">Once some data has been collected, you should see it show up in your Prometheus query browser:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png" data-image-dimensions="1085x494" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=1000w" width="1085" height="494" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/851be5e7-18e1-4196-a9e3-77ee057f89e5/prometheus-data-1.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h2>Graphing your data</h2><p class="">Now that we have the data available in Grafana, what can we do with it?</p><p class="">The first and most obvious thing would be to graph the check status across all your sites. For example, you could add a dashboard panel with a Prometheus query like this:</p><pre><code>checkly_check_status</code></pre><p class="">For this metric, a value of 1 indicates 'Pass' (site is up), and 0 indicates 'Fail' (site is down, or is returning an error, or an assertion is failing). So when all sites are up, as they should be, you'll see something like this:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png" data-image-dimensions="846x434" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=1000w" width="846" height="434" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/0aa666d7-58b2-4a42-9e33-1b34f9a09288/all-sites-1.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">All five of our test sites are at status 1, so there's just one line. If you want to see each site as a separate line, enable the 'Stack' option in the Grafana visualization settings:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png" data-image-dimensions="850x440" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=1000w" width="850" height="440" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/eb5e1cbe-df79-4045-b246-65107bab79c5/all-sites-stack.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">This is a good way to see the current status of all your sites over time. But suppose we want to answer the question "What percentage of my sites are up at any given point in time?" Another way to ask this question is "How reliable is my site serving infrastructure?"</p><h2>Reliability</h2><p class="">We can answer the 'reliability' question by visualizing the&nbsp;<em>average</em>&nbsp;check status as a percentage. For example, if you have five sites, and two of them are down at a given moment, your 'reliability' graph would show 60% at that moment in the time series. Here's the query:</p><pre><code>avg(checkly_check_status) * 100</code></pre><p class="">And here's what it looks like:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png" data-image-dimensions="844x361" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=1000w" width="844" height="361" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/e4616990-854c-4090-b9a7-c5feb553d4d8/reliability.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">You may like to review this 'reliability' graph from time to time with your engineers. Minor dips and wiggles are to be expected, of course, but over the long term you should expect to see this graph steady or improving. A gradual (or worse, rapid) decline in reliability wouldn't be a good sign.</p><p class="">We can also cross-reference the check status data against other metrics. For example, you could visualize reliability against your cloud hosting bill! As you spend more on cloud, you can see what return you're getting in terms of reliability improvements... or not.</p><h2>Graphing latency</h2><p class="">The check status is very useful, but we have more data to play with. The&nbsp;<code>checkly_check_result</code>&nbsp;metric gives us the response time in milliseconds (referred to as&nbsp;<em>latency</em>) for each site, for each region it's checked from:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png" data-image-dimensions="994x724" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=1000w" width="994" height="724" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b985adc9-d1a9-4278-b942-7e324e10d38b/latency-data.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">It might be interesting to graph, for example, how the performance of a single site varies by region. We could use a query like this:</p><pre><code>checkly_check_result{check_name=~"Site 1"}</code></pre><p class="">This will show only results for Site 1, but with a separate line for response data from each region:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png" data-image-dimensions="856x354" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=1000w" width="856" height="354" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/559881f2-41d4-41f5-a672-5d635acfea0c/site-1-response-eu.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">In this data, for example, we can see a spike in latency after 17:00, which affected the&nbsp;<code>eu-west-1</code>&nbsp;and&nbsp;<code>us-west-1</code>&nbsp;regions considerably, while&nbsp;<code>ap-northeast-1</code>&nbsp;fared slightly better. Reviewing data like this over a long time period can help you make the right decisions about where to host sites for best performance, or which geographic areas may have been more or less affected by an outage, for example.</p><p class="">It's also useful to see the average latency across all regions, with a query like:</p><pre><code>avg(checkly_check_result{check_name=~"Site 1"})</code></pre><p class="">This evens out fluctuations which may be due to local network issues, and gives you a sense of what your&nbsp;<em>global</em>&nbsp;performance looks like:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png" data-image-dimensions="857x245" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=1000w" width="857" height="245" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/932141f6-f112-4cd0-ab76-da4f05aa5d86/site-1-all-regions.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <h2>Graphing region performance</h2><p class="">It might be interesting to ask the question "Which region has the best response time for all my sites, and which the worst?" For example, we could write a query like the following:</p><pre><code>sum by (region) (checkly_check_result)</code></pre><p class="">This shows us the total latency for all sites&nbsp;<em>from each region</em>. The result looks like this:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png" data-image-dimensions="857x365" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=1000w" width="857" height="365" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/27379ec4-b7a3-4a0e-8d8a-bcc7f05e7565/all-sites-by-region.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">We can see that, across all sites, the&nbsp;<code>eu-west-1</code>&nbsp;region is clearly the fastest; customers in that region have the best experience of your sites. The&nbsp;<code>us-west-1</code>&nbsp;region is a little slower, and the&nbsp;<code>ap-northeast-1</code>&nbsp;region is slower still. It might not be a bad guess that all the example sites are hosted in the EU, which is in fact the case. The differing response times of the other regions reflect global internet topology: the EU has fast links to the USA, but traffic to Asia takes longer.</p><p class="">Where most of the customers for a particular site are clustered in one region, as sometimes happens, you can use data like this to help you relocate sites to the most appropriate region.</p><p class="">If a particular region suddenly shows a big blip on the graph, that could indicate a network problem affecting that region, and you might be able to take action to fix the issue for customers in that area by deploying a local replica, for example.</p><h2>Combining Checkly with other metrics</h2><p class="">The monitoring data supplied by Checkly is useful in itself, as we've seen, but it can be even more useful when you combine it with other metrics. For example, suppose you suspect that one of your servers is getting overloaded, and you see occasional alerts for excessive CPU usage. How can you tell if this is causing site outages?</p><p class="">Since Grafana can graph more than one PromQL query at once, we might give it the following queries:</p><pre><code>(1 - avg(irate(node_cpu{mode="idle", node=~"web164"}[15m])) by (instance)) * 100
checkly_check_status{check_name=~"Site 1"} * 100</code></pre><p class="">The first query calculates the percentage of CPU utilisation for a specific server over a 15-minute period. For example, if the CPU was pegged at 100% usage over that time, the graph would show 100%.</p><p class="">The second query shows the check status for a specific site, as a percentage (so that it's on the same scale as the first query). When the site is up, the graph will show 100%, and when it's down, 0%.</p><p class="">The resulting graph might look something like this:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png" data-image-dimensions="853x275" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=1000w" width="853" height="275" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/208c90cb-c9c7-4dc4-bf32-9bc64578c43a/cpu-vs-status.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">It's now much easier to see if a particular CPU spike coincides with monitoring failures.</p><h2>Performance against SLA</h2><p class="">It can be hard, when looking at a bunch of graphs, to know what you're really looking for. For example, if you're looking at a latency graph for a particular site, is there a problem or isn't there?</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png" data-image-dimensions="851x232" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=1000w" width="851" height="232" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/187a961d-7818-4350-94c9-eef2dd945cc2/response-all-regions.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">Maybe, maybe not. We can simplify things considerably by setting ourselves a Service Level Agreement (SLA), which for our purposes here simply defines what 'problem' means. If we say the SLA for a given site is 2000ms, we are saying that if the response time is below that, everything's fine, but if it's over that, there's a problem.</p><p class="">This means that we can produce a graph that's much easier to read: whether or not a given site is over SLA. For example:</p><pre><code>checkly_check_result{check_name=~"Site 1"} &gt; bool 2000</code></pre><p class="">This will show a value of 1 on the graph when the site is over SLA, and 0 when it isn't. The results might look like this:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png" data-image-dimensions="853x276" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=1000w" width="853" height="276" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/fb15c0ab-4681-4089-b99f-6cff22540d6f/site-1-over-sla.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">We can see clearly that there have been several out-of-SLA incidents in the last few hours, mostly from the&nbsp;<code>ap-northeast-1</code>&nbsp;region, but after 17:00, all three regions tripped the SLA alarm. Breaking the data down by region like this helps us eliminate 'local' latency problems, and spot incidents where (since all regions are affected) we probably have a server issue.</p><h2>Tripwires</h2><p class="">One other useful thing monitoring can do is supply us 'quick glance' information. Sometimes we just want to get a very high-level overview of what's going on: basically, answering the question "Is everything okay?"</p><p class="">So one final Grafana dashboard you may like to set up is one we call 'tripwires'. A tripwire is a danger signal, like a check failure, or a high-CPU alert. One or two individual signals probably aren't a concern. But if multiple things start tripping your tripwires at once, you may have a full-scale emergency on your hands.</p><p class="">Your tripwires will likely be repeaters of existing graphs or metrics you already have, but clustered together in one place for easy visibility. It's a good idea to use Grafana's 'Singlestat' visualization for this, which shows a metric or query as a single number, with an optional green, yellow, or red colour depending on the value.</p><p class="">For example, we could take the 'check status' graph we looked at earlier, and turn it into a Singlestat tripwire that answers the question "Are any sites down right now?"</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png" data-image-dimensions="270x157" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=1000w" width="270" height="157" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/04c86642-7197-4d58-bd02-c45840f1f0d2/any-sites-down.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">We can start with a query like this:</p><pre><code>avg(checkly_check_status)</code></pre><p class="">We choose the 'Singlestat' visualization type, define the thresholds for yellow and red status as 0.1 and 0.9, and set the colour mode to 'Background':</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png" data-image-dimensions="716x648" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=1000w" width="716" height="648" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/18909c12-4cb1-48af-b916-2c3ee5b3a455/singlestat.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">Just showing the number '1' would work, but isn't very informative, so let's map the different values to some appropriate wording:</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png" data-image-dimensions="714x347" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=1000w" width="714" height="347" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21aa3f5f-202a-4022-a6fe-1fa5bc6ed229/value-mappings.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">Now the range '1 to 1' will show the text 'ALL UP', and the range '0-0.99' will show 'MAN DOWN'.</p><p class="">Here's a small example set of tripwires that could be useful. 'Any sites down' is the most important one, followed by tripwires for each individual site, and then CPU, disk, memory, and network usage, with appropriate colour ranges defined for each one.</p><p class="">When you have multiple servers to monitor, you can have the tripwire show the highest value in each category. For example, if your busiest server is running at 89% CPU, your tripwire would show '89%'.</p>





















  
  














































  

    
  
    

      

      
        <figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png" data-image-dimensions="798x277" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=1000w" width="798" height="277" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/5f6fa4b4-86eb-478a-b51a-139eb4cee0c9/tripwires.png?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
      
        </figure>
      

    
  


  





  <p class="">The exact set of tripwires that you find useful will be specific to your infrastructure, and you will probably be able to refine it over time, based on your experience. But if you start with a few sensible ones, such as check status, and 'site over SLA', for example, you won't go far wrong.</p><h2>Conclusion</h2><p class="">If you're not already using Prometheus and Grafana to collect, query, visualize, and alert on your infrastructure metrics, this is a great time to start. You'll find it gives you some important advantages over more traditional 'up or down' monitoring, and some powerful tools for diagnosis and troubleshooting.</p><p class="">Connecting your existing Checkly monitoring to Prometheus and Grafana is very easy to do, and really worthwhile. Not only can you bring all your infrastructure data into a single portal and graph it, you can set up more complex queries and alerts based on combinations and aggregates of metrics.</p><p class="">If you're not already using Checkly to monitor your sites for status, performance, and user experience, that's something well worth looking into. You can&nbsp;<a href="https://app.checklyhq.com/signup"><strong>try the service for free</strong></a>&nbsp;for 14 days, and that includes the Prometheus/Grafana integration. Enjoy!</p><p class=""><em>This article, written with the invaluable assistance of </em><a href="https://www.instagram.com/lonetraceur/?hl=en"><em>Andy Pearson</em></a><em>, originally appeared on the Checkly blog. Header image by </em><a href="https://ministryoftype.co.uk/words/2008_a_type_odyssey"><em>Aegir Hallmundur</em></a>.</p>]]></content:encoded><media:content height="630" isDefault="true" medium="image" type="image/png" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1645899677228-KRH7FSXYPQJU6ZQWFGAK/type-odyssey.png?format=1500w" width="500"><media:title type="plain">Dips and wiggles: Prometheus, Grafana, and Checkly</media:title></media:content></item><item><title>“Wakeups”: the most important DevOps metric</title><dc:creator>John Arundel</dc:creator><pubDate>Wed, 19 Nov 2025 11:13:25 +0000</pubDate><link>https://bitfieldconsulting.com/posts/the-most-important-devops-metric</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:691da65bf1f60770f13c6e93</guid><description><![CDATA[“How many times were your people woken up by faults in production?” JAX 
DevOps talks to John about metrics, security, testing, and automation.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp" data-image-dimensions="4272x2848" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=1000w" width="4272" height="2848" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/21db8f47-044b-4721-bfcd-da7b89d7f9ed/sleeping-cat.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p class=""><em>Photo by </em><a href="https://unsplash.com/@ejleusink?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Erik-Jan Leusink</em></a><em> on </em><a href="https://unsplash.com/photos/silver-tabby-cat-sleeping-on-white-blanket-QTe-MHNUrz4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Unsplash</em></a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  





  <p class=""><em>An interview with John Arundel for the JAX DevOps blog</em></p><p class=""><strong>Who is leading the DevOps show? Develops or operators?</strong></p><p class="">The point is that they’re the same people, whether they realize it or not. Developers are intimately responsible for how their code performs in production; good developers relish that responsibility because direct feedback (such as being on call) makes better software and better developers. Meanwhile, operators are the people who write the code which provisions the infrastructure, deploys the software, monitors the services, and so on; they’re just as many developers as the developers, but they work on a different codebase. Developers are operating, and operators are developing; that’s DevOps.</p><p class=""><strong>The focus is slowly shifting from “What is DevOps?” Where do we start?”. How do we answer the second question?</strong></p><p class="">Put your developers on call for production.</p><p class=""><strong>How important is it to incorporate security into DevOps (DevSecOps)? What are the benefits? Should it be a priority or an afterthought?</strong></p><p class="">Security should be a mindset. Whenever you’re writing code that someone other than you will use, you have to think about ways they might try to attack and subvert it. Even better, you should try to do this yourself. This also applies to other people’s code that you rely on (for example, web servers). You will never have security because security is a process, not a feature. The minute you stop doing security, security is gone.</p><blockquote><p class="">Put your developers on call for production.</p></blockquote><p class=""><strong>How important is automation in a DevOps context and what are the areas where automation is really needed?</strong></p><p class="">Without automation, there is no DevOps. No programmer would waste her time manually translating code into machine language—it’s tedious, error-prone, repetitive, boring, and a waste of time. Instead, she gets the computer to do it, using a program called a compiler. The same applies to provisioning infrastructure, configuring firewalls, installing dependencies, building containers, checking web services, and so on. It’s just that not everyone realizes that yet.</p><p class=""><strong>Should testing become an essential component of the CI/CD pipeline? Are we underestimating its importance?</strong></p><p class="">Without testing, there is no CI/CD. If every commit results in a deploy, automated testing is the only way to know that you’re not going to break production. But testing has its limits. In modern, cloud-native, distributed systems everything can and will break at any time, and tests can’t catch that, so your code needs to be defensive about everything it does. Everything can fail. If you can handle the failure gracefully, then handle it; if not, crash, and let your infrastructure handle it. If your system is failure-tolerant, then testing is merely a matter of inducing deliberate failures. If the system is not failure-tolerant, no amount of testing will make it so.</p><p class=""><strong>Some companies are still struggling with DevOps metrics. What are the key metrics that matter and how can they enhance DevOps success?</strong></p><p class="">The most important metric that people don’t track is the number of out-of-hours and weekend pages generated by their monitoring system. In other words, how many times were your people woken up by faults in production? If you optimize for this metric, you’ll have a happy team and a highly reliable service.</p><p class=""><strong>How important is it to not skip steps in the DevOps transformation cycle? What are the steps that companies and/or teams usually ignore or underestimate?</strong></p><p class="">Not steps, but people. Some people just fundamentally don’t get it, or they do get it, but they don’t like it and won’t adopt it. There is no way around these people. They got used to doing things the traditional way, and resent the ground shifting under them. The most effective way to stop these people holding up the DevOps transformation is to replace them. Most companies are not willing to do this, and that is a major reason why most companies fail to transition to DevOps successfully.</p><p class=""><strong>Do you think the abundance of DevOps tools has helped or slowed down DevOps adoption?</strong></p><p class="">There are no DevOps tools. Is a gun a crime tool or a law enforcement tool? It depends on who’s holding it. The difference between doing DevOps and not DevOps is in the people, not the tools.</p><p class=""><strong>There’s a huge demand for DevOps professionals. What skills do you need to have in order to tap into the perks that accompany the job description?</strong></p><p class="">DevOps is not about skills either (it’s a lot easier to say what DevOps is not than to say what it is). Because it’s about people working together, one person cannot be a DevOps. *Teams* of people can *do* DevOps, which requires attitudes of mutual respect and collaboration, a willingness to learn and expand your conception of what your job is about, and a pragmatic approach to engineering. When one team of people writes&nbsp;some software, and another team of people runs that software, that’s not DevOps. When one team takes responsibility for the whole lifecycle of their software, from design to production and back again, and their management rewards and incentivizes them accordingly, that’s DevOps.</p><p class=""><strong>Thank you very much!</strong></p>]]></content:encoded><media:content height="1500" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1763551079118-NK1U4D0R8JDHNORGB0LL/sleeping-cat-square.webp?format=1500w" width="1500"><media:title type="plain">“Wakeups”: the most important DevOps metric</media:title></media:content></item><item><title>“Frowning at a screen for 200 hours”: on the writing process</title><dc:creator>John Arundel</dc:creator><pubDate>Fri, 14 Nov 2025 09:58:20 +0000</pubDate><link>https://bitfieldconsulting.com/posts/the-writing-process</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:6916fd42b0ddba610e4112e6</guid><description><![CDATA[John Arundel on his first steps as an author: “If just one person is helped 
and inspired by this book... then I’m not giving refunds to the others.”]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp" data-image-dimensions="1200x900" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=1000w" width="1200" height="900" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/b59f5549-4a15-4dcb-b1fa-491dc342495e/screen.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p data-rte-preserve-empty="true"><em>Photo by </em><a href="https://unsplash.com/@jeffgry?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Oğuzhan Akdoğan</em></a><em> on </em><a href="https://unsplash.com/photos/man-using-computer-inside-room-qYMkkREOHa4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Unsplash</em></a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  





  <p class="">&nbsp;John Arundel is the author of the recently published&nbsp;<a href="https://bitfieldconsulting.com/books/puppet-beginners-guide">Puppet Cookbook</a>,&nbsp;from Packt Publishing, which helps you build reliable, scalable, secure, high-performance systems to fully utilize the power of cloud computing.</p><p class=""><strong>Packt: Your book is published now. How does it feel to be a published author?</strong></p><p class=""><strong>John:</strong>&nbsp;Pretty great! At one time I wrote technical manuals for Psion, the palmtop computer manufacturer. Thanks to a conservative house style, the kind of books I wrote said things like: “To access file operations, select the File menu”. Not exactly a page-turner. I’m very happy now to be able to publish a book which is written more or less exactly the way I want it, on a subject I find very interesting, and with a lot of jokes.</p><p class=""><strong>Packt: What benefits did writing a book bring to your specialist area?</strong></p><p class=""><strong>John:</strong>&nbsp;The funny thing is that despite being a Puppet user almost since the very beginning, I really don’t use many of its features. In fact, most of them have been added since I started using Puppet, and I don’t have a lot of time to experiment with new stuff, so writing the book was a great opportunity to delve into all the Puppet features I didn’t know about. I hope readers will also find out stuff they didn’t know and that it will be useful. If just one person is helped and inspired by this book... then I’m not giving refunds to the others.</p><p class="">It’s done a lot to raise the profile of my consulting business; I was introduced to one potential client as “This is John. He literally wrote the book on Puppet”. I had to modestly point out that in fact, other, probably better books are available.</p><p class=""><strong>Packt: Our authors usually have full-time jobs whilst writing for us. Was this the case for you and how did you approach managing your time?</strong></p><p class=""><strong>John:</strong>&nbsp;As any freelancer knows, the job is more than full-time. I practically had to invent new physics to figure out a way of using my negative free time to write a book.</p><p class="">I blocked out one day a week devoted to writing, and set myself a goal of a number of hours to achieve each month, which I mostly met. Because the book is so code-focused, I not only had to write about each technique I was describing, but also develop complete, working, reusable software in Puppet to implement it, and then test this on a virtual machine.</p><p class="">Quite frequently I’d discover later that I’d been doing something wrong, or a behaviour in Puppet had changed, and I’d have to go back and fix all the code. I’m sure there are still quite a few bugs, which I am going to pretend I’ve deliberately inserted to help the reader learn to debug and fix Puppet code; something they will, after all, spend a great deal of time doing.</p><p class="">In all, what with researching, writing, coding, testing, fixing, editing, and complaining on&nbsp;Twitter, I spent about 200 hours on the book over 8 months.</p><p class=""><strong>Packt: Whilst writing your book, did you find that it overshadowed personal life in any way? How did you deal with this?</strong></p><p class=""><strong>John:</strong>&nbsp;Not really. It could have, if I’d got into serious deadline trouble. Fortunately, I managed to keep up a continuous, manageable level of mild deadline trouble. I don’t think my friends or family noticed, except occasionally I’d say things at dinner like, “Could you pass the Puppet? I mean pepper.”</p><p class=""><strong>Packt: Do you have any advice for other authors who may be interested in writing for Packt, but are still unsure?</strong></p><p class=""><strong>John:</strong>&nbsp;Go for it! But make sure you have two hundred unallocated hours in your schedule. You’d be amazed how much time you can save by not watching TV, going out, putting on clothes, etc.</p><p class="">Really, my advice would be to plan the book carefully - agreeing the outline in advance with your editor helps a lot. Henry Ford said that there are no big problems, just lots of little problems. Breaking down a book into chapters and sections and subsections and tackling them one by one makes it seem less daunting. And managing your time well helps avoid last-minute-essay syndrome.</p><p class=""><strong>Packt: Do you have any tips for other authors, or tricks that you learnt whilst writing, that you'd like to share?</strong></p><p class=""><strong>John:</strong>&nbsp;One good tip is, once you’ve written a chapter, let it lie fallow for a few weeks and then come back to it with a fresh eye. What you thought were immaculately-crafted sentences turn out to be pompous waffle. And what seemed clear and explicit now seems larded with techno-babble.<br>I read somewhere that P.G. Wodehouse would stick each page of manuscript to the wall as he wrote it, somewhere around the skirting board level, and as he obsessively reworked and rewrote and polished the text he would gradually move it higher and higher up the wall until he judged it good enough - somewhere near the ceiling. Well, I’m not saying I’m P.G. Wodehouse — I’ll leave that for others to say — but it’s a useful way to think about the writing process. Rewrite, rewrite, rewrite! “What is written without effort,” Dr Johnson pointed out, “is in general read without pleasure.”</p><p class=""><strong>Packt: How did you find the overall experience of writing your book for Packt?</strong></p><p class=""><strong>John:</strong>&nbsp;It ranged from great fun to grinding tedium and crushing despair. It’s a bit like sculpting a block of marble - at first it’s all hard work and it seems like you’re getting nowhere, and standing in a pile of rubble. Then as you go on, things start to take shape and you’re motivated to go on because you see your imagined shape emerging from the stone. In the final stages it’s very exciting because you’re just removing the last few crumbs which are clinging to the finished sculpture.</p><p class="">What really made it a terrific experience was the help and support I got from people in the Puppet community, on Twitter and IRC, at meet-ups and user groups, and by e-mail. People kept asking about the book and sounding really excited about it, which was a great motivator. Many Puppet users who are much more expert than me gave up their free time to help me fix my code or my text. Many of the ideas and techniques in the book came from blog posts or code that other people have generously made public, and I’ve made a point of citing the sources for things wherever I can, and giving a link for the reader to go and explore the topic in more detail.</p><p class="">Since the book’s been published, I’ve been really touched that so many people have said kind things about it. I never imagined that it would be so widely read or attract so much favourable comment, and it makes me glad now that I put the effort into making it the very best thing I could do.</p><p class=""><strong>Packt: During the writing process, did you come across any issues/ difficulties that affected your writing and how did you overcome these?</strong></p><p class=""><strong>John:</strong>&nbsp;One of the main difficulties is that Puppet is a moving target. We decided early on in the process to pick 2.7 as our reference version, since that was shortly to be released and would still be current when the book was published. Including lots of caveats such as, “but if you’re using Puppet 2.6 it’s this syntax, and with 0.25.4 you can’t use this feature...” would have cluttered up the book and made it unmanageable. Similarly, I had to choose one operating system — Ubuntu. Elaborating all the tiny changes you need for Debian, CentOS, Red Hat Linux, and so on would have made the book a massive doorstop. It’s my conviction that people who buy this book are smart enough to figure out those differences for themselves.</p><p class="">However, I’m sure that there are still several things in the book which don’t work as they should with the current version of Puppet, and as Puppet develops, it will break more and more of the examples - but if I’m really good all year, perhaps I’ll be allowed a second edition.</p><p class=""><strong>Packt: Was there anything interesting that happened during the writing of the book?</strong></p><p class=""><strong>John:</strong>&nbsp;Only in the sense that the Chinese use, when they curse you to live in interesting times. Quite often I wrote myself to a standstill and just stared blankly at the laptop, hoping it would explain something complicated for me, or think up a useful and instructive example when I couldn’t. On one occasion I decided that the best thing to do with a certain long, difficult, and laboriously-constructed section was to delete it altogether, improving the book immeasurably as a result. The deleted scenes will be available on a forthcoming DVD, together with a ‘making of’ documentary which consists of me frowning at a screen for 200 hours and intermittently making tea.</p><p class=""><strong>Packt: How did Packt’s Acquisition Editors help you - what kind of things did they help you with and how did they support you throughout the writing process?</strong></p><p class=""><strong>John:</strong>&nbsp;The biggest help at the start was giving me structure by insisting on an outline, and then setting individual chapter deadlines to plan the writing time - then gently but persistently enforcing them.<br>What was also very useful was to see sample chapters of other books, to get an idea of where I was supposed to be going, and getting very detailed feedback on the early chapters about exactly how to lay things out and how to make everything consistent.</p><p class="">Beyond that, I was pleased and surprised by how little the editors interfered with what I was doing. By and large I was allowed to write my own book the way I wanted. No one suggested I write sentences like “To access file operations, select the File menu.” When I asked for help, I got it, and when I didn’t, I was left in peace and trusted to do the right thing. That’s a great way to write.</p><p class=""><strong>Packt: What projects, if any, are you working on at the moment?</strong></p><p class=""><strong>John:</strong>&nbsp;Several people have asked what the next book’s going to be. I have said, only half-jokingly, that I might do one on Chef. I have a kind of a semi-formed idea about a book of system administration patterns and practices, based on my several decades worth of experience (read: mistakes). But just now I’m enjoying a break from writing, and I’m spending my negative free time reading other people’s books, playing Beethoven on my toy piano like Schroeder out of Peanuts, and learning to bake the perfect Cornish pasty. Ah! Excuse me, that was the oven timer.</p><p class=""><em>Editor’s note: John went on to write </em><a href="https://bitfieldconsulting.com/books"><em>quite a few other books</em></a><em>. None of them on Chef.</em></p>]]></content:encoded><media:content height="900" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1763114543439-5S2SHBU59QWDJ0BSRHF6/screen-square.webp?format=1500w" width="900"><media:title type="plain">“Frowning at a screen for 200 hours”: on the writing process</media:title></media:content></item><item><title>Build bridges, not walls</title><dc:creator>John Arundel</dc:creator><pubDate>Thu, 13 Nov 2025 17:14:57 +0000</pubDate><link>https://bitfieldconsulting.com/posts/bridges-not-walls</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:69161217ab61372237b1add7</guid><description><![CDATA[We live in a world of walls, unfortunately, and some people would like to 
build even more of them. Whatever you think about that, the walls between 
software developers and IT operations staff don’t do anybody any favours.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp" data-image-dimensions="1200x900" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=1000w" width="1200" height="900" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/01143be9-dee1-4928-b116-29660eba9a3f/bridge.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p data-rte-preserve-empty="true"><em>Photo by </em><a href="https://unsplash.com/@lphik?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Tatiana P</em></a><em> on </em><a href="https://unsplash.com/photos/a-large-bridge-spanning-over-a-large-body-of-water-UfFSO6JOlKE?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Unsplash</em></a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  



  
    <p>We live in a world of walls, unfortunately, and some people would
like to build even more of them. Whatever you think about that, the
walls between software developers and IT operations staff don’t do
anybody any favours.</p>
<h2 id="looking-over-the-wall">Looking over the wall</h2>
<p>If you’re a developer, have you ever wondered why ops seem so
antagonistic? Here’s why: they’re fed up with your buggy software that
doesn’t work in production, and your apparent lack of interest in fixing
it.</p>
<p>If you’re an ops person, how do you think you’re seen by developers?
The answer is, they think you’re grumpy, unhelpful, resistant to trying
new things, and unresponsive to requests for changes. Oh, and you’re a
real buzzkill about security.</p>
<p>I’m going to tell you something now which will shock you rigid.</p>
<p>The fact is, those folk in the other team aren’t idiots, and they
don’t hate you. They’re smart, motivated, and professional, and they’re
focused on doing their jobs. But you’re not making it any easier for
them. Here are some ideas on how to change that.</p>
<blockquote>
<p><em>People who make music together cannot be enemies… at least while
the music lasts.</em><br />
—Paul Hindemith</p>
</blockquote>
<h2 id="getting-together">Getting together</h2>
<p>First, <strong>empathise</strong>. Understand a bit more about what
your colleagues in the other team do, what they care about, and why it
matters to them.</p>
<p>Second, <strong>collaborate</strong>. When you work closely with
someone, you get a great insight into what it’s like to do their
job.</p>
<p>Software developers, get more involved in how your stuff is deployed
and run in production. “Throwing code over the wall” won’t fly any more.
Your ops friends will help you get a development environment that
mirrors production. You can use Vagrant boxes or cloud instances built
by the same automation that builds production. No more “it works on my
laptop”; when something breaks in production, you will have an identical
environment to troubleshoot it.</p>
<p>Ops professionals, you already write and maintain software that runs
your infrastructure, so make sure you’re using the same workflows and
tools as your friends in development. Get them to do code reviews for
you. Their entire working lives are focused on good software engineering
practice; there’s a lot you can learn from them.</p>
<p>When there’s a new application or service to be deployed, involve the
developers from day one. The software that configures the servers,
installs the dependencies, and manages deployments, is as much part of
the application as the source code itself.</p>
<p>Do <strong>pairing</strong>. The best way to collaborate with someone
is to pair program with them (or pair sysadmin, depending on the task).
That means you’re both sitting at the same screen and keyboard, talking
about what you’re doing and working by consensus.</p>
<p>You might be coding, troubleshooting a problem, or anything else
that’s part of your normal work: you’re just doing it together. If a
disagreement comes up about what to do, talk it out or take it to a
whiteboard. Involve other people if you think they can help. If you
don’t have the information you need to solve something, find the person
who does, and pair with them to solve it.</p>
<blockquote>
<p><em>I am a man of fixed and unbending principles, the first of which
is to be flexible at all times.</em><br />
—Everett Dirksen</p>
</blockquote>
<h2 id="re-thinking-your-work">Re-thinking your work</h2>
<p>New or experimental projects often need a lot of flexibility. If IT
can’t offer this to developers, they’ll have to go around IT to get the
job done, and that doesn’t spell collaboration.</p>
<p>If developers need your help to get virtual machines running, make it
so; if they need the ability to spin up cloud instances to test things,
make sure they have it. Re-think your <strong>priorities</strong> as an
operations engineer. It might seem like answering questions, helping
people, and working with developers is taking time away from your real
work. Guess again. That is your real work!</p>
<p>Developers, you might think your job ends with a
<code>git push</code>. But software that doesn’t work in the real world
is a waste of bits. You need to understand where your code runs in
production, how it gets there, how the servers are built, how the cloud
provisioning works, what happens when your stuff breaks, and how to fix
it.</p>
<p>You might think <strong>learning</strong> about Linux command lines,
TCP/IP, and network latency is a waste of your time. Actually, it’s
making you a better developer. If you think it’s not your job to know
this stuff, you misunderstand what your job is.</p>
<p>The truth is there was never a neat line between dev and ops. The
overlap is precisely where things get interesting. Lots of important
work simply can’t be done without having a foot in both worlds, and the
way to do that is for dev and ops to share their particular
<em>fu</em>.</p>
<p>If deploys are fragile and often result in unplanned downtime, work
on that together. Building a safe, reliable, easy-to-use deployment
system is right in the centre of the Venn diagram between dev and ops.
If you get that right, much else will follow.</p>
<p>If releases pass tests, but fail intermittently in production due to
weird edge cases, you’ll need to work together to debug that. If
performance is a problem, it takes dev and ops collaboration to fix
it.</p>
<blockquote>
<p><em>Show me a completely smooth operation and I’ll show you someone
who’s covering mistakes. Real boats rock.</em><br />
—Frank Herbert, ‘Chapterhouse: Dune’</p>
</blockquote>
<h2 id="closing-the-loop">Closing the loop</h2>
<p>Finally, <strong>monitoring</strong> connects it all together.
Monitoring tells ops that the services are up, and it tells devs how the
software is performing.</p>
<p>Good automated monitoring checks don’t just test that a webserver is
responding: they match text strings that prove it’s working; they fetch
multiple URLs that exercise different parts of the system; they do
queries which verify the whole stack.</p>
<p>If the system uses login sessions, the monitoring checks log in and
behave like users: searching, filling forms, uploading content. The
developers know what needs to be tested, and the ops team know how to
write checks that test it. Good monitoring demands empathy and
collaboration from dev and ops, and it closes the loop between those who
write the software and those who run the software.</p>
<p>“But we already write unit tests!” Great, but monitoring is
different. Unit tests demonstrate that your code works in theory.
Monitoring tells you whether it’s working in <strong>practice</strong>.
Unit tests only show what happens in the failure modes you can think of;
the real world will throw you failure modes you won’t believe. Tests are
essential, monitoring is essential, but they’re not the same.</p>
<p>When monitoring detects that a service is down, that alert needs to
go to the person who wrote the service. This is one area where
developers can be surprisingly resistant to change. Some people have got
used to the idea that their <strong>responsibility</strong> ends once
the code ships. But that’s not the case.</p>
<p>If software is breaking in production, fixing it needs to take
priority over new features, and that means developers need to get that
information directly: ops don’t want to spend their time nagging devs
about bugs, and it creates a tension between the teams which is
unnecessary and unhelpful.</p>
<p>A good way to get developers interested and involved in operations is
to set up a highly-visible dashboard screen, showing current system
status and uptime. If all is well, the board is green. When there’s a
problem, that should be visible to all developers, and when there’s an
outage, developers should be getting paged. It’s amazing how being
on-call for your own stuff concentrates the mind… on fixing it.</p>
<blockquote>
<p><em>Outside ideas of right doing and wrong doing there is a field.
I’ll meet you there.</em><br />
—Rumi</p>
</blockquote>
<h2 id="tearing-down-the-wall">Tearing down the wall</h2>
<p>When devs and ops collaborate, good stuff happens, so start breaking
down the walls and coming out of your boxes.</p>
<ul>
<li><p>The devs learn about how to deploy, run, and monitor services at
scale</p></li>
<li><p>Ops learn good coding practices, the power of pair programming,
and how to build software as a team</p></li>
<li><p>We all learn how to be better at our jobs, how to be less
defensive, and how to be more empathetic.</p></li>
</ul>
<p>Don’t wait for the management memo, then. Just start today. Instead
of throwing stuff over the walls, try tearing them down instead.</p>]]></content:encoded><media:content height="900" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1763054393118-2KNC2FK2ZTPAG7GEEC0F/bridge-square.webp?format=1500w" width="900"><media:title type="plain">Build bridges, not walls</media:title></media:content></item><item><title>Sysadmins take note</title><dc:creator>John Arundel</dc:creator><pubDate>Wed, 12 Nov 2025 09:26:41 +0000</pubDate><link>https://bitfieldconsulting.com/posts/sysadmins-take-note</link><guid isPermaLink="false">5e10bdc20efb8f0d169f85f9:5e949913434baa2223121b85:691452d6127ad716dabfcc6f</guid><description><![CDATA[Working scientists keep a lab notebook in which they write down everything 
they did, and what the results were. Sysadmin work is also about careful, 
scientific, detailed experimentation, especially when troubleshooting.]]></description><content:encoded><![CDATA[<figure class="
              sqs-block-image-figure
              intrinsic
            "
        >
          
        
        

        
          
            
          
            
                
                
                
                
                
                
                
                <img data-stretch="false" data-image="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp" data-image-dimensions="1200x800" data-image-focal-point="0.5,0.5" alt="" data-load="false" elementtiming="system-image-block" src="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=1000w" width="1200" height="800" sizes="(max-width: 640px) 100vw, (max-width: 767px) 100vw, 100vw" onload="this.classList.add(&quot;loaded&quot;)" srcset="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=100w 100w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=300w 300w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=500w 500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=750w 750w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=1000w 1000w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=1500w 1500w, https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/486d4755-b282-49d6-a839-bc9624998965/notebook.webp?format=2500w 2500w" loading="lazy" decoding="async" data-loader="sqs">

            
          
        
          
        

        
          
          <figcaption class="image-caption-wrapper">
            <p data-rte-preserve-empty="true"><em>Photo by </em><a href="https://unsplash.com/@nublson?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Nubelson Fernandes</em></a><em> on </em><a href="https://unsplash.com/photos/a-person-writing-on-a-notebook-with-a-pen-5VplEcYvMrE?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText"><em>Unsplash</em></a></p>
          </figcaption>
        
      
        </figure>
      

    
  


  





  <p class="">After many years as a professional sysadmin, my best tip for those just starting out is: take notes. Our job is basically about solving hard problems. (If they were easy, users would have solved them already.) This being so, keeping a detailed notebook of everything you do is like having an external brain pack:</p><ul data-rte-list="default"><li><p class="">You don’t have to remember or rediscover how you solved that bizarre&nbsp;DNS&nbsp;problem back at your last gig; it’s in the book</p></li><li><p class="">You don’t have to reread the man page for half an hour to remind yourself how to self-sign an&nbsp;SSL&nbsp;certificate; it’s in the book</p></li><li><p class="">And you don’t have to waste time wondering where you got to on the current project at the end of last week; it’s in the&nbsp;book</p></li></ul><h2><strong>How to keep a&nbsp;notebook</strong></h2><p class="">The sysadmin notebook is one place technology won’t help you. A simple text file is best; you can check it into version control, and access it everywhere. It’s searchable, and if you organise your entries by date, you can find stuff that way as&nbsp;well.</p><p class="">I don’t recommend using a paper notebook, as one of the most useful functions of my notebook is to copy and paste information into it directly from the console session, or from web pages, and then to copy that information out again to save retyping. If you need your notebook with you when you’re on the move, try keeping the text file in your Dropbox and accessing it from your smartphone that way. Alternatively, if you keep it as a Simplenote document, you can sync it with Simplenote on your mobile&nbsp;device.</p><h2><strong>What to put in&nbsp;it</strong></h2><p class="">Working scientists keep a lab notebook in which they write down everything they did, and what the results were. Sysadmin work is also about careful, scientific, detailed experimentation, especially when troubleshooting. When there are many combinations of things to try, you need to document which ones you’ve already tried, and what happened. You can copy and paste console output directly into your notebook for later&nbsp;reference.</p><p class="">One handy tip is to record your console sessions with the&nbsp;script&nbsp;command, for later reference. For&nbsp;example:</p><pre><code>$ script session.txt
Script started, output file is session.txt
bash-3.2$ echo Hello, world
Hello, world
bash-3.2$ exit
^F
Script done, output file is session.txt

$ cat session.txt
Script started on Mon Sep 27 17:30:40 2010
bash-3.2$ echo Hello, world
Hello, world
bash-3.2$ exit
^F
Script done on Mon Sep 27 17:30:52 2010
</code></pre><p class="">Once the job is completed, you can use the session file as a reference for writing up your notes, including copying and pasting commands and output where&nbsp;necessary.</p><h2><strong>A record of what you&nbsp;did</strong></h2><p class="">If you’re a freelancer, it’s important to be able to justify the time you’ve spent on a client project, and to be able to account for what you did and why. If you’re on salary, it’s even more important to be able to communicate regularly and accurately with the rest of the organisation, explaining what you do, and letting management know how valuable you are. The notebook is ideal for ‘weekly achievements’ meetings and emails, and a goldmine when it comes to annual review&nbsp;time.</p><h2><strong>A valuable&nbsp;reference</strong></h2><p class="">The nature of our work tends to involve complicated, lengthy processes requiring accurate command-line input and configuration settings, which take time to get right. This is where the notebook really scores: you need never rely on memory again. Having gone through the process of trial and error to get a system working, you can go back through your notes and distil them into a ‘how to do it’ document which will help to share knowledge with your co-workers, and form part of your essential procedure&nbsp;documentation.</p><h2><strong>A project management&nbsp;tool</strong></h2><p class="">You can also use it to store ‘things to do’ tasks and other reminders (keeping those at the bottom ensures that you see them every time you write a new entry). When you meet with clients or management to discuss project status, your notebook is an essential reference for what you’ve done, problems and roadblocks you’ve encountered, questions that need answering, and tasks that still remain to be&nbsp;done.</p><h2><strong>A communication&nbsp;tool</strong></h2><p class="">Wherever two or more sysadmins are gathered, confusion and problems can result from them all working on the same system - either at the same time, from remote locations, or consecutively, in a shift system. A common document where everybody details what they did and when, can be very useful. When something goes wrong it’s essential to be able to trace through all the changes that were made recently, and find out who or what broke the&nbsp;system.</p><p class="">Anyone who works in a team (and we all do, whether we realise it or not) needs a way to share information with their colleagues about what’s happening, and a team notebook is an excellent way to do&nbsp;this.</p><h2><strong>Examples</strong></h2><p class="">Here’s a couple of illustrative entries from my own&nbsp;notebook:</p><pre><code># 2009-10-09 Friday

Got Cribbage Corner site migrated from HostMonster and ran upgrade
script OK, but now having some trouble delegating the DNS from HM to
DynDNS.

Need to accept mail on peterman for help@cribbagecorner.com so trying
out sendmail virtusertable as detailed here:

    https://www.rushworth.us/lisa/?p=1526

This works fine so email to CC now works - everything is ready to
delegate the domain, but HostMonster doesn't accept the DynDNS
nameservers. Possibly it just hasn't been loaded yet on DynDNS so will
try again later.

Now fixed - the right sequence was:

    Set up the domain on DynDNS and 'pre-activate' it ('Force
    activation') 
    
    Wait for DynDNS to load the records:

	$ host cribbagecorner.com 204.13.249.76 
        Using domain server:
	Name: 204.13.249.76 
        Address: 204.13.249.76#53 
        Aliases:

	cribbagecorner.com has address 94.76.216.167
	cribbagecorner.com mail is handled by 10
	peterman.bitfieldconsulting.com.

    Then go to HostMonster and enter the nameservers:

	ns1.mydyndns.org ns2.mydyndns.org

    It didn't accept them as IP addresses which is why it kept saying
    'Invalid nameserver'. Dumb or what?

I found when the DNS was delegated over, accessing the main page as a
normal user gave 'Access denied' - though subpages worked OK. Found
this page:

    http://drupal.org/node/492734

which suggests going to Site Configuration / Performance and clicking
'Clear cached data'. This worked!

# 2010-09-08 Wednesday

Working on PHP build. Downloaded PHP-FPM:

  [link]  
Generated patch:

  php-fpm-0.6-5.3.0/generate-fpm-patch
  
Downloaded PHP:

  [link]
  
Patched:

  patch -p1 &lt; ../fpm.patch
  
Prepared for Debian build:

  dh_make -e sysadmin@example.com -f ../php-5.3.0.tar.gz
  
Edited control file and added custom configure line to debian/rules:

  override_dh_auto_configure:
          ./buildconf --force
          ./configure --with-fpm \
            --with-libevent=shared,/usr/local/lib/libevent.so \
            --with-mcrypt \
            --with-zlib \
            --enable-mbstring \
            --with-openssl \
            --with-mysql=/usr/bin/mysql_config \
            --with-mysqli=mysqlnd \
            --with-pdo-mysql=mysqlnd \
            --with-mysql-sock \
            --without-sqlite \
            --with-curl

per

  http://www.debian.org/doc/maint-guide/ch-dreq.en.html
  
Then:

  dpkg-buildpackage -b -uc -rfakeroot
</code></pre><p class="">In general, whenever I have to expend brainpower to solve a problem, I’ll consider it worth recording the solution (and the problem) in the notebook. Similarly, if there’s a command line that took some trial and error to construct, it’s worth a couple of seconds to copy and paste it into the&nbsp;notebook.</p><h2><strong>Start&nbsp;today</strong></h2><p class="">It doesn’t matter if you haven’t been keeping notes up to now and think it’s too late to start. It’s not. Just open a new text file, put the date, and type in what you did&nbsp;today.</p>]]></content:encoded><media:content height="800" isDefault="true" medium="image" type="image/webp" url="https://images.squarespace-cdn.com/content/v1/5e10bdc20efb8f0d169f85f9/1762939950873-6K8R9Z73NLLHQZIXQ1AT/notebook-square.webp?format=1500w" width="800"><media:title type="plain">Sysadmins take note</media:title></media:content></item></channel></rss>