<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Western Wilson</title><link>https://westernwilson.com/</link><description>Recent content by Western Wilson</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sun, 06 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://westernwilson.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Now</title><link>https://westernwilson.com/now/</link><pubDate>Sun, 06 Sep 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/now/</guid><description>&lt;h2 id="amsterdam" class="group relative scroll-mt-20"&gt;&#10; Amsterdam&#10; &lt;a&#10; href="#amsterdam"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;Finally in Amsterdam after an extended stay in Belgrade, Serbia!! I&amp;rsquo;ve accepted a Cloud Engineer role at &lt;a href="https://www.sensysgatso.com/"&gt;Sensys Gatso Group&lt;/a&gt; and I&amp;rsquo;m building a new normal around it.&lt;/p&gt;&#10;&lt;p&gt;Moving through life with more intention. At the moment that means focusing on Health (sleep hygiene and training), learning fast in the new role, and reading well: the Jane Austen classics and the Bible.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="amsterdam" class="group relative scroll-mt-20">
  Amsterdam
  <a
    href="#amsterdam"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Finally in Amsterdam after an extended stay in Belgrade, Serbia!! I&rsquo;ve accepted a Cloud Engineer role at <a href="https://www.sensysgatso.com/">Sensys Gatso Group</a> and I&rsquo;m building a new normal around it.</p>
<p>Moving through life with more intention. At the moment that means focusing on Health (sleep hygiene and training), learning fast in the new role, and reading well: the Jane Austen classics and the Bible.</p>
<h2 id="routine" class="group relative scroll-mt-20">
  Routine
  <a
    href="#routine"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>There is freedom in having a thought out structure to life.</p>
<p>A routine is something I&rsquo;ve been iterating on for years. This latest iteration is the culmination of all my efforts and I&rsquo;m really happy with it.</p>
<p>Up with the sun, creatine and amino water, no phone for the first hour. Journal and walk before the office 2-3 days a week with consistent hours, gym on the days I&rsquo;m home. Game dev after work, no screens from 9pm, then reading before bed. Fasting until lunch, eating only between 12pm and 6pm.</p>
<p>I use pomodoro timers to gamify focus time across my days.</p>
<h2 id="building" class="group relative scroll-mt-20">
  Building
  <a
    href="#building"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>I&rsquo;m making a 2D exploration golf game in the <a href="https://godotengine.org">Godot</a> engine!! It&rsquo;s built around a swing mechanic I haven&rsquo;t seen anywhere else in my research. Watch this space.</p>
<p>See the tech I&rsquo;m using here: <a href="/techstack/">Tech Stack</a>.</p>
]]></content:encoded></item><item><title>The DevOps Handbook</title><link>https://westernwilson.com/books/the-devops-handbook/</link><pubDate>Sun, 06 Sep 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/books/the-devops-handbook/</guid><description>&lt;h2 id="notes-on-the-devops-handbook" class="group relative scroll-mt-20"&gt;&#10; Notes on The DevOps Handbook&#10; &lt;a&#10; href="#notes-on-the-devops-handbook"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;ve read this book twice now, once early on in my career and again now whilst having a breadth of more DevOps experience at companies with different levels of DevOps maturity.&#10;I found myself skimming sections that weren&amp;rsquo;t as useful to me now, but re-reading in detail sections I found new insights from. You&amp;rsquo;ll be able to tell which based on my most recent quote pulls at the end.&#10;The whole book hangs off the Three Ways, which is kind of funny because Taoism talks about &amp;ldquo;The Way&amp;rdquo;, so this recent reading reminded me of Taoism and the book I recently read there.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="notes-on-the-devops-handbook" class="group relative scroll-mt-20">
  Notes on The DevOps Handbook
  <a
    href="#notes-on-the-devops-handbook"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>I&rsquo;ve read this book twice now, once early on in my career and again now whilst having a breadth of more DevOps experience at companies with different levels of DevOps maturity.
I found myself skimming sections that weren&rsquo;t as useful to me now, but re-reading in detail sections I found new insights from. You&rsquo;ll be able to tell which based on my most recent quote pulls at the end.
The whole book hangs off the Three Ways, which is kind of funny because Taoism talks about &ldquo;The Way&rdquo;, so this recent reading reminded me of Taoism and the book I recently read there.</p>
<ul>
<li>The First Way is flow: get work moving left to right, from the business to the customer, as fast as possible.</li>
<li>The Second Way is feedback: get signal moving right to left, fast and constantly, so problems get fixed near where they were made.</li>
<li>The Third Way is a culture of experimentation and learning, where you deliberately take risks and treat failure as information rather than something to hide.</li>
</ul>
<p>The best companies I&rsquo;ve worked at give a culture safety to experimentation, AND provide tooling to support safe experiments. Which in essence gives cultural safety again! If deploys are scary, that&rsquo;s a signal about your architecture and your test coverage, not about the inherent difficulty of shipping.</p>
<p>Continuous integration, trunk-based development, telemetry everywhere(!!!), feature flags, and labelling low-risk releases.</p>
<p>Small batches of changes are important to make failures small, cheap, easy to attribute, and easy to rollback. Big batches make failures ambiguous and tough to revert.</p>
<p>The importance of blameless postmortem was rehighlighted here, Atlassian has a great doc on it. The argument is that blame drives information under the rug, if naming a cause means naming a person who gets punished, you stop learning what actually happened, remove cultural safety of experimentation, and reduce the effectiveness of the whole investigation.</p>
<p>Workshops and their importance to get everyone involved so we can discover the quick wins; this is workshops including both tech experts, decision makers, and domain experts.</p>
<p>Value Stream mapping is next exercise, showing how new features (or new value) is added to the app. Probably customer request or business hypothesis, flowing through planning, dev, and deploy.
There is skill involved in identifying the teams involved with a given Value stream. I think that a value stream is a domain within a company and the value that a given team (or cluster of teams) offers the customer.</p>
<p>One key thing that tends to happen is surfacing of the many heroics across the business.
Normally we think of heroics as great, but in DevOps of someone has to be a hero than the process has failed us and we should fix that process. Minimising heroics from the team, like late nights or weekend works, is important for the longevity of the dev team overall.</p>
<p>Deployment methodologies for safe deployments, one method is blue/green databases with a switch the flicks over post-successful deployment. The better solution is to actually decouple the database changes from the application, this is what I&rsquo;ve seen done more often than not. No deletion of columns, and make sure the changes are additive only. Complex db changes require different approaches.</p>
<p>Use the strangler patter to safely evolve the architecture of a system over time. The strangler pattern is worth knowing about, I experienced this during my time at Xero.</p>
<h2 id="quotes" class="group relative scroll-mt-20">
  Quotes
  <a
    href="#quotes"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<h3 id="introductions" class="group relative scroll-mt-20">
  Introductions
  <a
    href="#introductions"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>High performers deployed code thirty times more frequently, and the time required to go from “code committed” to “successfully running in production” was two hundred times faster—high performers had lead times measured in minutes or hours, while low performers had lead times measured in weeks, months, or even quarters.</p>
</blockquote>
<blockquote>
<p>Another more extreme example is Amazon. In 2011, Amazon was performing approximately seven thousand deploys per day. By 2015, they were performing 130,000 deploys per day.</p>
</blockquote>
<blockquote>
<p>We describe value streams, how DevOps is the result of applying Lean principles to the technology value stream, and the Three Ways: Flow, Feedback, and Continual Learning and Experimentation.</p>
</blockquote>
<h3 id="part-i--the-three-ways" class="group relative scroll-mt-20">
  Part I — The Three Ways
  <a
    href="#part-i--the-three-ways"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Improving daily work is even more important than doing daily work.</p>
</blockquote>
<blockquote>
<p>In addition to lead times and process times, the third key metric in the technology value stream is percent complete and accurate (%C/A). This metric reflects the quality of the output of each step in our value stream. Karen Martin and Mike Osterling state that “the %C/A can be obtained by asking downstream customers what percentage of the time they receive work that is ‘usable as is,’ meaning that they can do their work without having to correct the information that was provided, add missing information that should have been supplied, or clarify information that should have and could have been clearer.”</p>
</blockquote>
<blockquote>
<p>By seeing problems as they occur and swarming them until effective countermeasures are in place, we continually shorten and amplify our feedback loops, a core tenet of virtually all modern process improvement methodologies. This maximizes the opportunities for our organization to learn and improve.</p>
</blockquote>
<h4 id="the-first-way-the-principles-of-flow" class="group relative scroll-mt-20">
  The First Way: The Principles of Flow
  <a
    href="#the-first-way-the-principles-of-flow"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h4>
<blockquote>
<p>Work is not done when Development completes the implementation of a feature—rather, it is only done when our application is running successfully in production, delivering value to the customer.</p>
</blockquote>
<blockquote>
<p>REDUCE THE NUMBER OF HANDOFFS</p>
</blockquote>
<blockquote>
<p>High performers, regardless of whether an engineer is in Development, QA, Ops, or Infosec, state that their goal is to help maximize developer productivity.</p>
</blockquote>
<h4 id="the-second-way-the-principles-of-feedback" class="group relative scroll-mt-20">
  The Second Way: The Principles of Feedback
  <a
    href="#the-second-way-the-principles-of-feedback"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h4>
<blockquote>
<p>Dr. Sidney Dekker, who also codified some of the key elements of safety culture, observed another characteristic of complex systems: doing the same thing twice will not predictably or necessarily lead to the same result. It is this characteristic that makes static checklists and best practices, while valuable, insufficient to prevent catastrophes from occurring.</p>
</blockquote>
<blockquote>
<p>We use peer reviews of our proposed changes to gain whatever assurance is needed that our changes will operate as designed. We automate as much of the quality checking typically performed by a QA or Information Security department as possible. Instead of developers needing to request or schedule a test to be run, these tests can be performed on demand, enabling developers to quickly test their own code and even deploy those changes into production themselves. By doing this, we truly make quality everyone’s responsibility as opposed to it being the sole responsibility of a separate department.</p>
</blockquote>
<h4 id="the-third-way-the-principles-of-continuous-learning-and-experimentation" class="group relative scroll-mt-20">
  The Third Way: The Principles of Continuous Learning and Experimentation
  <a
    href="#the-third-way-the-principles-of-continuous-learning-and-experimentation"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h4>
<blockquote>
<p>The leader helps coach the person conducting the experiment with questions that may include:</p>
<ul>
<li>What was your last step and what happened?</li>
<li>What did you learn?</li>
<li>What is your condition now?</li>
<li>What is your next target condition?</li>
<li>What obstacle are you working on now?</li>
<li>What is your next step?</li>
<li>What is your expected outcome?</li>
<li>When can we check?</li>
</ul>
</blockquote>
<h3 id="part-ii--where-to-start" class="group relative scroll-mt-20">
  Part II — Where to Start
  <a
    href="#part-ii--where-to-start"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Instead of the top-down, command-and-control approach, we need broad support from throughout the organization, especially from those doing the daily work.</p>
</blockquote>
<blockquote>
<p>Find Innovators and Early Adopters: In the beginning, we focus our efforts on teams who actually want to help—these are our kindred spirits and fellow travelers who are the first to volunteer to start the DevOps journey.</p>
</blockquote>
<blockquote>
<p>Based on their research, Dr. Govindarajan and Dr. Trimble assert that organizations need to create a dedicated transformation team that is able to operate outside of the rest of the organization that is responsible for daily operations (which they call the “dedicated team” and “performance engine” respectively).</p>
</blockquote>
<blockquote>
<p>By dedicating 20% of our cycles so that Dev and Ops can create lasting countermeasures to the problems we encounter in our daily work, we ensure that technical debt doesn’t impede our ability to quickly and safely develop and operate our services in production.</p>
</blockquote>
<blockquote>
<p>These observations led to what is now known as Conway’s Law, which states that “organizations which design systems… are constrained to produce designs which are copies of the communication structures of these organizations…. The larger an organization is, the less flexibility it has and the more pronounced the phenomenon.”</p>
</blockquote>
<blockquote>
<p>In high-performing organizations, everyone within the team shares a common goal—quality, availability, and security aren’t the responsibility of individual departments, but are a part of everyone’s job, every day.</p>
</blockquote>
<blockquote>
<p>As Jason Cox, Director of Systems Engineering at Disney, described, “Inside of Operations, we had to change our hiring practices. We looked for people who had ‘curiosity, courage, and candor,’ who were not only capable of being generalists but also renegades… We want to promote positive disruption so our business doesn’t get stuck and can move into the future.”</p>
</blockquote>
<blockquote>
<p>Another way to enable high-performing outcomes is to create stable service teams with ongoing funding to execute their own strategy and road map of initiatives. These teams have the dedicated engineers needed to deliver on concrete commitments made to internal and external customers, such as features, stories, and tasks.</p>
</blockquote>
<blockquote>
<p>Another way we can enable more market-oriented outcomes is by enabling product teams to become more self-sufficient by embedding Operations engineers within them, thus reducing their reliance on centralized Operations.</p>
</blockquote>
<h3 id="part-iii--the-first-way-flow" class="group relative scroll-mt-20">
  Part III — The First Way: Flow
  <a
    href="#part-iii--the-first-way-flow"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Deploying the same way to every environment</p>
</blockquote>
<blockquote>
<p>But why does using version control for our environments predict IT and organizational performance better than using version control for our code? Because in almost all cases, there are orders of magnitude more configurable settings in our environment than in our code. Consequently, it is the environment that needs to be in version control the most.</p>
</blockquote>
<blockquote>
<p>In other words, we will only accept development work as done when it can be successfully built, deployed, and confirmed that it runs as expected in a production-like environment, instead of merely when a developer believes it to be done—ideally, it runs under a production-like load with a production-like dataset, long before the end of a sprint.</p>
</blockquote>
<blockquote>
<p>ADOPT TRUNK-BASED DEVELOPMENT PRACTICES</p>
</blockquote>
<blockquote>
<p>Frequent code commits to trunk means we can run all automated tests on our software system as a whole and receive alerts when a change breaks some other part of the application or interferes with the work of another developer. And because we can detect merge problems when they are small, we can correct them faster.</p>
</blockquote>
<blockquote>
<p>Of course, we know that we need to be deploying more frequently to achieve our desired outcome of smooth and fast flow, not less frequently. To enable this, we need to decouple our production deployments from our feature releases. In practice, the terms deployment and release are often used interchangeably.</p>
</blockquote>
<blockquote>
<p>Gracefully degrade performance: When our service experiences extremely high loads that would normally require us to increase capacity or, worse, risk having our service fail in production, we can use feature toggles to reduce the quality of service. In other words, we can increase the number of users we serve by reducing the level of functionality delivered (e.g., reduce the number of customers who can access a certain feature, disable CPU-intensive features such as recommendations, etc.).</p>
</blockquote>
<h3 id="part-iv--the-second-way-feedback" class="group relative scroll-mt-20">
  Part IV — The Second Way: Feedback
  <a
    href="#part-iv--the-second-way-feedback"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>As Adrian Cockcroft pointed out, “Monitoring is so important that our monitoring systems need to be more available and scalable than the systems being monitored.”</p>
</blockquote>
<blockquote>
<p>To enable everyone to be able to find and fix problems in their daily work, we need to enable everyone to create metrics in their daily work that can be easily created, displayed, and analyzed.</p>
</blockquote>
<blockquote>
<p>When critical production services have problems, waking people at 2 a.m. may be the right thing to do. However, when we create alerts that are not actionable or are false-positives, we’ve unnecessarily woken up people in the middle of the night.</p>
</blockquote>
<blockquote>
<p>Tom Limoncelli, co-author of The Practice of Cloud System Administration: Designing and Operating Large Distributed Systems and a former Site Reliability Engineer at Google, relates the following story on monitoring: “When people ask me for recommendations on what to monitor, I joke that in an ideal world, we would delete all the alerts we currently have in our monitoring system. Then, after each user-visible outage, we’d ask what indicators would have predicted that outage and then add those to our monitoring system, alerting as needed. Repeat. Now we only have alerts that prevent outages, as opposed to being bombarded by alerts after an outage already occurred.”</p>
</blockquote>
<blockquote>
<p>Before we build a feature, we should rigorously ask ourselves, “Should we build it, and why?” We should then perform the cheapest and fastest experiments possible to validate through user research whether the intended feature will actually achieve the desired outcomes. We can use techniques such as hypothesis-driven development, customer acquisition funnels, and A/ B testing, concepts we explore throughout this chapter.</p>
</blockquote>
<blockquote>
<p>The surprising reality is that in environments with low-trust, command-and-control cultures, the outcomes of these types of change control and testing countermeasures often result in an increased likelihood that problems will occur again, potentially with even worse outcomes.</p>
</blockquote>
<blockquote>
<p>One of the core beliefs in the Toyota Production System is that “people closest to a problem typically know the most about it.”</p>
</blockquote>
<blockquote>
<p>Pair programming has the additional benefit of spreading knowledge throughout the organization and increasing information flow within the team. Having more experienced engineers review while the less experienced engineer codes is also an effective way to teach and be taught.</p>
</blockquote>
<h3 id="part-v--the-third-way-continual-learning-and-experimentation" class="group relative scroll-mt-20">
  Part V — The Third Way: Continual Learning and Experimentation
  <a
    href="#part-v--the-third-way-continual-learning-and-experimentation"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Blameless post-mortems are about accurately learning what happened, not about assigning blame.</p>
</blockquote>
<blockquote>
<p>During the meeting and the subsequent resolution, we should explicitly disallow the phrases “would have” or “could have,” as they are counterfactual statements that result from our human tendency to create possible alternatives to events that have already occurred. Counterfactual statements, such as “I could have…” or “If I had known about that, I should have…,” frame the problem in terms of the system as imagined instead of in terms of the system that actually exists, which is the context we need to restrict ourselves to.</p>
</blockquote>
<blockquote>
<p>In addition to the Lean-oriented terms kaizen blitz and improvement blitz, the technique of dedicated rituals for improvement work has also been called spring or fall cleanings and ticket queue inversion weeks. Other terms have also been used, such as hack days, hackathons, and 20% innovation time.</p>
</blockquote>
<blockquote>
<p>A dynamic culture of learning creates conditions so that everyone can not only learn, but also teach, whether through traditional didactic methods (e.g., people taking classes, attending training) or more experiential or open methods (e.g., conferences, workshops, mentoring). One way that we can foster this teaching and learning is to dedicate organizational time to it.</p>
</blockquote>
<blockquote>
<p>Bland said, at that time, there was a 20% innovation time policy at Google, enabling developers to spend roughly one day per week on a Google-related project outside of their primary area of responsibility. Some engineers chose to form grouplets, ad hoc teams of like-minded engineers who wanted to pool their 20% time, allowing them to do focused improvement blitzes.</p>
</blockquote>
<h3 id="part-vi-integrating-security-change-management-and-compliance" class="group relative scroll-mt-20">
  Part VI: Integrating Security, Change Management and Compliance
  <a
    href="#part-vi-integrating-security-change-management-and-compliance"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Instead of inspecting security into our product at the end of the process, we will create and integrate security controls into the daily work of Development and Operations, so that security is part of everyone’s job, every day. Ideally, this work will be automated and put into our deployment pipeline. Furthermore, we will augment our manual practices, acceptances, and approval processes with automated controls, relying less on controls such as separation of duties and change approval processes.</p>
</blockquote>
<blockquote>
<p>Source code integrity and code signing: All developers should have their own PGP key, perhaps created and managed in a system such as keybase.io.</p>
</blockquote>
<blockquote>
<p>OWASP publishes a great deal of useful guidance such as the Cheat Sheet series, which includes: How to store passwords How to handle forgotten passwords How to handle logging How to prevent cross-site scripting (XSS) vulnerabilities</p>
</blockquote>
<blockquote>
<p>No one actually looks at the unit tests, and they’re run every time someone commits code to the repo.” This demonstrates that in order to adequately protect the integrity of our applications and environments, we must also mitigate the attack vectors on our deployment pipeline.</p>
</blockquote>
<blockquote>
<p>In this case, we must ensure that any submitted change requests are as complete and accurate as possible, giving the CAB everything they need to properly evaluate our change—after all, if our change request is malformed or incomplete, it will be bounced back to us, increasing the time required for us to get into production and casting doubt on whether we actually understand the goals of the change management process.</p>
</blockquote>
<blockquote>
<p>For Mangot and Mathew, one of the key successes from all the repeatability and rigor they designed into the process was being told by their change management group that “infrastructure changes made through Puppet would now be treated as ‘standard changes,’ requiring far less or even no further approvals from the CAB.” Furthermore, they noted that “manual changes to infrastructure would still require approvals.”</p>
</blockquote>
<h3 id="appendices" class="group relative scroll-mt-20">
  Appendices
  <a
    href="#appendices"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>APPENDIX 9 THE SIMIAN ARMY After the 2011 AWS EAST Outage, Netflix had numerous discussions about engineering their systems to automatically deal with failure. These discussions have evolved into a service called “Chaos Monkey.”</p>
</blockquote>
]]></content:encoded></item><item><title>Containerising my coding agent</title><link>https://westernwilson.com/reflections/containerising-my-coding-agent/</link><pubDate>Sun, 21 Jun 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/containerising-my-coding-agent/</guid><description>&lt;p&gt;I containerised Claude Code this week because I wanted to let it run with fewer guardrails and not hand it unlimited access the rest of my machine. There&amp;rsquo;s a &lt;a href="https://news.ycombinator.com/item?id=46268222"&gt;Hacker News thread&lt;/a&gt; from 6 months ago about someone running Claude in yolo mode who asked it to clean something up and watched it &lt;code&gt;rm -rf&lt;/code&gt; their home directory 😅. So let&amp;rsquo;s aim to at least avoid that.&lt;/p&gt;&#10;&lt;p&gt;Thankfully, I found &lt;a href="https://containers.dev"&gt;Dev Containers&lt;/a&gt;, which turned into its own rabbit hole but lets us lock down Claude without having to maintain our own docker image.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I containerised Claude Code this week because I wanted to let it run with fewer guardrails and not hand it unlimited access the rest of my machine. There&rsquo;s a <a href="https://news.ycombinator.com/item?id=46268222">Hacker News thread</a> from 6 months ago about someone running Claude in yolo mode who asked it to clean something up and watched it <code>rm -rf</code> their home directory 😅. So let&rsquo;s aim to at least avoid that.</p>
<p>Thankfully, I found <a href="https://containers.dev">Dev Containers</a>, which turned into its own rabbit hole but lets us lock down Claude without having to maintain our own docker image.</p>
<p>This reflection post talks through the containerised setup and some issues I faced while setting it up. If you just want the solution, then the implementation is in the <a href="https://github.com/wjkw1/westernwilson.com/tree/main/.devcontainer">.devcontainer/</a> directory of this website&rsquo;s source code.</p>
<h2 id="feature-or-docker-image" class="group relative scroll-mt-20">
  Feature or docker image?
  <a
    href="#feature-or-docker-image"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>I assumed that Anthropic would ship an official Claude base image you build on top of. They don&rsquo;t, they only post a recommended starting point. Claude Code installs into any <a href="https://code.claude.com/docs/en/devcontainer">Dev Container</a> through the <code>ghcr.io/anthropics/devcontainer-features/claude-code:1.0</code> feature, which you reference in <code>devcontainer.json</code> like <a href="https://containers.dev/features">any other devcontainers feature</a>. It works wherever the Dev Containers spec is supported e.g. VS Code, Codespaces, JetBrains, and (with some rough edges) Cursor. In VS Code it can be configured to pull in the Claude Code extension, keeping Claude decoupled from the base image entirely.</p>
<p>That leaves three layers to assemble: a base image, the tooling you use, and Claude Code on top. The real decision is whether you bake those middle two layers into a custom docker image you push to a registry, or assemble them per-repo through <code>devcontainer.json</code> features. A custom image is the better call for a team, where you want a validated, secure, and reusable environment everyone builds on. For a solo setup spun up per-repo, features are easier. The cost is a slow first build and less control over exactly what lands in the image, which is what <code>post-create.sh</code> helps automate somewhat.</p>
<p>My <code>devcontainer.json</code> ended up looking like this, trimmed to the main parts:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-jsonc" data-lang="jsonc"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;image&#34;</span>: <span style="color:#e6db74">&#34;mcr.microsoft.com/devcontainers/base:ubuntu&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;features&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;ghcr.io/anthropics/devcontainer-features/claude-code:1.0&#34;</span>: {}
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;remoteUser&#34;</span>: <span style="color:#e6db74">&#34;vscode&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;mounts&#34;</span>: [
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;source=claude-code-config-${devcontainerId},target=/home/vscode/.claude,type=volume&#34;</span>
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;runArgs&#34;</span>: [<span style="color:#e6db74">&#34;--cap-add=NET_ADMIN&#34;</span>, <span style="color:#e6db74">&#34;--cap-add=NET_RAW&#34;</span>],
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;postCreateCommand&#34;</span>: <span style="color:#e6db74">&#34;sudo zsh .devcontainer/post-create.sh&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;postStartCommand&#34;</span>: <span style="color:#e6db74">&#34;sudo /usr/local/sbin/init-firewall.sh&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;waitFor&#34;</span>: <span style="color:#e6db74">&#34;postStartCommand&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The non-root <code>vscode</code> user is important if you ever run with <code>--dangerously-skip-permissions</code>, Claude Code requires it. The named volume on <code>~/.claude</code> means login persists across rebuilds instead of re-authenticating every time. The <code>NET_ADMIN</code>/<code>NET_RAW</code> capabilities exist so that we can create the containerised firewall. <code>waitFor: postStartCommand</code> blocks the terminal until that firewall script finishes, so there&rsquo;s no window where Claude is already running before egress is locked down.</p>
<p><code>post-create.sh</code> is the everyday stuff a feature-based setup doesn&rsquo;t hand you for free: setting git aliases, adding a couple of zsh aliases for <code>kubectl</code> and <code>terraform</code>, locking down <code>sudo</code>/<code>su</code>.</p>
<p><code>init-firewall.sh</code> sets default-DROP policies on iptables, then builds an <code>ipset</code> allowlist of everything the container is allowed to reach outbound. Part of that allowlist is GitHub&rsquo;s own published IP ranges, fetched live at container start:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>gh_ranges<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>curl -s https://api.github.com/meta<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">while</span> read -r cidr; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>    ipset add allowed-domains <span style="color:#e6db74">&#34;</span>$cidr<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span> &lt; &lt;<span style="color:#f92672">(</span>echo <span style="color:#e6db74">&#34;</span>$gh_ranges<span style="color:#e6db74">&#34;</span> | jq -r <span style="color:#e6db74">&#39;(.web + .api + .git)[]&#39;</span> | aggregate -q<span style="color:#f92672">)</span>
</span></span></code></pre></div><p>Everything else —npm, <code>api.anthropic.com</code>, the VS Code marketplace— gets resolved and added individually. The firewall script finishes by checking its own work: it tries <code>curl https://example.com</code> and expects that to fail, then tries <code>curl https://api.github.com/zen</code> and expects that to succeed.</p>
<h2 id="firewall-and-sudo" class="group relative scroll-mt-20">
  Firewall and sudo
  <a
    href="#firewall-and-sudo"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>I spent a decent chunk of time configuring who&rsquo;s allowed to touch <code>iptables</code>. The <code>common-utils</code> feature grants the <code>vscode</code> user blanket passwordless <code>sudo</code> by default, and my first cut of <code>post-create.sh</code> narrowed that down to the <code>iptables</code>/<code>iptables-save</code>/<code>ipset</code> binaries themselves, with no argument restriction. Which meant Claude or anything else running as <code>vscode</code>, including under <code>--dangerously-skip-permissions</code>, could just run <code>sudo iptables -F &amp;&amp; sudo iptables -P OUTPUT ACCEPT</code> and the firewall would be gone! Anthropic&rsquo;s own reference devcontainer avoids this by granting <code>sudo</code> on one fixed script path instead of the raw binaries, baked into their image at build time so the script itself can&rsquo;t be edited either. I didn&rsquo;t want a custom Dockerfile yet, but dev containers can do the same thing with just <code>postCreateCommand</code>: copy <code>init-firewall.sh</code> to a root-owned <code>/usr/local/sbin/init-firewall.sh</code> outside the bind-mounted workspace while the feature&rsquo;s broad sudo grant is still active, then narrow <code>sudo</code> down to NOPASSWD on that one frozen executable file only. The tradeoff is that editing the live <code>init-firewall.sh</code> to allow a new domain now needs a full <strong>Rebuild Container</strong>, not just a restart, since the frozen copy is what actually runs.</p>
<p>Claude has it&rsquo;s own way to limit it&rsquo;s own execution too using <code>.claude/settings.json</code>. We&rsquo;ve configured this on top to deny any <code>Bash(sudo *)</code> and <code>Bash(su *)</code> commands, so in normal mode Claude won&rsquo;t even attempt the command. But <code>--dangerously-skip-permissions</code> does exactly what its name says: it bypasses every permission rule, deny rules included. So that file is a speed bump for the default mode, not something to lean on once the safety&rsquo;s off. The frozen-script sudoers restriction is the one that locks it down regardless, because it&rsquo;s enforced by the OS, not by Claude Code.</p>
<h2 id="gap-in-the-firewall" class="group relative scroll-mt-20">
  Gap in the firewall
  <a
    href="#gap-in-the-firewall"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>One thing I noticed was that <code>api.github.com/meta</code>&rsquo;s ip ranges aren&rsquo;t scoped to the API. They cover GitHub&rsquo;s whole edge network, which includes GitHub Pages in some cases. For example, this website, <a href="https://westernwilson.com">westernwilson.com</a>, runs on GitHub Pages. So with the firewall up and supposedly restricting the agent to an approved set of domains, I could still reach my own site. And by the same logic, any other <code>*.github.io</code> page, or anything hosted there by someone with worse intentions. A page on <code>*.github.io</code> could potentially hold a payload hosted somewhere else entirely, and the firewall would wave it through without ever knowing the difference.</p>
<p>I see three ways to close that off:</p>
<ul>
<li>Proxy the traffic and filter on hostname instead of IP. Cleanest in theory, but Encrypted Client Hello can still hide the hostname from anything sitting.</li>
<li>Drop <code>*.github.io</code> from the allowlist entirely. Unfortunately this also takes <code>raw.githubusercontent.com</code> out too since they share the same ip ranges, but if that&rsquo;s not something the container needs, it&rsquo;s a fine trade.</li>
<li>Accept the risk and move on.</li>
</ul>
<p>I haven&rsquo;t picked one yet, for now I&rsquo;m sitting with the risk while I think about which tradeoff is actually worth it.</p>
<h2 id="logging-in-to-claude" class="group relative scroll-mt-20">
  Logging in to Claude
  <a
    href="#logging-in-to-claude"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Logging in had its own gotcha. In VS Code there is a big orange &ldquo;use your subscription&rdquo; button that opens a browser and waits for a callback to <code>localhost</code>, which never makes it back into the container and just hangs. The fix is to skip the button, open a terminal inside the container and run <code>claude</code>, which kicks off the same browser flow but resolves the callback correctly.</p>
<p>We then use <code>~/.claude</code>, which is now owned by the <code>vscode</code> thanks to the <code>chown</code> at the top of <code>post-create.sh</code>, so once logged in, we gain stickier login sessions.</p>
<p>In doing the whole login dance from a bare terminal meant that my first real session with Claude Code inside the dev container was just text based interaction with no IDE extension wrapper around it at all. It&rsquo;s an interesting way to work and removes a lot of the UI distraction, but not something I&rsquo;ll rely on fulltime.</p>
<h2 id="port-forwarding" class="group relative scroll-mt-20">
  Port forwarding
  <a
    href="#port-forwarding"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>The other thing that briefly worried me is VS Code forwarded around thirty-two ports the moment the container came up. My first thought was that something in the container was reaching out on its own. It&rsquo;s not, after some searching I found that it&rsquo;s VS Code&rsquo;s automatic port-forwarding scanning the container for anything listening on a port, for example a dev server that you&rsquo;ll want to open in a browser. It&rsquo;s a separate, fairly trigger-happy feature, and apparently a known source of noise; <a href="https://github.com/microsoft/vscode-remote-release/issues/10926">This GitHub issue</a> describes the exact same &ldquo;over 20 ports auto-forwarded&rdquo; behaviour after a setting silently flips from <code>process</code> to <code>hybrid</code> detection mode.</p>
<p>If you want to see the actual config rather than the excerpts above, it&rsquo;s all in this repo under <a href="https://github.com/wjkw1/westernwilson.com/tree/main/.devcontainer">.devcontainer/</a>. Treat it as a personal setup to extend, not a hardened image to copy verbatim.</p>
<p>Further reading: Anthropic&rsquo;s <a href="https://code.claude.com/docs/en/devcontainer">Dev Containers docs</a> and the <a href="https://code.visualstudio.com/docs/devcontainers/tutorial">VS Code Dev Containers tutorial</a>.</p>
]]></content:encoded></item><item><title>Clean Code</title><link>https://westernwilson.com/books/clean-code/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/books/clean-code/</guid><description>&lt;h2 id="notes-on-clean-code" class="group relative scroll-mt-20"&gt;&#10; Notes on Clean Code&#10; &lt;a&#10; href="#notes-on-clean-code"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;This book is packed with examples and opinions by people with decades of experience. Martin has clearly written a lot of code and seen a lot of projects go sideways, then picked up the pieces and improved them.&lt;/p&gt;&#10;&lt;p&gt;My main takeaway is that the cost of bad code is huge, and it compounds over time. A team that moves fast with messy code will eventually stall. Anyone who has picked up someone else&amp;rsquo;s code and spent an hour just trying to figure out what it does will know exactly what he means.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="notes-on-clean-code" class="group relative scroll-mt-20">
  Notes on Clean Code
  <a
    href="#notes-on-clean-code"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>This book is packed with examples and opinions by people with decades of experience. Martin has clearly written a lot of code and seen a lot of projects go sideways, then picked up the pieces and improved them.</p>
<p>My main takeaway is that the cost of bad code is huge, and it compounds over time. A team that moves fast with messy code will eventually stall. Anyone who has picked up someone else&rsquo;s code and spent an hour just trying to figure out what it does will know exactly what he means.</p>
<p>Somewhere in the first chapter he makes a link between the abstraction away from byte code to assembly to object oriented languages, then expands further to consider a new AI system that will do our jobs for us (similar to what we are living through). It gave me a strong sense that no matter what level of abstraction we rise to (AI non-deterministic prompting being the latest), we still need strong, clear communication of our ideas.</p>
<p>There was a comment about Coders being essentially Authors, I don&rsquo;t know why, but was a paradigm shift in my brain. We are writers using language syntax that tells machines what to do, but also has to show other Authors/engineers what it does. So be professional and communicate clearly. You&rsquo;ve read bad articles or books, and you&rsquo;ve read good ones, make sure your code aligns closely with the good ones.</p>
<p>He put a lot of emphasis on the effort you should spend on good names. But not letting that effort slow you down because it is refactorable. Both of these change how I think about naming things.</p>
<p>A clever shorthand variable name might save you ten seconds when you write it, but it&rsquo;ll cost the next person ten minutes when they read it.</p>
<p>A good variable or function name is a small piece of documentation that you never have to keep in <em>sync</em> with the code, because it <em>is</em> the code. The time you spend thinking of a better name is almost always worth it.</p>
<p>Functions should do one thing. That principle sounds obvious until you realise how hard it actually is to follow. While reading this chapter I remembered functions I&rsquo;d written that did a few things, and cringed. Ideally, the function should do one thing well, and be named in line with exactly what it does.</p>
<p>I&rsquo;d heard these comment vs code arguments from an old colleague, this book reemphasised what he was saying. Comments are a failure; they&rsquo;re a sign that the code isn&rsquo;t clear enough to speak for itself. Every comment you write is maintenance debt. Code changes; comments drift. There&rsquo;s nothing worse than a comment that confidently describes something the code no longer does. I agreed with this chapter wholeheartedly.</p>
<p>The boy scout rule is great too: leave the campground cleaner than you found it. Don&rsquo;t just fix the bug and ship it. Rename the thing that confused you, or extract the chunk that didn&rsquo;t belong. Don&rsquo;t rewriting everything, but small incremental improvements accumulating over time into a codebase makes it actually pleasant to work in.</p>
<p>One key idea to me was the purpose of writing OOP; from what I gather, it&rsquo;s to make the code easily changeable and extensible in such a way that future features are easier to add. This tends to look like standardised patterns so you know exactly how to make changes to extend the code without stepping on each others toes. By following uncle Bob&rsquo;s rules in this book, you can both make code easy to understand, but also easy to change.</p>
<p>The final chapters taught me that writing clean code is not something you get right the first time. It&rsquo;s like writing an article or blog post, you have multiple stages of drafts, and over time the output condenses into something legible.</p>
<p>The book shows a systematic way go from idea to messy code, then from messy code to refactored clean code (iteratively). To do this, automated unit tests are a requirement. OOP helps make testing easier by abstracting away key components of the system.</p>
<h2 id="quotes" class="group relative scroll-mt-20">
  Quotes
  <a
    href="#quotes"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<h3 id="foreword" class="group relative scroll-mt-20">
  Foreword
  <a
    href="#foreword"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Clean code honors the deep roots of wisdom beneath our broader culture, or our culture as it once was, or should be, and can be with attentiveness to detail.</p>
</blockquote>
<blockquote>
<p>It is a recommended practice in Scrum that re-factoring be part of the concept of “Done.” Neither architecture nor clean code insist on perfection, only on honesty and doing the best we can. To err is human; to forgive, divine. In Scrum, we make everything visible. We air our dirty laundry. We are honest about the state of our code because code is never perfect.</p>
</blockquote>
<h3 id="chapter-1-clean-code" class="group relative scroll-mt-20">
  Chapter 1 Clean Code
  <a
    href="#chapter-1-clean-code"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>Yo&hellip; AI? It&rsquo;s not there yet, not sure if it will ever be.</p>
<blockquote>
<p>They are hoping that one day we will discover a way to create machines that can do what we want rather than what we say. These machines will have to be able to understand us so well that they can translate vaguely specified needs into perfectly executing programs that precisely meet those needs.</p>
</blockquote>
<p>Truth. Though I strive to not allow it to be my truth.</p>
<blockquote>
<p>We’ve all said we’d go back and clean it up later. Of course, in those days we didn’t know LeBlanc’s law: Later equals never .</p>
</blockquote>
<p>I worked somewhere stuck in this same situation, it&rsquo;s so refreshing to know that 1) it wasn&rsquo;t a unique experience to me, and 2) that there are ways around it.</p>
<blockquote>
<p>Now the two teams are in a race. The tiger team must build a new system that does everything that the old system does. Not only that, they have to keep up with the changes that are continuously being made to the old system. Management will not replace the old system until the new system can do everything that the old system does. This race can go on for a very long time. I’ve seen it take 10 years. And by the time it’s done, the original members of the tiger team are long gone, and the current members are demanding that the new system be redesigned because it’s such a mess.</p>
</blockquote>
<blockquote>
<p>You will not make the deadline by making the mess. Indeed, the mess will slow you down instantly, and will force you to miss the deadline. The only way to make the deadline—the only way to go fast—is to keep the code as clean as possible at all times.</p>
</blockquote>
<blockquote>
<p>Bjarne Stroustrup, inventor of C + + and author of The C + + Programming Language</p>
</blockquote>
<blockquote>
<p>But don’t make the mistake of thinking that we are somehow “right” in any absolute sense. There are other schools and other masters that have just as much claim to professionalism as we. It would behoove you to learn from them as well.</p>
</blockquote>
<blockquote>
<p>We are authors. And one thing about authors is that they have readers. Indeed, authors are responsible for communicating well with their readers. The next time you write a line of code, remember you are an author, writing for readers who will judge your effort.</p>
</blockquote>
<blockquote>
<p>It’s not enough to write the code well. The code has to be kept clean over time. We’ve all seen code rot and degrade as time passes. So we must take an active role in preventing this degradation.</p>
</blockquote>
<h3 id="chapter-2-meningful-names" class="group relative scroll-mt-20">
  Chapter 2 Meningful Names
  <a
    href="#chapter-2-meningful-names"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>The name of a variable, function, or class, should answer all the big questions. It should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.</p>
</blockquote>
<blockquote>
<p>Classes and objects should have noun or noun phrase names like Customer , WikiPage , Account , and AddressParser . Avoid words like Manager , Processor , Data , or Info in the name of a class. A class name should not be a verb.</p>
</blockquote>
<blockquote>
<p>Single-letter variable names are only acceptable as loop counters. Everything else deserves better.</p>
</blockquote>
<blockquote>
<p>The name AccountVisitor means a great deal to a programmer who is familiar with the VISITOR pattern. What programmer would not know what a JobQueue was? There are lots of very technical things that programmers have to do. Choosing technical names for those things is usually the most appropriate course.</p>
</blockquote>
<h3 id="chapter-3-functions" class="group relative scroll-mt-20">
  Chapter 3 Functions
  <a
    href="#chapter-3-functions"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Functions that do one thing cannot be reasonably divided into sections.
&hellip;
To say this differently, we want to be able to read the program as though it were a set of TO paragraphs, each of which is describing the current level of abstraction and referencing subsequent TO paragraphs at the next level down. To include the setups and teardowns, we include setups, then we include the test page content, and then we include the teardowns.</p>
</blockquote>
<blockquote>
<p>The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.</p>
</blockquote>
<blockquote>
<p>Functions should do one thing. They should do it well. They should do it only.
This feels achievable until you&rsquo;re actually in the middle of writing something and you just want to add one more thing because it&rsquo;s almost related.</p>
</blockquote>
<blockquote>
<p>A function with two arguments is harder to understand than a monadic function. For example, writeField( name) is easier to understand than writeField( output-Stream, name). Though the meaning of both is clear, the first glides past the eye, easily depositing its meaning. The second requires a short pause until we learn to ignore the first parameter. And that, of course, eventually results in problems because we should never ignore any part of code. The parts we ignore are where the bugs will hide.</p>
</blockquote>
<blockquote>
<p>Have No Side Effects Side effects are lies. Your function promises to do one thing, but it also does other hidden things. Sometimes it will make unexpected changes to the variables of its own class.</p>
</blockquote>
<blockquote>
<p>In general output arguments should be avoided. If your function must change the state of something, have it change the state of its owning object.</p>
</blockquote>
<blockquote>
<p>On the other hand, if you use exceptions instead of returned error codes, then the error processing code can be separated from the happy path code and can be simplified:</p>
</blockquote>
<h3 id="chapter-4-comments" class="group relative scroll-mt-20">
  Chapter 4 Comments
  <a
    href="#chapter-4-comments"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>So when you find yourself in a position where you need to write a comment, think it through and see whether there isn’t some way to turn the tables and express yourself in code.</p>
</blockquote>
<blockquote>
<p>Others who see that commented-out code won’t have the courage to delete it. They’ll think it is there for a reason and is too important to delete.</p>
</blockquote>
<blockquote>
<p>Every time you write a comment, you should grimace and feel the failure of your ability of expression.</p>
</blockquote>
<h3 id="chapter-5-formatting" class="group relative scroll-mt-20">
  Chapter 5 Formatting
  <a
    href="#chapter-5-formatting"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>We use horizontal white space to associate things that are strongly related and disassociate things that are more weakly related.</p>
</blockquote>
<h3 id="chapter-6-objects-and-data-structures" class="group relative scroll-mt-20">
  Chapter 6 Objects and Data Structures
  <a
    href="#chapter-6-objects-and-data-structures"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>In any complex system there are going to be times when we want to add new data types rather than new functions. For these cases objects and OO are most appropriate. On the other hand, there will also be times when we’ll want to add new functions as opposed to data types. In that case procedural code and data structures will be more appropriate.</p>
</blockquote>
<blockquote>
<p>More precisely, the Law of Demeter says that a method f of a class C should only call the methods of these: • C • An object created by f • An object passed as an argument to f • An object held in an instance variable of C The method should not invoke methods on objects that are returned by any of the allowed functions.</p>
</blockquote>
<p>I wrote a note here to look more into procedural vs oop methodoloies and programming.</p>
<blockquote>
<p>They have functions that do significant things, and they also have either public variables or public accessors and mutators that, for all intents and purposes, make the private variables public, tempting other external functions to use those variables the way a procedural program would use a data structure.</p>
</blockquote>
<p>The quasi-encapsulation of beans seems to make some OO purists feel better but usually provides no other benefit.</p>
<h3 id="chapter-7-use-exceptions-rather-than-return-codes" class="group relative scroll-mt-20">
  Chapter 7 Use Exceptions Rather Than Return Codes
  <a
    href="#chapter-7-use-exceptions-rather-than-return-codes"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>Use Exceptions Rather Than Return Codes</p>
</blockquote>
<blockquote>
<p>Create informative error messages and pass them along with your exceptions. Mention the operation that failed and the type of failure. If you are logging in your application, pass along enough information to be able to log the error in your catch.</p>
</blockquote>
<blockquote>
<p>It’s easy to say that the problem with the code above is that it is missing a null check, but in actuality, the problem is that it has too many. If you are tempted to return null from a method, consider throwing an exception or returning a SPECIAL CASE object instead.</p>
</blockquote>
<h3 id="chapter-8-boundaries" class="group relative scroll-mt-20">
  Chapter 8 Boundaries
  <a
    href="#chapter-8-boundaries"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>If you use a boundary interface like Map, keep it inside the class, or close family of classes, where it is used. Avoid returning it from, or accepting it as an argument to, public APIs.</p>
</blockquote>
<p>In learning tests we call the third-party API, as we expect to use it in our application. We’re essentially doing controlled experiments that check our understanding of that API. The tests focus on what we want out of the API.</p>
<p>We manage third-party boundaries by having very few places in the code that refer to them. We may wrap them as we did with Map , or we may use an ADAPTER to convert from our perfect interface to the provided interface. Either way our code speaks to us better, promotes internally consistent usage across the boundary, and has fewer maintenance points when the third-party code changes.</p>
<h3 id="chapter-9-unit-tests" class="group relative scroll-mt-20">
  Chapter 9 Unit Tests
  <a
    href="#chapter-9-unit-tests"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>in the mad rush to add testing to our discipline, many programmers have missed some of the more subtle, and important, points of writing good tests.</p>
<p>First Law You may not write production code until you have written a failing unit test. Second Law You may not write more of a unit test than is sufficient to fail, and not compiling is failing. Third Law You may not write more production code than is sufficient to pass the currently failing test.</p>
<p>The moral of the story is simple: Test code is just as important as production code. It is not a second-class citizen. It requires thought, design, and care. It must be kept as clean as production code.</p>
<p>That is the nature of the dual standard. There are things that you might never do in a production environment that are perfectly fine in a test environment. Usually they involve issues of memory or CPU efficiency. But they never involve issues of cleanliness.</p>
<p>Timely The tests need to be written in a timely fashion. Unit tests should be written just before the production code that makes them pass. If you write tests after the production code, then you may find the production code to be hard to test. You may decide that some production code is too hard to test. You may not design the production code to be testable.</p>
<blockquote>
<p>Clean boundaries. Code at the boundaries needs clear separation and tests that define expectations.
There&rsquo;s a whole class of bugs that only exist because the assumptions on one side of an API boundary were never written down anywhere. Tests are the written-down version.</p>
</blockquote>
<h3 id="chapter-10-classes" class="group relative scroll-mt-20">
  Chapter 10 Classes
  <a
    href="#chapter-10-classes"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>There is seldom a good reason to have a public variable. Public functions should follow the list of variables. We like to put the private utilities called by a public function right after the public function itself. This follows the stepdown rule and helps the program read like a newspaper article.</p>
<p>With functions we measured size by counting physical lines. With classes we use a different measure. We count responsibilities</p>
<p>The problem is that too many of us think that we are done once the program works. We fail to switch to the other concern of organization and cleanliness. We move on to the next problem rather than going back and breaking the overstuffed classes into decoupled units with single responsibilities.</p>
<p>many developers fear that a large number of small, single-purpose classes makes it more difficult to understand the bigger picture. They are concerned that they must navigate from class to class in order to figure out how a larger piece of work gets accomplished. However, a system with many small classes has no more moving parts than a system with a few large classes. There is just as much to learn in the system with a few large classes. So the question is: Do you want your tools organized into toolboxes with many small drawers each containing well-defined and well-labeled components? Or do you want a few drawers that you just toss everything into?</p>
<p>To restate the former points for emphasis: We want our systems to be composed of many small classes, not a few large ones.</p>
<p>We learned in OO 101 that there are concrete classes, which contain implementation details (code), and abstract classes, which represent concepts only.</p>
<h3 id="chapter-11-systems" class="group relative scroll-mt-20">
  Chapter 11 Systems
  <a
    href="#chapter-11-systems"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>Domain-Specific Languages allow all levels of abstraction and all domains in the application to be expressed as POJOs, from high-level policy to low-level details.</p>
<p>Whether you are designing systems or individual modules, never forget to use the simplest thing that can possibly work .</p>
<h3 id="chapter-12-emergence" class="group relative scroll-mt-20">
  Chapter 12 Emergence
  <a
    href="#chapter-12-emergence"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>As systems become more complex, they take more and more time for a developer to understand, and there is an ever greater opportunity for a misunderstanding. Therefore, code should clearly express the intent of its author. The clearer the author can make the code, the less time others will have to spend understanding it. This will reduce defects and shrink the cost of maintenance. You can express yourself by choosing good names. We want to be able to hear a class or function name and not be surprised when we discover its responsibilities. You can also express yourself by keeping your functions and classes small. Small classes and functions are usually easy to name, easy to write, and easy to understand.</p>
<h3 id="chapter-13-concurrency" class="group relative scroll-mt-20">
  Chapter 13 Concurrency
  <a
    href="#chapter-13-concurrency"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>Recommendation : Write tests that have the potential to expose problems and then run them frequently, with different programatic configurations and system configurations and load. If tests ever fail, track down the failure. Don’t ignore a failure just because the tests pass on a subsequent run.</p>
<p>Recommendation : Run your threaded code on all target platforms early and often.</p>
<p>The point is to jiggle the code so that threads run in different orderings at different times. The combination of well-written tests and jiggling can dramatically increase the chance finding errors.</p>
<h3 id="chapter-14-junit-internals" class="group relative scroll-mt-20">
  Chapter 14 JUnit Internals
  <a
    href="#chapter-14-junit-internals"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>I don’t like the way that the last two lines of the new function return variables, but the first two don’t. They aren’t using consistent conventions [G11]. So we should change findCommonPrefix and findCommonSuffix to return the prefix and suffix values.</p>
<h3 id="chapter-16-refactoring-serialdate" class="group relative scroll-mt-20">
  Chapter 16 Refactoring SerialDate
  <a
    href="#chapter-16-refactoring-serialdate"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>This chapter was hard to read on my ereader, like it took a bit of time to disect what was happening. But the explanations that walked you through his thinking when refactoring was SO GOOD. There are probably Youtubers who walk through code like this too, giving me a new angle and new lease on thinking about writing good code.</p>
<blockquote>
<p>It’s generally a bad idea for base classes to know about their derivatives. To fix this, we should use the ABSTRACT FACTORY 3 pattern and create a DayDateFactory . This factory will create the instances of DayDate that we need and can also answer questions about the implementation, such as the maximum and minimum dates.</p>
</blockquote>
<blockquote>
<p>The next abstract method is toDate (lines 838–844). It converts a DayDate to a java.util.Date . Why is this method abstract? If we look at its implementation in SpreadsheetDate (lines 198–207, Listing B-5 , page 382 ), we see that it doesn’t depend on anything in the implementation of that class [G6]. So I pushed it up.</p>
</blockquote>
<h3 id="chapter-17-smells-and-heuristics" class="group relative scroll-mt-20">
  Chapter 17 Smells and Heuristics
  <a
    href="#chapter-17-smells-and-heuristics"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>All else being equal, we want to eliminate Feature Envy because it exposes the internals of one class to another. Sometimes, however, Feature Envy is a necessary evil.</p>
<p>This is a great example of how to make code more readable by avoiding selector arguments. With selector arguments we break the single responsibility principle, which is bad because it makes the code harder to understand.</p>
<blockquote>
<p>There is hardly anything more abominable than a dangling false argument at the end of a function call. What does it mean? What would it change if it were true ? Not only is the purpose of a selector argument difficult to remember, each selector argument combines many functions into one.</p>
</blockquote>
<p>I like this, I often get troubled with reading boundaries for <code>++</code> or <code>-=1</code> and sometimes result to trial and error.</p>
<blockquote>
<p>Notice that level + 1 appears twice. This is a boundary condition that should be encapsulated within a variable named something like nextLevel . int nextLevel = level + 1; if( nextLevel &lt; tags.length) { parts = new Parse( body, tags, nextLevel, offset + endTag); body = null; }</p>
</blockquote>
<blockquote>
<p>More specifically, if A collaborates with B , and B collaborates with C , we don’t want modules that use A to know about C . (For example, we don’t want a.getB(). getC(). doSomething() ;.) This is sometimes called the Law of Demeter. The Pragmatic Programmers call it “Writing Shy Code.” 12 In</p>
</blockquote>
<blockquote>
<p>Rather we want our immediate collaborators to offer all the services we need. We should not have to roam through the object graph of the system, hunting for the method we want to call. Rather we should simply be able to say: myCollaborator.doSomething().</p>
</blockquote>
<blockquote>
<p>J2: Don’t Inherit Constants I have seen this several times and it always makes me grimace. A programmer puts some constants in an interface and then gains access to those constants by inheriting that interface.</p>
</blockquote>
<blockquote>
<p>T6: Exhaustively Test Near Bugs Bugs tend to congregate. When you find a bug in a function, it is wise to do an exhaustive test of that function. You’ll probably find that the bug was not alone.</p>
</blockquote>
<h2 id="appendix-a-concurrency-ii" class="group relative scroll-mt-20">
  Appendix A Concurrency II
  <a
    href="#appendix-a-concurrency-ii"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>On evaluating and understanding how complex async programming becomes.</p>
<blockquote>
<p>If the code is processor bound, more processing hardware can improve throughput, making our test pass. But there are only so many CPU cycles available, so adding threads to a processor-bound problem will not make it go faster. On the other hand, if the process is I/ O bound, then concurrency can increase efficiency. When one part of the system is waiting for I/ O, another part can use that wait time to process something else,</p>
</blockquote>
<blockquote>
<p>And this is a simple problem. If we cannot demonstrate broken code easily with this problem, how will we ever detect truly complex problems? So what approaches can we take to demonstrate this simple failure? And, more importantly, how can we write tests that will demonstrate failures in more complex code? How will we be able to discover if our code has failures when we do not know where to look?</p>
</blockquote>
]]></content:encoded></item><item><title>Tools &amp; Tech</title><link>https://westernwilson.com/techstack/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/techstack/</guid><description>&lt;p&gt;A snapshot of the tools, languages, and tech I&amp;rsquo;m currently using or exploring. For what I&amp;rsquo;m up to right now, see my &lt;a href="https://westernwilson.com/now/"&gt;/now&lt;/a&gt; page.&lt;/p&gt;&#10;&lt;h2 id="hardware--software-of-choice" class="group relative scroll-mt-20"&gt;&#10; Hardware &amp;amp; software of choice&#10; &lt;a&#10; href="#hardware--software-of-choice"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Macbook Pro 16&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://anytype.io"&gt;AnyType&lt;/a&gt; for personal notes&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://claude.ai"&gt;Claude&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.flow.app"&gt;Flow&lt;/a&gt; for pomodoro timing&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://arc.net"&gt;Arc&lt;/a&gt; browser&lt;/li&gt;&#10;&lt;li&gt;Kindle ereader (11th Gen Paperwhite)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="technical-stack" class="group relative scroll-mt-20"&gt;&#10; Technical Stack&#10; &lt;a&#10; href="#technical-stack"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Cloud &amp;amp; Infrastructure:&lt;/strong&gt; &lt;a href="https://aws.amazon.com"&gt;AWS&lt;/a&gt; (&lt;a href="https://aws.amazon.com/eks/"&gt;EKS&lt;/a&gt;, &lt;a href="https://aws.amazon.com/iam/"&gt;IAM&lt;/a&gt;, &lt;a href="https://aws.amazon.com/rds/"&gt;RDS&lt;/a&gt;), &lt;a href="https://developer.hashicorp.com/terraform"&gt;Terraform&lt;/a&gt;, &lt;a href="https://www.docker.com"&gt;Docker&lt;/a&gt;, &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt;, &lt;a href="https://helm.sh"&gt;Helm&lt;/a&gt;, &lt;a href="https://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;, &lt;a href="https://www.openstack.org"&gt;OpenStack&lt;/a&gt;, &lt;a href="https://cloud.google.com"&gt;GCP&lt;/a&gt; (wanting to learn)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;CI/CD&lt;/strong&gt;: &lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt;, &lt;a href="https://www.jetbrains.com/teamcity/"&gt;TeamCity&lt;/a&gt;, &lt;a href="https://octopus.com"&gt;Octopus Deploy&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Languages&lt;/strong&gt;: &lt;a href="https://www.python.org"&gt;Python&lt;/a&gt; (&lt;a href="https://fastapi.tiangolo.com"&gt;FastAPI&lt;/a&gt;, &lt;a href="https://flask.palletsprojects.com"&gt;Flask&lt;/a&gt;), &lt;a href="https://www.gnu.org/software/bash/"&gt;Bash&lt;/a&gt;, &lt;a href="https://go.dev"&gt;Go&lt;/a&gt; (learning), some C# &amp;amp; Java&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Observability:&lt;/strong&gt; &lt;a href="https://newrelic.com"&gt;New Relic&lt;/a&gt;, &lt;a href="https://www.sumologic.com/"&gt;Sumo Logic&lt;/a&gt;, &lt;a href="https://prometheus.io"&gt;Prometheus&lt;/a&gt;, &lt;a href="https://grafana.com"&gt;Grafana&lt;/a&gt;, &lt;a href="https://www.elastic.co/kibana"&gt;Kibana / ELK&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Practices&lt;/strong&gt;: REST API Design • Infrastructure as Code • Observability • Security Controls • Agile/Scrum&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;Also worked with:&lt;/strong&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>A snapshot of the tools, languages, and tech I&rsquo;m currently using or exploring. For what I&rsquo;m up to right now, see my <a href="/now/">/now</a> page.</p>
<h2 id="hardware--software-of-choice" class="group relative scroll-mt-20">
  Hardware &amp; software of choice
  <a
    href="#hardware--software-of-choice"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<ul>
<li>Macbook Pro 16</li>
<li><a href="https://anytype.io">AnyType</a> for personal notes</li>
<li><a href="https://claude.ai">Claude</a></li>
<li><a href="https://www.flow.app">Flow</a> for pomodoro timing</li>
<li><a href="https://arc.net">Arc</a> browser</li>
<li>Kindle ereader (11th Gen Paperwhite)</li>
</ul>
<h2 id="technical-stack" class="group relative scroll-mt-20">
  Technical Stack
  <a
    href="#technical-stack"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<ul>
<li><strong>Cloud &amp; Infrastructure:</strong> <a href="https://aws.amazon.com">AWS</a> (<a href="https://aws.amazon.com/eks/">EKS</a>, <a href="https://aws.amazon.com/iam/">IAM</a>, <a href="https://aws.amazon.com/rds/">RDS</a>), <a href="https://developer.hashicorp.com/terraform">Terraform</a>, <a href="https://www.docker.com">Docker</a>, <a href="https://kubernetes.io">Kubernetes</a>, <a href="https://helm.sh">Helm</a>, <a href="https://www.postgresql.org">PostgreSQL</a>, <a href="https://www.openstack.org">OpenStack</a>, <a href="https://cloud.google.com">GCP</a> (wanting to learn)</li>
<li><strong>CI/CD</strong>: <a href="https://github.com/features/actions">GitHub Actions</a>, <a href="https://www.jetbrains.com/teamcity/">TeamCity</a>, <a href="https://octopus.com">Octopus Deploy</a></li>
<li><strong>Languages</strong>: <a href="https://www.python.org">Python</a> (<a href="https://fastapi.tiangolo.com">FastAPI</a>, <a href="https://flask.palletsprojects.com">Flask</a>), <a href="https://www.gnu.org/software/bash/">Bash</a>, <a href="https://go.dev">Go</a> (learning), some C# &amp; Java</li>
<li><strong>Observability:</strong> <a href="https://newrelic.com">New Relic</a>, <a href="https://www.sumologic.com/">Sumo Logic</a>, <a href="https://prometheus.io">Prometheus</a>, <a href="https://grafana.com">Grafana</a>, <a href="https://www.elastic.co/kibana">Kibana / ELK</a></li>
<li><strong>Practices</strong>: REST API Design • Infrastructure as Code • Observability • Security Controls • Agile/Scrum</li>
</ul>
<p><strong>Also worked with:</strong></p>
<ul>
<li><strong>Frontend:</strong> <a href="https://vuejs.org">Vue.js</a> (<a href="https://nuxt.com">Nuxt</a>), <a href="https://react.dev">React</a> (<a href="https://nextjs.org">Next.js</a>), <a href="https://tailwindcss.com">Tailwind CSS</a>, <a href="https://gohugo.io">Hugo</a></li>
<li><strong>ML basics</strong>: <a href="https://developers.google.com/machine-learning/crash-course">Google&rsquo;s MLCC</a> with hands-on experience using <a href="https://pandas.pydata.org">Pandas</a>, <a href="https://numpy.org">NumPy</a>, <a href="https://www.tensorflow.org">TensorFlow</a></li>
</ul>
]]></content:encoded></item><item><title>Travelling Europe and the Balkans</title><link>https://westernwilson.com/reflections/travelling-europe-and-the-balkans/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/travelling-europe-and-the-balkans/</guid><description>&lt;p&gt;I&amp;rsquo;ve visited a bunch of countries across Europe and the Balkans recently. Mid last year did loads of Asia too. This all probably deserves its own deeper dive, but I don&amp;rsquo;t want to sink a load of time into this reflection just yet so here is a brain dump.&lt;/p&gt;&#10;&lt;p&gt;A highlight has been hearing the difference between Germanic, Slavic, and Romance languages up close. Back home in NZ I mostly heard English, some Cantonese, and a little Māori, so all these new sounds have been a great for my brain.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ve visited a bunch of countries across Europe and the Balkans recently. Mid last year did loads of Asia too. This all probably deserves its own deeper dive, but I don&rsquo;t want to sink a load of time into this reflection just yet so here is a brain dump.</p>
<p>A highlight has been hearing the difference between Germanic, Slavic, and Romance languages up close. Back home in NZ I mostly heard English, some Cantonese, and a little Māori, so all these new sounds have been a great for my brain.</p>
<p>The food everywhere has been amazing too, nothing crazy adventurous, but plenty to satisfy the palate.</p>
<ul>
<li><strong>Greece</strong>: Island hopping was fantastic, friendly people, and such a rich history. We toured the major islands and timed Mykonos perfectly, just before party season kicked off. We had room to walk and explore without the crowds. Saw a lot of churches and heard about the various empires that occupied these parts.</li>
<li><strong>Denmark</strong>: Beautiful but expensive. Everyone wears gorgeous, sustainable, layered clothing. This makes sense given how much colder it is than NZ. Metro made it so easy to get around. Bikes everywhere.</li>
<li><strong>Amsterdam</strong>: Visited to explore Amsterdam and also for a bunq hackathon (<a href="/reflections/bunq-hackathon-7">reflection here</a>). I loved the vibe, though getting around without a bike was slow. So a bike is the first thing I&rsquo;m getting once I live there. Free ferries were fantastic!</li>
<li><strong>Italy</strong>: Five days in Rome. It felt like you could walk in any direction and stumble onto a grand church or building. The food here was the best I&rsquo;ve had anywhere, once we found one particular shop&rsquo;s tiramisu we ate three in a single day. Also saw Michelangelo&rsquo;s Creation of Adam on the Sistine Chapel ceiling, the scale of it is hard to describe. I guess it was well over 4 years of his life!</li>
<li><strong>Croatia</strong>: Some of the best scenery I&rsquo;ve seen outside of New Zealand. We hired a car and drove from Zagreb to Split, stopping at the Plitvice Lakes National Park in between for some incredible waterfalls. Driving on the opposite side of the road took adjusting (NZ left-side and Croatia right-side), but having a car unlocked so many random stops for our journey.</li>
<li><strong>Serbia</strong>: A taxi driver suggest Burek to me and I ate it most days. I learnt about Belgrade&rsquo;s hectic history, even up until recently. I&rsquo;m in awe of what people there have survived, over four passport changes in the last 25 years and across many different occupations. We were told it&rsquo;s because of Belgrade is positioned on the Danube and Sava rivers, prime real estate with a fortress that holds the high ground.</li>
</ul>
]]></content:encoded></item><item><title>DevOps vs Platform vs Infrastructure Engineer</title><link>https://westernwilson.com/reflections/devops-vs-platform-vs-infrastructure-engineer/</link><pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/devops-vs-platform-vs-infrastructure-engineer/</guid><description>&lt;p&gt;I&amp;rsquo;ve been a DevOps &amp;amp; Infra Engineer for over 5 years. To me, it&amp;rsquo;s about helping developers move from code to production in the fastest, safest, most secure, and most reliable way possible. The job market seems a bit less certain about this definition however&amp;hellip;&lt;/p&gt;&#10;&lt;p&gt;Scrolling through job listings this past month, I noticed roles titled &lt;em&gt;DevOps Engineer&lt;/em&gt; or &lt;em&gt;Platform Engineer&lt;/em&gt; or &lt;em&gt;Infrastructure Engineer&lt;/em&gt; that were listed with almost identical responsibilities.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ve been a DevOps &amp; Infra Engineer for over 5 years. To me, it&rsquo;s about helping developers move from code to production in the fastest, safest, most secure, and most reliable way possible. The job market seems a bit less certain about this definition however&hellip;</p>
<p>Scrolling through job listings this past month, I noticed roles titled <em>DevOps Engineer</em> or <em>Platform Engineer</em> or <em>Infrastructure Engineer</em> that were listed with almost identical responsibilities.</p>
<p>I know innately that these roles have some differences, but the titles are often used interchangeably by hiring teams.</p>
<h2 id="where-devops-came-from" class="group relative scroll-mt-20">
  Where DevOps came from
  <a
    href="#where-devops-came-from"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>DevOps is a word created from the combination of the words <strong>Developer</strong> and <strong>Operations</strong>. To understand why that portmanteau word became a job title, you need to understand the problem it was trying to solve.</p>
<p>Traditionally, software teams were split in two. Developers wrote code in one room, and an <strong>Operations</strong> team —sometimes called Ops— deployed and ran that code in production. Each group had different incentives e.g. devs wanted to ship fast whilst ops wanted stability. That tension caused friction, slow releases, and potentially a blame culture when things broke.</p>
<p>The DevOps movement, popularised in part by <em>The Phoenix Project</em> (Kim, Behr, Spafford, 2013) and <em>The DevOps Handbook</em>, proposed a cultural fix to tear down that wall. If developers owned their code all the way to production, they&rsquo;d build with operability in mind. They&rsquo;d care about deployment pipelines, alerting, and recovery because those things would become their problem too.</p>
<p>DevOps is meant to be a shared practice of principles across teams, and not a single person&rsquo;s role. But organisations found it easier to spin up a DevOps teams or hire someone with the DevOps title who embodied that DevOps mindset to show some adoption of &ldquo;DevOps&rdquo; when it was first cool.</p>
<h2 id="what-a-devops-engineer-actually-does" class="group relative scroll-mt-20">
  What a DevOps Engineer actually does
  <a
    href="#what-a-devops-engineer-actually-does"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>In practice, the <strong>DevOps Engineer</strong> tends to be embedded within a product team, sometimes as the only person with that focus. Their job is improving the speed and safety of getting code from a developer&rsquo;s machine into production.</p>
<p>That means CI/CD pipelines (think GitHub Actions, TeamCity, CircleCI, or similar), automated testing, deployment automation, monitoring and alerting, and security gates. If a developer is waiting too long for a build, or a deployment is a manual, error-prone process, the DevOps Engineer is the one called on.</p>
<p>My own experience working in this role felt exactly like that: a solo DevOps expert embedded in a development team, focused on making the production path faster and more reliable. In smaller companies you will probably get the opportunity to pick up other development tasks outside of the usual job description. This helps you experience the pain that engineers go through to build better solutions.</p>
<h2 id="platform-engineering" class="group relative scroll-mt-20">
  Platform Engineering
  <a
    href="#platform-engineering"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Platform Engineering has similar tools and similar outcomes, but from what I&rsquo;ve seen, the scope and audience are different.</p>
<p>Where a DevOps Engineer typically serves one team, a <strong>Platform Engineer</strong> serves many. This role, I think, is oriented around building an Internal Developer Platform (IDP). An IDP is a curated set of tools, templates, and standards that every engineering team in an organisation can use. The CNCF communities often describe this as building <strong>golden paths</strong>: opinionated, well-maintained routes that make the right thing the easy thing for developers to do.</p>
<p>Think of it as the difference between solving a problem for your team versus building the infrastructure so any team can solve that problem themselves. Platform teams treat internal developers as their customers. They build self-service tooling —things like standardised deployment workflows, observability stacks, or secrets management patterns— so that product teams don&rsquo;t have to reinvent those wheels independently.</p>
<p>This tends to appear at larger organisations, where inconsistency across teams becomes a bigger cost. At a company with ten engineers, one strong DevOps person can handle everything informally. At a company with five hundred, the absence of shared standards can create chaos.</p>
<p>The tradeoff here is somehow creating efficient standards to keep things stable, but also giving optionality to engineers so they have autonomy over the tooling they choose.</p>
<p>A good tool to know about is Spotify&rsquo;s open source Backstage tool: <a href="https://backstage.io/">https://backstage.io/</a></p>
<h2 id="infrastructure-engineering" class="group relative scroll-mt-20">
  Infrastructure Engineering
  <a
    href="#infrastructure-engineering"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Infrastructure Engineering overlaps with both DevOps and Platform, but sits slightly further from application code in my experience.</p>
<p>An <strong>Infrastructure Engineer</strong> is typically focused on the foundations: cloud architecture, networking, access management, and the security controls that sit beneath the application layer. They&rsquo;re thinking about VPCs, subnets, firewall rules, load balancers, DNS, and how services communicate across boundaries. Often this involves deep expertise with IaC tools like Terraform and close familiarity with the cloud provider&rsquo;s lower-level primitives.</p>
<p>A DevOps Engineer might spend their day modifying a GitHub Actions workflow or adjusting a Dockerfile. An Infrastructure Engineer might spend theirs designing the network topology that those pipelines eventually deploy into.</p>
<p>That said, in my experience, in any organisation you have problems to solve and there is never a clean separation. So you&rsquo;re owning all of it, and carrying all three titles simultaneously whenever something is required.</p>
<h2 id="why-the-roles-overlap" class="group relative scroll-mt-20">
  Why the roles overlap
  <a
    href="#why-the-roles-overlap"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>There are more than the three role titles I&rsquo;ve talked about too, e.g. Systems Engineer, Site Reliability Engineer, Integration Engineer, and many others that cover <em>similar</em> responsibilities with decent overlap.</p>
<p>Part of it is because fashionable titles shift over time; DevOps was the go-to term but now it feels like Platform Engineering is. This is probably because it better captures the product-focused, team-of-teams thinking that scales for devops/platform teams.</p>
<p>Each organisation decides on responsibilities differently depending on team size, product maturity, culture in the industry, and even the background of whoever&rsquo;s hiring. So the title they&rsquo;re given is essentially a best guess based on responsibilities required at the start of the role, which often morphs into different roles as projects progress.</p>
<p>A startup with five engineers often doesn&rsquo;t have the headcount for specialisation. The person managing Terraform, building the deployment pipeline, and configuring the VPC is doing DevOps, platform, and infrastructure work all at once.</p>
<p>At larger companies, the separation becomes more meaningful. A Platform team with a product mindset and a roadmap looks very different from a DevOps Engineer embedded in a squad, even if both are deep into Kubernetes on any given Tuesday.</p>
<h2 id="read-the-job-description" class="group relative scroll-mt-20">
  Read the job description
  <a
    href="#read-the-job-description"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>After reading many applications, the practical lesson is to read past the title and into the responsibilities. A &ldquo;Platform Engineer&rdquo; role at a thirty-person startup will look nothing like the same title at an enterprise company. A &ldquo;DevOps Engineer&rdquo; role at a bank might be closer to what a Platform team does at a tech company.</p>
<p>The questions worth asking in an interview: Are you embedded in a product team or sitting in a centralised infrastructure group? Are you building tooling <em>for</em> developers, or helping productionise a single product app? What does ownership look like when something breaks at 3am?</p>
<p>I like that DevOps Engineering is broad; I like having the option to do whatever is required to get the job done.</p>
]]></content:encoded></item><item><title>Why AWS SSO? Why OIDC?</title><link>https://westernwilson.com/reflections/oidc/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/oidc/</guid><description>&lt;p&gt;While building a demo coffee app you realise that the infrastructure-as-code needs appropriate authentication &amp;amp; authorisation to make changes securely. I&amp;rsquo;ve set this up in previous jobs but have been on a travel break since then, and the muscle memory needs a reminder.&lt;/p&gt;&#10;&lt;p&gt;So, I&amp;rsquo;m refreshing my understanding and reflecting on it here.&lt;/p&gt;&#10;&lt;h2 id="important-terminology-and-tools" class="group relative scroll-mt-20"&gt;&#10; Important terminology and tools&#10; &lt;a&#10; href="#important-terminology-and-tools"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;An important distiction is the difference between Authentication and Authorisation. &lt;strong&gt;Authentication&lt;/strong&gt; asks &amp;ldquo;&lt;em&gt;are you who you say you are&lt;/em&gt;&amp;rdquo;, while &lt;strong&gt;Authorisation&lt;/strong&gt; says &amp;ldquo;&lt;em&gt;I know you, but are you allowed to do what you&amp;rsquo;re trying to do&lt;/em&gt;&amp;rdquo;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>While building a demo coffee app you realise that the infrastructure-as-code needs appropriate authentication &amp; authorisation to make changes securely. I&rsquo;ve set this up in previous jobs but have been on a travel break since then, and the muscle memory needs a reminder.</p>
<p>So, I&rsquo;m refreshing my understanding and reflecting on it here.</p>
<h2 id="important-terminology-and-tools" class="group relative scroll-mt-20">
  Important terminology and tools
  <a
    href="#important-terminology-and-tools"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>An important distiction is the difference between Authentication and Authorisation. <strong>Authentication</strong> asks &ldquo;<em>are you who you say you are</em>&rdquo;, while <strong>Authorisation</strong> says &ldquo;<em>I know you, but are you allowed to do what you&rsquo;re trying to do</em>&rdquo;.</p>
<p><strong>Infrastructure-as-code (IaC)</strong> is self-explanatory, it allows you to codify your infrastructure changes. Letting you track infra changes over time, and efficiently keep the infra actually deployed in line with your intentions. When there is drift between intentions and actual, IaC helps it realign.</p>
<p><strong>Terraform</strong> is a great tool for IAC, and is the focus for this reflection.</p>
<p>There are many other tools that help with IaC including <strong>CloudFormation</strong> and <strong>Ansible</strong>. <strong>CloudFormation</strong> is AWS&rsquo;s solution for the IaC giving declarative provisioning, but its locked to AWS. <strong>Ansible</strong> is more focused on configuration management and orchestration once infrastructure exists, though there is some overlap with IaC.</p>
<p><strong>CI/CD</strong> is Continuous Integration / Continuous Delivery. It is a mindset with many tools that helps our code build, test, and deploy safely for our users. <strong>GitHub Actions</strong> our CI/CD tool of choice. There are others like TeamCity, Circle CI, Jenkins, Octopus Deploy, GitLab CI etc.</p>
<p>When mentioning <strong>MacBook</strong> it could be any developer machine enabling you to make infra changes by running Terraform.</p>
<p>Given all of this, there are two main use cases:</p>
<ul>
<li><strong><em>Human to machine</em></strong> - Connecting MacBook to AWS</li>
<li><strong><em>Machine to machine</em></strong> - Connecting GitHub Actions to AWS</li>
</ul>
<p>Both use cases need to authenticate and authorise, but they do it using different methods. We&rsquo;ll explore that difference in the upcoming sections.</p>
<h2 id="human-to-machine" class="group relative scroll-mt-20">
  Human to machine
  <a
    href="#human-to-machine"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>When running Terraform from your laptop, the industry standard is to authenticate with <strong>AWS SSO</strong> (rebranded as AWS IAM Identity Center in 2022, though the CLI commands remain the same).</p>
<p>From the terminal you run an aws cli command that opens your browser, asks you to log in using your identity provider (or aws creds), and grabs a short-lived credential for use during your AWS terminal session. It normally expires after 8 hours, but this is configurable. Terraform picks up that short-lived credential and uses it to configure the infrastructure however you&rsquo;ve defined.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span><span style="color:#75715e"># day to day example (assumes sso roles are setup)</span>
</span></span><span style="display:flex;"><span>aws sso login --profile terraform
</span></span><span style="display:flex;"><span>export AWS_PROFILE<span style="color:#f92672">=</span>terraform
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>terraform apply
</span></span></code></pre></div><p>These commands work because there&rsquo;s a human in the loop. Developers can login to AWS, click a button, and complete an MFA prompt. It&rsquo;s verifiable that you are the person at the keyboard.</p>
<p>This is not possible the moment you&rsquo;re out of the loop.</p>
<h2 id="machine-to-machine" class="group relative scroll-mt-20">
  Machine to machine
  <a
    href="#machine-to-machine"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>A GitHub Actions CI/CD pipeline can&rsquo;t easily open a browser or tap an allow button on your phone. It needs to authenticate to AWS without a person in the loop, securely, and without any of the interactive trust mechanisms that make SSO work locally.</p>
<p>The old answer was to generate an AWS access key pair, paste it into GitHub as a secret, and let the pipeline use that to authenticate with AWS. This still works, but it&rsquo;s a liability and no longer recommended. Long-lived access keys, if leaked, can grant authentication to whoever gets them. Unfortunately, because infra work requires a broader set of permissions, losing these keys is a bigger security risk.</p>
<p>Long-lived keys should also be <em>rotated</em> frequently (i.e. rotating means to update the keys). That requires additional reminder scheduling, and if the key is used in many places, the work to update might require a bit more effort.</p>
<p>The better answer is Open ID Connect (OIDC). To understand OIDC, you have to understand JWTs first.</p>
<h2 id="jwts" class="group relative scroll-mt-20">
  JWTs
  <a
    href="#jwts"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>JWT stands for JSON Web Token. It&rsquo;s a compact, self-contained token for passing claims between parties.</p>
<p>A <strong>claim</strong> is a key-value assertion about a subject, e.g. <code>&quot;sub&quot;: &quot;user_123&quot;</code> or <code>&quot;email&quot;: &quot;john@example.com&quot;</code>. The issuer party (AWS or GitHub) is saying: I assert these properties are true about the subject of this token (and that it is valid for the stated audience and time window)</p>
<p>What might be at first surprising, is that a signed JWT isn&rsquo;t encrypted and anyone can read it. Paste a JWT string into <a href="https://jwt.io">jwt.io</a> and you&rsquo;ll see everything inside (with real tokens, keep them out of public tools!).</p>
<p>The point of JWT tokens isn&rsquo;t secrecy, it&rsquo;s integrity. If you trust the issuer&rsquo;s public key, and the signature checks out, then you can trust the claims.</p>
<p>Think of it like a driver&rsquo;s licence, it&rsquo;s contents are not a secret (anyone who physically holds it has the info), but it&rsquo;s validity is trusted because it was created by a trusted authority. Other parties (police, security, the supermarket cashier) recognise it and trust what it <em>claims</em>.</p>
<p>A JWT is a string with three parts separated by dots. Header, payload, and signature.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>&lt;header&gt;.&lt;payload&gt;.&lt;signature&gt;
</span></span></code></pre></div><p>The <em>header</em> says how it was signed. The <em>payload</em> is a set of claims or facts about whoever the token is describing. The <em>signature</em> is the issuer&rsquo;s cryptographic stamp.</p>
<p>E.g.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjdXJpb3VzX3JlYWRlciIsIm5hbWUiOiJZb3UsIHRoZSBjdXJpb3VzIG9uZSIsIm1lc3NhZ2UiOiJUaGV5IHNheSBjdXJpb3NpdHkga2lsbGVkIHRoZSBjYXQsIGJ1dCBpdCB3b24ndCBraWxsIHlvdS4gU3RheSBjdXJpb3VzLiIsImlhdCI6MTc0NjY2MjQwMCwiZXhwIjo5OTk5OTk5OTk5fQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
</span></span></code></pre></div><p>You can read more detail here: <a href="https://www.jwt.io/introduction#what-is-json-web-token">https://www.jwt.io/introduction#what-is-json-web-token</a></p>
<h2 id="oidc" class="group relative scroll-mt-20">
  OIDC
  <a
    href="#oidc"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>OIDC (OpenID Connect) is how JWTs get used for authentication. An identity provider mints a signed JWT about who someone is or what it&rsquo;s doing, and a relying party verifies the token and decides what to do.</p>
<p>Back to the analogy of a JWT and a drivers license. The issuing authority is whoever grants drivers licenses, in New Zealand this would be NZ Transport Agency (NZTA or Waka Kotahi). The relying party, again, could be police, security, or the supermarket cashier.</p>
<p>For your CI/CD, GitHub is the identity provider and AWS is the relying party. GitHub publishes its public keys at a well-known URL. AWS knows where to find them and when the workflow pipeline runs, GitHub mints a JWT describing that specific job (the repo, git branch, the workflow file) and signs it. The pipeline hands the token to AWS, AWS verifies the signature, reads the claims, and decides whether to issue short-lived credentials.</p>
<p>There&rsquo;s no shared secret and no pre-arranged handshake. You declare in your AWS account that you trust GitHub&rsquo;s issuer URL and from that moment, AWS knows how to verify any token GitHub signs.</p>
<p>This grants you authentication, and next it&rsquo;s important to look at the policies that grant authorisation to make changes.</p>
<h2 id="the-trust-policy" class="group relative scroll-mt-20">
  The trust policy
  <a
    href="#the-trust-policy"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>With the authentication and JWT verification mostly handled by AWS &amp; GitHub, you can focus more on deciding which GitHub claims you&rsquo;ll accept.</p>
<p>For example, every JWT GitHub mints contains a <code>sub</code> claim, i.e. the subject</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-zsh" data-lang="zsh"><span style="display:flex;"><span>repo:my-org/my-repo:ref:refs/heads/main
</span></span></code></pre></div><p>That string identifies exactly which repo, branch, or trigger produced the token. Your trust policy in AWS is a set of conditions on those claims. So if you pin the <code>sub</code> claim to a specific repo and your main branch (like the above example), then only that combination can assume the role.</p>
<p>Set up the AWS trust policy too loosely and you replace one risk with another. The biggest mistake is wildcarding the subject <code>repo:*</code>, which means that any repository on GitHub could potentially assume your role. The fix is to only give access to specific repositories that need it, following least privilege access. Most tutorials highlight this point too.</p>
<h2 id="using-github-environments" class="group relative scroll-mt-20">
  Using GitHub environments
  <a
    href="#using-github-environments"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Many tutorials suggest the use of GitHub Environments for production deploys. So you can pin the trust policy to <code>repo:my-org/my-repo:environment:production</code>. You then set up your repo to require manual approval on production deployments, thus adding another layer of protection. Now even a workflow on main can&rsquo;t deploy until a human clicks a button.</p>
<p>For an enterprise team, this makes sense. Multiple people merging code, the reviewer and the deployer might not be the same person. So the approval gate gives you a real second pair of eyes.</p>
<p>For a solo project without actual customers it&rsquo;s optional, but worth knowing the pattern exists for when the team grows.</p>
<h2 id="relearning" class="group relative scroll-mt-20">
  Relearning
  <a
    href="#relearning"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Coming back to OIDC after a year of travel, I noticed new details since the first time round. Especially in terms of trust policies, and <em>why</em> the <code>sub</code> claim specificity matters. Going back to first principles about JWTs and OIDC before touching any config gave that stronger mental model.</p>
<p>If you want to see how someone might configure all of this, check out my repositories here:</p>
<ul>
<li>AWS Foundation SSO &amp; OIDC setup: <a href="https://github.com/wjkw1/aws-foundations">https://github.com/wjkw1/aws-foundations</a></li>
<li>Demo Application: <a href="https://github.com/wjkw1/devops-profile-coffee-card-app-demo">https://github.com/wjkw1/devops-profile-coffee-card-app-demo</a></li>
</ul>
]]></content:encoded></item><item><title>Babel: An Arcane History</title><link>https://westernwilson.com/books/babel-an-arcane-history/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/books/babel-an-arcane-history/</guid><description>&lt;h2 id="notes-on-babel" class="group relative scroll-mt-20"&gt;&#10; Notes on Babel&#10; &lt;a&#10; href="#notes-on-babel"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;This book begins and ends with sadness. It has many ups and downs which helped me feel really invested in the students journey.&lt;/p&gt;&#10;&lt;p&gt;The author moves through the story really quick. It&amp;rsquo;s great to read because it feels like there is no dawdling on useless parts of text or superflous detail. It all moves the plot or develops a character. Such a good author.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="notes-on-babel" class="group relative scroll-mt-20">
  Notes on Babel
  <a
    href="#notes-on-babel"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>This book begins and ends with sadness. It has many ups and downs which helped me feel really invested in the students journey.</p>
<p>The author moves through the story really quick. It&rsquo;s great to read because it feels like there is no dawdling on useless parts of text or superflous detail. It all moves the plot or develops a character. Such a good author.</p>
<p>It reminds me of Derek Sivers and his ethos about reducing and refining the texts he writes into their most condensed form. It makes his writing great to read too, because it shifts quickly and captures big ideas in a small amount of text.</p>
<p>I drew parallels with the plot of this book and the age of AI we are currently living through. Yes it&rsquo;s written in a time near the industrial revolution but the silverworking automations are more like magic (which in some ways is what AI can feel like).</p>
<p>The book explores privileged/secret knowledge learnt by a select few scholars (in real life, this would be tech workers), and the benefits of these outputs given to that small class. With the age of AI, we are being sold a pipe dream where complex technical knowledge is no longer limited to a select few who studied it, and now anyone can pull up a chatbot to create something.</p>
<p>There are substantial job losses as a result of silver working by the scholars and researchers at Oxford. Loads of people losing work because of the efficiency gains that silver gives machines. It sounds similar to what&rsquo;s happening now in the AI revolution.</p>
<p>The book forces us to acknowledge how our optimisations affect people without that knowledge, it&rsquo;s something that the scholars (or tech workers) tend to ignore. Confronting the loss of jobs and sometimes even loss of lives due to new technology (self driving cars, unsafe automated machinery, and working conditions or environmental impact). It&rsquo;s a bleak look at a different era where some of these same problems seem to be reoccurring.</p>
<p>There are huge colonialist undertones throughout the book and in the secret (or perhaps no longer secret) inner workings of the translation society in bringing the world under their leadership. They achieve this not through war, but through knowledge and intentional mistranslation of treaties. Tbh they might have been unintentional mistranslations, but at the time it was the best translation they could give with their scoped in points of view.</p>
<p>This idea is relevant to the Treaty of Waitangi and central to some of the arguments presented in NZ politics. They argue that Māori understood and signed the Māori document, assuming that same intent was on the English version. There are arguments that this was intentional mistranslation in favor of the crown.</p>
<p>Many characters in the story come from broken homes, grew up as slaves, or lost their families to sickness. The kids that showed intelligence were plucked by the translation society, and indoctrinated into an English upbringing to take advantage of their natural language heritage. We know how much our environment can shape our opinions. Imagine growing up as a child taken from your homeland and raised inside the empire of the English? Imagine how much that upbringing might shape your perspective of the world. Western ideas and Western thoughts.</p>
<p>On that, AI as built today, thinks in this same Western thought. The data is more heavily weighted towards what is on the internet, and so minority languages that arent as common in the public domain are weighted out. Not an easy problem to solve, and probably not one many people will want to solve because of the percieved &ldquo;pay off&rdquo;. It&rsquo;ll take a strong and well-driven community to drive it, or a banding together of indigenous folks in similar predicaments. The latter is more realistic imo.</p>
<p>At the end of this book, I can&rsquo;t help but feel sad. Their dependence on silver working was the downfall of the empire. There are lessons here warning us about over-dependence on AI.</p>
<p>Professor Lovell in the book uses the argument that the scholars&rsquo; hard work is justification enough for them to profit from it. He believes this information is out there for anyone to learn, they could&rsquo;ve taken advantage of the system and learnt and succeeded like him. But what that argument misses is that the environment in which the professor grew up in is vastly different. The tutors, the private lessons, access to resources. Even something as simple as time to study.</p>
<p>It&rsquo;s sad we don&rsquo;t hear about whether the empire recovered from the tragedy of the tower collapse. But I assume this is intentional as it allows us the freedom to think about what might have happened.</p>
<h2 id="quotes" class="group relative scroll-mt-20">
  Quotes
  <a
    href="#quotes"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Chapter 2</p>
<blockquote>
<p>&lsquo;But in latin, malum means &ldquo;bad&rdquo; and mālum,&rsquo; he wrote the words out for Robin, emphasizing the macron with force, &lsquo;means &ldquo;apple&rdquo;&rsquo;.
Interesting comparison to the macron in Maori. The main example I remember has to do with shit I think.</p>
</blockquote>
<p>Learnt about the word &ldquo;erudite&rdquo;, which means to have or show great knowledge.</p>
<blockquote>
<p>He learned what the Corns Laws we&rsquo;re and what they had to do with a Frenchman named Napoleon. He learned who the Catholics and Protestants were, and how the (he thought at least) small doctrinal differences between the two were apparently a matter of great and bloody importance.</p>
</blockquote>
<blockquote>
<p>New words in English were a game to him, for in understanding the word he always came to understand something about English history or culture itself. He delighted when common words were, unexpectedly, formed from other words he knew. Hussy was a compound word of house and wife. Holiday was a compound of holy and day. Bedlam came, implausibly, from Bethlehem. Goodbye was, incredibly, a shortened version of God be with you.</p>
</blockquote>
<p>Chapter 3</p>
<blockquote>
<p>So what does that tell you Birdie? If they&rsquo;re going to tell stories about you, use it to your advantage. The English are never going to think I&rsquo;m posh, but if I fit into their fantasy, then they&rsquo;ll at least think I&rsquo;m royalty.&rsquo;
This was Remy assuming the identity of a royal and behaving in a way that took control of the narrative of how he was perceived. Within the realms of what the English though practicable.</p>
</blockquote>
<p>Chapter 6</p>
<blockquote>
<p>He felt loose, vulnerable. Too passionate for what should have been and intellectual discussion. &lsquo;We take their languages, their ways of seeing and describing the world. We ought to give them something in return.&rsquo;
&lsquo;But language,&rsquo; said Professor Lovell, &lsquo;is not like a commercial good, like tea or silks, to be bought and paid for. Language is an infinite resource. And if we learn it, if we use it - who are we stealing from?&rsquo;</p>
</blockquote>
<p>Chapter 9</p>
<blockquote>
<p>&lsquo;Then the expense is entirely invented?&rsquo; Robin asked. This came about more sharply than he&rsquo;d intended. But her was thinking, then, of the choleric plague that had swept through London; of how Mrs Piper explained the poor simply could not be helped, for silver-work was so terribly costly.
&lsquo;Oh, yes.&rsquo; Professor Playfair seemed to find this all very funny. &lsquo;We hold the secrets, and we can set whatever terms we like. That&rsquo;s the beauty of being cleverer than everyone else&hellip;&rsquo;
This makes me think about IT or any other skill based or learned field. We charge a premium not for the difficulty of the task at hand. But for the years of experience spent learning to make the task fast. Some element of time efficiency and not having to burden ourselves with that knowledge too. Even if it might be easy to learn, shrouding it in difficulty makes paying for it easier.
I want to try my best to learn throughout my whole life. Always tinker and try things out myself. We have the internet in our pockets.</p>
</blockquote>
<p>Chapter 10</p>
<blockquote>
<p>&lsquo;But China has no reciprocal appetite for British goods. When the Qianglong Emperor received a display of British manufactured items from Lord Macartney, do you know what his response was? Strange and costly objects do not interest me. The Chinese don&rsquo;t need anything we&rsquo;re selling; they can produce everything they want on their own. So silver keeps flowing to China, and there&rsquo;s nothing the British can do about it because they can&rsquo;t alter supply and demand. One day it won&rsquo;t matter how much translation talent we have, because silver reserves will simply not exist to put it to use. The British Empire will crumble as a consequence of its own greed. Meanwhile, silver will accrue in new centres of power - places that have heretofore had their resources stolen and exploited&hellip;&rsquo;
This conversation was between Robin and his older brother.</p>
</blockquote>
<p>Chapter 12</p>
<blockquote>
<p>The full impact of so-called silver industrial revolution, a term coined by Peter Gaskell just six years before, was just beginning to be felt across the country. Silver-powered machines of the kind William Blake dubbed &lsquo;dark Satanic Mills&rsquo; were rapidly replacing artisinal labour, but rather than bringing prosperity to all, they had instead created an economic recession, had caused a widening gap between rich and poor that would soon become the stuff of novels by Disraeli and Dickens. Rural agriculture was in decline; men, woman, and children moved en masse to urban centres to work in factories, where they laboured unimaginably long hours and lost limbs and lives in frightful accidents.
Our civilization has gone through this industrial revolution already. But it begs the question, what would happen if the knowledge work was replaced by agentic systems. Would people in the know have working knowledge to profit from it? Will people retreat from urban centres into the country sides, in an effort to claim back that which makes us human?</p>
</blockquote>
<p>Chapter 32
Todo: add characters.</p>
<blockquote>
<p>Insane was not enough to cover it, Robin thought. English was insufficient to describe all this. His mind wandered to old Chinese texts, the idioms they employed about dynastic collapse and change. (Add chars here); tiānfalāndìfù. The heavens fell, and the earth collapsed in on itself.</p>
</blockquote>
<p>Chapter 33</p>
<blockquote>
<p>How did one make peace with one&rsquo;s own death? According to the accounts of Crito, the Phaedo, and the Apology, Socrates went to his death without distress, with such preternatural calm that he refused multiple entreaties to escape.</p>
</blockquote>
<p>I copied this quote while on a plane leaving Athens. This was after walking past the prison of Socrates and the university building where he taught.</p>
]]></content:encoded></item><item><title>Deciding what to keep</title><link>https://westernwilson.com/reflections/bunq-hackathon-7/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/bunq-hackathon-7/</guid><description>&lt;p&gt;It was around 4am when I scrapped the project. AI had co-built me a FastAPI cathedral, full of libraries I didn&amp;rsquo;t recognise and an architecture more ambitious than a 24 hour hackathon could achieve.&#10;The code worked, sort of. Extending it would be challenging, and integrating it with AWS would take time we didn&amp;rsquo;t have. So I deleted everything and started again with something simple that we could build and demo quickly.&lt;/p&gt;</description><content:encoded><![CDATA[<p>It was around 4am when I scrapped the project. AI had co-built me a FastAPI cathedral, full of libraries I didn&rsquo;t recognise and an architecture more ambitious than a 24 hour hackathon could achieve.
The code worked, sort of. Extending it would be challenging, and integrating it with AWS would take time we didn&rsquo;t have. So I deleted everything and started again with something simple that we could build and demo quickly.</p>
<p>This moment has been circling in my mind across this last week.</p>
<h2 id="how-i-got-there" class="group relative scroll-mt-20">
  How I got there
  <a
    href="#how-i-got-there"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>I went to Amsterdam for three nights to see the city and to take part in Bunq&rsquo;s 24-hour hackathon. I arrived alone and Bunq connected me with a team. On the day two people didn&rsquo;t show so we ended up finding two others in similar situations and formed a new team.</p>
<p>The hackathon brief was to build something that solved a problem, used the Bunq API, and had some kind of multi-modal AI. I didn&rsquo;t know what we&rsquo;d build, and honestly that was exciting to me!</p>
<h2 id="who-i-worked-with" class="group relative scroll-mt-20">
  Who I worked with
  <a
    href="#who-i-worked-with"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Our team was called &ldquo;4 corners&rdquo; with people from four countries. We had a DevOps Engineer, Business Analyst, Product Owner, and even a CFO. Honestly, it was a privilege to be around such accomplished people whose lives had taken different trajectories leading them to this moment.</p>
<h2 id="the-build" class="group relative scroll-mt-20">
  The build
  <a
    href="#the-build"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>On the day we had a brilliant talk by someone from AWS about spec driven development. I took that talk to heart and tried building our app using that same ethos. This led me to a vibe-coded backend app in Python, the language I knew, but attempting to use production-grade infrastructure. For a 24-hour prototype, this was overkill. The AI, eager to please, happily built me something far beyond what the hackathon called for.</p>
<p>It reminds me of something an old colleague once told me:</p>
<blockquote>
<p>building something is easy, productionising it is the hard bit</p>
</blockquote>
<p>In this hackathon, unfortunately, I started off by inverting their advice. I was productionising something before I&rsquo;d built anything.</p>
<h2 id="the-pivot" class="group relative scroll-mt-20">
  The pivot
  <a
    href="#the-pivot"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>You can wrangle an app together in 24 hours. What matters more is whether you&rsquo;ve thought clearly about the problem, can articulate the pain point, and can demonstrate something useful with honest tradeoffs.</p>
<ul>
<li>Mock APIs not crucial to the core solution instead of integrating with them.</li>
<li>Stub the database or run one locally to show the core idea working.</li>
<li>Later, if there&rsquo;s time then connect the real infra.</li>
</ul>
<p>The lesson for me was closing the gap between ambition and execution. A gentle reminder to focus more on the problem being solved, and on building an MVP that demonstrates a useful solution.</p>
<p>That&rsquo;s what scrapping the bloated AI app got me back to. Something small, something easily understood, something we could actually show someone.</p>
<p>The winning teams that night? Mostly localhost applications.</p>
<h2 id="amsterdam" class="group relative scroll-mt-20">
  Amsterdam
  <a
    href="#amsterdam"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>After the Hackathon winners were announced, I left Bunq HQ into a city I&rsquo;d been told was lovely. My cousin recommended Amsterdam as the place to be, and I could see why. Flat landscape, bike-friendly, sunny skies that week (rare, apparently), with free ferries criss-crossing the harbour, and people moving through it all with quiet competence. It felt like a larger Wellington with better trains.</p>
<p>I came partly to see whether I could live in Amsterdam. I left knowing I could.</p>
]]></content:encoded></item><item><title>Terraform state s3 bucket naming convention</title><link>https://westernwilson.com/reflections/terraform-state-s3-bucket-naming-convention/</link><pubDate>Sat, 11 Apr 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/terraform-state-s3-bucket-naming-convention/</guid><description>&lt;p&gt;While working on my personal devops project (&lt;a href="https://github.com/wjkw1/devops-profile-coffee-card-app-demo"&gt;see github&lt;/a&gt;), I realised I hadn&amp;rsquo;t thought through the naming convention of my terraform state in a long time.&lt;/p&gt;&#10;&lt;h2 id="naming-convention-decision" class="group relative scroll-mt-20"&gt;&#10; Naming convention decision&#10; &lt;a&#10; href="#naming-convention-decision"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;Final name:&lt;/strong&gt; &lt;code&gt;tfstate-{AccountId}-{GitHubRepo}&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;tfstate-123456789012-aws-foundations&lt;/code&gt;&lt;/p&gt;&#10;&lt;h3 id="why-this-name" class="group relative scroll-mt-20"&gt;&#10; Why this name&#10; &lt;a&#10; href="#why-this-name"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h3&gt;&#10;&lt;p&gt;The convention earns its structure because I&amp;rsquo;m committing to one terraform state bucket per project.&lt;/p&gt;</description><content:encoded><![CDATA[<p>While working on my personal devops project (<a href="https://github.com/wjkw1/devops-profile-coffee-card-app-demo">see github</a>), I realised I hadn&rsquo;t thought through the naming convention of my terraform state in a long time.</p>
<h2 id="naming-convention-decision" class="group relative scroll-mt-20">
  Naming convention decision
  <a
    href="#naming-convention-decision"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p><strong>Final name:</strong> <code>tfstate-{AccountId}-{GitHubRepo}</code></p>
<p><strong>Example:</strong> <code>tfstate-123456789012-aws-foundations</code></p>
<h3 id="why-this-name" class="group relative scroll-mt-20">
  Why this name
  <a
    href="#why-this-name"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>The convention earns its structure because I&rsquo;m committing to one terraform state bucket per project.</p>
<p>So, the three pieces of information communicated in the bucket name:</p>
<table>
	<thead>
			<tr>
					<th>Segment</th>
					<th>Reason</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>tfstate</code> <em>prefix</em></td>
					<td>Immediately identifies the bucket&rsquo;s purpose to any observer, without needing to open it.</td>
			</tr>
			<tr>
					<td><code>{AccountId}</code></td>
					<td>The only reliable way to guarantee global S3 uniqueness (bucket names are shared across all of AWS); also identifies ownership at a glance in billing, access logs, and cross-account contexts.</td>
			</tr>
			<tr>
					<td><code>{GitHubRepo}</code></td>
					<td>1:1 mapping between bucket and repository — you can navigate from S3 → GitHub (or back) without any lookup.</td>
			</tr>
	</tbody>
</table>
<p>The convention scales cleanly: every future infrastructure repository gets <code>tfstate-{AccountId}-{GitHubRepo}</code>, with no naming decisions to revisit.</p>
<h3 id="options-considered-and-why-they-were-rejected" class="group relative scroll-mt-20">
  Options considered and why they were rejected
  <a
    href="#options-considered-and-why-they-were-rejected"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p><strong><code>tfstate-aws-foundations</code></strong></p>
<p>Not globally unique, and almost certainly already taken thanks to the known limitation of S3. It also encodes the github project name in a bucket originally conceived as potentially holding all account state. Once my intent changed, the name became misleading.</p>
<p><strong><code>tfstate-{AccountId}</code></strong> <em>(original)</em></p>
<p>Unique and future-proof, but loses the repo link. Fine if one bucket holds state for many projects; less good when the bucket is scoped 1:1 to a single repo, where the link has real navigational value.</p>
<p><strong><code>{AccountId}-aws-foundations</code></strong></p>
<p>Globally unique, still links to the repo. But without a <code>tfstate</code> indicator, anyone browsing S3 has no immediate signal about what the bucket is for. The AccountId prefix also groups infrastructure buckets together when sorted, at the cost of reading unnaturally left-to-right.</p>
<p><strong><code>aws-foundations-{AccountId}</code></strong></p>
<p>More readable than the account-first example, same repo link. Still missing the <code>tfstate</code> indicator.</p>
<p><strong><code>tfstate-{AccountId}-aws-foundations</code></strong> <em>(chosen)</em></p>
<p>Purpose is obvious. Guaranteed to be unique. The 1:1 GitHub mapping is perfect. The tradeoff: twelve digits in the middle makes the name long and not great to say out loud. That cost is low because this string gets typed approximately twice in the bucket&rsquo;s lifetime &ndash; at creation and in <code>versions.tf</code> &ndash; so conversational readability is not important.</p>
<h3 id="convention-going-forward" class="group relative scroll-mt-20">
  Convention going forward
  <a
    href="#convention-going-forward"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p><code>tfstate-{AccountId}-{GitHubRepo}</code></p>
<p>Other projects I create that require aws infra will each get their own S3 state bucket following this pattern.</p>
<p>This gives me state isolation between projects; a terraform mistake in one project cannot affect another, and state files stay small and fast.</p>
]]></content:encoded></item><item><title>When I say 'chasing threads'</title><link>https://westernwilson.com/reflections/chasing-threads/</link><pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/chasing-threads/</guid><description>&lt;p&gt;There is this idea in productivity circles, self-help books, and startup culture: Focus. Only work on what matters. Cut the noise. Eliminate the distractions.&lt;/p&gt;&#10;&lt;p&gt;I think that&amp;rsquo;s only half the story.&lt;/p&gt;&#10;&lt;p&gt;Chasing threads is about following inspiration when it strikes.&lt;/p&gt;&#10;&lt;p&gt;During my induction week as a graduate engineer, over seven years ago, I was told a story about our CEO. It&amp;rsquo;s where I first felt encouraged to be curious at work.&lt;/p&gt;</description><content:encoded><![CDATA[<p>There is this idea in productivity circles, self-help books, and startup culture: Focus. Only work on what matters. Cut the noise. Eliminate the distractions.</p>
<p>I think that&rsquo;s only half the story.</p>
<p>Chasing threads is about following inspiration when it strikes.</p>
<p>During my induction week as a graduate engineer, over seven years ago, I was told a story about our CEO. It&rsquo;s where I first felt encouraged to be curious at work.</p>
<p>The CEO had a planned visit to one of our front-line Telco stores. All staff were prepped with questions and had a stacked agenda for photos and content capture. When the boss arrived, he spotted a staff member off to the side, absorbed in something (repairing a device, restocking a shelf) I can&rsquo;t quite remember.</p>
<p>He walked over and simply asked: &ldquo;Hey what are you doing?&rdquo;</p>
<p>Whoever this person was, they spoke about what they were doing with such genuine passion and joy that the CEO scrapped the rest of the agenda entirely. He spent the whole visit just talking with this one person about technology, about their goals, about what they loved about the job.</p>
<p>That conversation turned into a friendship. They kept in touch across the year. And eventually, that staff member grew into a role back at headquarters. All because the boss saw someone doing something that looked interesting, and went and spoke to them.</p>
<p>Do you know the feeling? You&rsquo;re reading something and a word catches you, you look it up, and suddenly you&rsquo;re twenty minutes deep into the history of something you never knew you cared about.</p>
<p>The conventional wisdom says to ignore that. To get back on task and stay on track.</p>
<p>But what if the thread is the track?</p>
<p>Here&rsquo;s the reframe I often come back to: life is distractions. It&rsquo;s the distractions the give life meaning. Not in the doom-scrolling-at-2am sense. In the sense that the interesting stuff, the stuff that actually shapes who you are, rarely arrives through the front door. It sneaks up on you. It&rsquo;s the thing you weren&rsquo;t supposed to be doing that ends up mattering most.</p>
<p>Steve Jobs is probably the most famous example of this. In his 2005 Stanford commencement speech, he talked about dropping in on a calligraphy class at Reed College out of pure curioisty, with no practical application in mind. A decade later, that class became the foundation for the beautiful typography of the first Macintosh.</p>
<p>He didn&rsquo;t chase calligraphy because it was strategic. He chased it because it pulled at him. Same instinct as a CEO who walks past a stacked agenda to talk to someone doing something interesting. Same instinct as anyone who follows a word into a twenty-minute rabbit hole and comes out knowing something they didn&rsquo;t expect to care about.</p>
<p>But I have to be honest with myself, because this idea has a dark twin. Something a colleague of mine introduced me to because I was chasing the latest technologies all the time.</p>
<p>There&rsquo;s a concept called <a href="https://en.wikipedia.org/wiki/Shiny_object_syndrome">Shiny Object Syndrome</a>. The idea that some people —especially curious, creative, entrepreneurial types— get so drawn to the next interesting thing that they never actually go deep on anything. They bounce from project to project, tool to tool, always chasing novelty, never building something real.</p>
<p>I&rsquo;ve been there. Most curious people have. You pick up a thread and it leads you to another thread, and another, and before you know it you&rsquo;ve spent a whole day falling down rabbit holes and nothing is finished.</p>
<p>The shiny ball of yarn is real. And if you&rsquo;re not careful, chasing threads becomes a sophisticated-sounding excuse for avoiding the hard, slow, unglamorous work of actually finishing things.</p>
<p>The difference between a thread worth chasing and a shiny object isn&rsquo;t obvious in the moment; but it becomes clear with a bit of distance. If you park an idea for a few days and it&rsquo;s still pulling at you, it&rsquo;s probably a thread worth chasing. If you forget about it, then it probably wasn&rsquo;t worth following anyway.</p>
<p>So go chase your own threads. Not all of them and not recklessly. But when something tugs at you, don&rsquo;t immediately dismiss it as a distraction.</p>
]]></content:encoded></item><item><title>Linting and formatting code</title><link>https://westernwilson.com/reflections/linting-and-formatting/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/linting-and-formatting/</guid><description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Code style and formatting should &lt;strong&gt;not&lt;/strong&gt; steal your brainpower. Set up linting/formatting at three touchpoints (your editor, your pre-commit hook, and your CI pipeline) and formatting becomes automatic from the moment you write code to the moment it merges. At the end, this reflection walks through exactly how to do that for a Python &lt;a href="https://fastapi.tiangolo.com"&gt;FastAPI&lt;/a&gt; project.&lt;/p&gt;&#10;&lt;h2 id="linting-and-formatting-code" class="group relative scroll-mt-20"&gt;&#10; Linting and formatting code&#10; &lt;a&#10; href="#linting-and-formatting-code"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;Recently, while working on a &lt;a href="https://github.com/wjkw1/devops-profile-coffee-card-app-demo"&gt;personal DevOps project&lt;/a&gt;, I spent some time looking into linting tools. They amazingly save your brainpower for the actual problem, instead of code styling. Whether it&amp;rsquo;s two spaces or four per indent, single quotes or double, I don&amp;rsquo;t mind. Other people have thought deeply about these trade-offs, landed on reasonable answers, and codified them. I&amp;rsquo;m happy to piggyback on that.&lt;/p&gt;</description><content:encoded><![CDATA[<p><strong>TL;DR:</strong> Code style and formatting should <strong>not</strong> steal your brainpower. Set up linting/formatting at three touchpoints (your editor, your pre-commit hook, and your CI pipeline) and formatting becomes automatic from the moment you write code to the moment it merges. At the end, this reflection walks through exactly how to do that for a Python <a href="https://fastapi.tiangolo.com">FastAPI</a> project.</p>
<h2 id="linting-and-formatting-code" class="group relative scroll-mt-20">
  Linting and formatting code
  <a
    href="#linting-and-formatting-code"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Recently, while working on a <a href="https://github.com/wjkw1/devops-profile-coffee-card-app-demo">personal DevOps project</a>, I spent some time looking into linting tools. They amazingly save your brainpower for the actual problem, instead of code styling. Whether it&rsquo;s two spaces or four per indent, single quotes or double, I don&rsquo;t mind. Other people have thought deeply about these trade-offs, landed on reasonable answers, and codified them. I&rsquo;m happy to piggyback on that.</p>
<p>I aim to write code that works, and let the styling happen automatically.</p>
<p>There&rsquo;s a side benefit too: when a codebase consistently uses a popular linting tool, anyone familiar with it can scan the code and immediately know where to look. It&rsquo;s like how headings in a research paper let you scan before you commit to reading.</p>
<h2 id="three-touchpoints-that-matter" class="group relative scroll-mt-20">
  Three touchpoints that matter
  <a
    href="#three-touchpoints-that-matter"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Good linting hygiene should show up at meaningful points in your development lifecycle:</p>
<ol>
<li><strong>In your IDE (or text editor), as you write</strong></li>
<li><strong>Locally, before you commit</strong></li>
<li><strong>In CI/CD, before code merges</strong></li>
</ol>
<p>Each layer serves a different purpose. Together, they make code quality essentially automatic. (Tools like <a href="https://www.sonarsource.com/products/sonarqube/">SonarQube</a> go deeper —security, coverage, and code smells at a project level— but that&rsquo;s a reflection for another time.)</p>
<p><figure class="img-light"><img src="/reflections/linting-and-formatting/linting_touchpoints_light.svg"
			alt="Three linting touchpoints">
</figure>

<figure class="img-dark"><img src="/reflections/linting-and-formatting/linting_touchpoints_dark.svg"
			alt="Three linting touchpoints">
</figure>
</p>
<p><strong>In the IDE (or text editor)</strong>, I use <a href="https://code.visualstudio.com">VS Code</a> with the relevant extensions installed and Format on Save enabled. It does what it sounds like: when the file saves, the formatter runs. Sometimes this needs dev dependencies installed to work properly; check the official docs and you&rsquo;ll figure it out quickly, I&rsquo;ll go into <a href="https://docs.astral.sh/ruff/">Ruff</a> for Python specifically later in this post.</p>
<p><strong>Locally, before committing</strong>, is where things get interesting. At various companies I&rsquo;ve seen the same pattern repeat: developer forgets to format, pushes code, CI fails, they fix locally, force-push, CI reruns. This adds ten minutes to the dev cycle, minimum. Multiply that across a team and it adds up quickly.</p>
<p>Git&rsquo;s <a href="https://pre-commit.com">pre-commit</a> hooks solve this cleanly. When you attempt a commit, a hook runs your formatting and linting checks first. If they fail, the commit doesn&rsquo;t go through. This means no broken CI and no annoying formatting fix commits. This enables the code to validate itself before it ever leaves your machine.</p>
<p>The one catch: developers can skip the setup step entirely, which is exactly why the third layer exists.</p>
<p><strong>In CI/CD</strong>, the pipeline enforces what the pre-commit hook encourages. Even if someone bypasses local hooks, nothing merges unless the linting passes. This is your actual quality gate and is what you rely on.</p>
<h2 id="why-ruff-instead-of-black-and-flake8" class="group relative scroll-mt-20">
  Why Ruff instead of Black and Flake8
  <a
    href="#why-ruff-instead-of-black-and-flake8"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Three or more years ago, I used <a href="https://black.readthedocs.io/en/stable/">Black</a> for formatting and <a href="https://flake8.pycqa.org/en/latest/">Flake8</a> for static analysis. Two tools, two configs, two things to install and maintain. It was a minor friction, but that friction compounded and was a little naff.</p>
<p>Ruff replaces both with a single tool. It&rsquo;s faster, simpler, and opinionated enough that you don&rsquo;t have to make many decisions. For a project where the point is <em>less thinking about styling and formatting</em>, that&rsquo;s exactly what I wanted.</p>
<h2 id="the-setup-python--fastapi" class="group relative scroll-mt-20">
  The Setup (Python / FastAPI)
  <a
    href="#the-setup-python--fastapi"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>Three things to configure, in roughly this order:</p>
<h3 id="step-1-vs-code--ruff-extension" class="group relative scroll-mt-20">
  Step 1: VS Code + Ruff extension
  <a
    href="#step-1-vs-code--ruff-extension"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>Install the <a href="https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff">Ruff extension</a> from the VS Code marketplace — the official one is published by Astral (charliermarsh.ruff). Once installed, add this to your .vscode/settings.json:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;[python]&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;editor.formatOnSave&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;editor.defaultFormatter&#34;</span>: <span style="color:#e6db74">&#34;charliermarsh.ruff&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;editor.codeActionsOnSave&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;source.fixAll.ruff&#34;</span>: <span style="color:#e6db74">&#34;explicit&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;source.organizeImports.ruff&#34;</span>: <span style="color:#e6db74">&#34;explicit&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Committing <code>.vscode/settings.json</code> to the repo is intentional. Anyone who clones the project and opens it in VS Code gets the same formatting behaviour automatically. No extra knowledge required.</p>
<p>No separate Ruff installation is needed as the VS Code extension ships its own binary, and the pre-commit hook (added in Step 2) bundles its own too. You just need to add your Ruff config to <code>ruff.toml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#a6e22e">tool</span>.<span style="color:#a6e22e">ruff</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">line-length</span> = <span style="color:#ae81ff">88</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">target-version</span> = <span style="color:#e6db74">&#34;py312&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>[<span style="color:#a6e22e">tool</span>.<span style="color:#a6e22e">ruff</span>.<span style="color:#a6e22e">lint</span>]
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">select</span> = [<span style="color:#e6db74">&#34;E&#34;</span>, <span style="color:#e6db74">&#34;F&#34;</span>, <span style="color:#e6db74">&#34;I&#34;</span>]
</span></span></code></pre></div><p>The select block opts into pycodestyle errors (E), Pyflakes (F), and isort import sorting (I). This covers what you&rsquo;d previously have needed Black, Flake8, and <a href="https://pycqa.github.io/isort/">isort</a> to do separately.</p>
<h3 id="step-2-pre-commit-hooks" class="group relative scroll-mt-20">
  Step 2: Pre-commit hooks
  <a
    href="#step-2-pre-commit-hooks"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>First, install the pre-commit package:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install pre-commit
</span></span></code></pre></div><p>Then create a <code>.pre-commit-config.yaml</code> in the root of your repo. Here&rsquo;s exactly what&rsquo;s in the project:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">repos</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">repo</span>: <span style="color:#ae81ff">https://github.com/astral-sh/ruff-pre-commit</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">rev</span>: <span style="color:#ae81ff">v0.11.4</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">hooks</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">id</span>: <span style="color:#ae81ff">ruff</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">args</span>: [--<span style="color:#ae81ff">fix]</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">files</span>: <span style="color:#ae81ff">^api/</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">id</span>: <span style="color:#ae81ff">ruff-format</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">files</span>: <span style="color:#ae81ff">^api/</span>
</span></span></code></pre></div><p>A few things worth noting here. The files: <code>^api/</code> pattern scopes the hooks to the API directory only — the frontend has its own linting story (<a href="https://eslint.org">ESLint</a> + <a href="https://prettier.io">Prettier</a>), so there&rsquo;s no point running Ruff across the whole repo. The ruff hook handles lint checks and the <code>--fix</code> flag means it&rsquo;ll auto-fix what it can rather than just reporting issues. The ruff-format hook handles formatting, equivalent to what Black would have done.
Once the file is in place, install the hooks:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pre-commit install
</span></span></code></pre></div><p>This wires the hooks into your local <code>.git/hooks/pre-commit</code>. From this point, every time you run git commit, Ruff runs first. If it finds issues it can&rsquo;t auto-fix, the commit is blocked and the problems are printed to the terminal. Fix them, stage the changes, and commit again.
You can also run it manually across all files at any point:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pre-commit run --all-files
</span></span></code></pre></div><p>Useful for a first run after setting up, or after pulling in changes from someone else who hasn&rsquo;t had the hooks installed.</p>
<p>That&rsquo;s it. Once it&rsquo;s done, your automatic project linting works in the background. Which is perfect.</p>
]]></content:encoded></item><item><title>Unfuck Your Boundaries</title><link>https://westernwilson.com/books/unfuck-your-boundaries/</link><pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/books/unfuck-your-boundaries/</guid><description>&lt;blockquote&gt;&#10;&lt;p&gt;We respect these boundaries through how we care for others and by letting them have their own emotional experiences&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Too often we feel the emotional burden, or perceived microexpression that means a person is angry or sad. Then you immediate feel an urge to rush in and fix it.&lt;/p&gt;&#10;&lt;p&gt;That isn&amp;rsquo;t respect. They didnt ask for help. They&amp;rsquo;re processing something you can&amp;rsquo;t see. You must let them have that experience and not feel guilty about not forcing your hand to help.&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
<p>We respect these boundaries through how we care for others and by letting them have their own emotional experiences</p>
</blockquote>
<p>Too often we feel the emotional burden, or perceived microexpression that means a person is angry or sad. Then you immediate feel an urge to rush in and fix it.</p>
<p>That isn&rsquo;t respect. They didnt ask for help. They&rsquo;re processing something you can&rsquo;t see. You must let them have that experience and not feel guilty about not forcing your hand to help.</p>
<p>This book comes with a lot of hard-hitting boundary related questions that are worth diving into. Some are pretty confronting tbh. Firstly it&rsquo;s important to understand the categories, even at a surface level.</p>
<p>The categories Fath G. Harper uses for the various boundaries we have:</p>
<ul>
<li><strong>Time boundaries</strong> - limits around how you spend your time, who has access to it, and protecting it from overcommitment or others&rsquo; demands</li>
<li><strong>Spiritual boundaries</strong> (belief system) - the right to hold your own beliefs, values, and practices without pressure to adopt or justify them to others</li>
<li><strong>Intellectual boundaries</strong> - protecting your thoughts, opinions, and ideas from ridicule, dismissal, or coercion to think differently</li>
<li><strong>Emotional/relational boundaries</strong> - defining what emotional labor you will and won&rsquo;t take on, and how others are allowed to treat you in relationships</li>
<li><strong>Sexual boundaries</strong> - determining what sexual contact, conversation, or attention you consent to, with whom, and under what circumstances</li>
<li><strong>Property boundaries</strong> - your right to control who uses your belongings, space, and possessions</li>
<li><strong>Physical boundaries</strong> - limits around your body, personal space, and physical touch</li>
</ul>
<blockquote>
<p>Questions for consideration:</p>
<ol>
<li>what are some of the boundaries you have for each of these categories?</li>
<li>which kinds of boundary violations do you experience the most? Which categories do those violation s belong to?</li>
<li>which kinds of boundaries do you have the most difficulty respecting for others? Which categories do those violations belong to?</li>
</ol>
</blockquote>
<blockquote>
<p>Four main biggest reasons boundaries get walked over:</p>
<ol>
<li>Overall social hierarchy problems</li>
<li>Screwed up attachment styles</li>
<li>High conflict personality</li>
<li>Perpetuation of coercive control</li>
</ol>
</blockquote>
<p>Originally it was difficult to understand what the words in this sentence meant: &ldquo;perpetuation of coercive control&rdquo;. Breaking down what each part of the sentence means makes more sense. It feels like a persistent, slow burn where a person is using force to control someone. Hectic.</p>
<p>The book explains an ideal cultural norm where &ldquo;yes means yes&rdquo; and not having the opposite where we are forced to look &ldquo;no means no&rdquo;. She describes the the flip of what we currently have. This is good.</p>
<p>The books says tha all large political movements started with a boundary being crossed and someone standing up for their right to protect said boundary. So, in essence, boundaries are eventually political by nature. They&rsquo;re definitely shaped by politics and what is acceptable by both our culture and our laws.</p>
<p>Schools aren&rsquo;t always equipped to set kids up for healthy communication and boundaries, though they try. Sometimes it feels like they&rsquo;re set up for compliance and adhering to the status quo. I remember a time as a young kid where answering questions was fun, and more people put up their hands. Eventually less and less hands were keen to go up. It felt like you were shamed for answering questions wrong, and so the social rejection / embarrassment trained you to ask less questions. Since then, I consistently try to ask one useful question in meetings/events that I&rsquo;m a part of. Something that&rsquo;s taken time to unlearn. Heck, I still get nervous at the thought of putting my hand up to ask a question.</p>
<p>Three attachment styles in a nutshell:</p>
<ul>
<li>Secure attachment - general comfortability with others. 60% of people in the US.</li>
<li>Avoidance attachment - hard time trusting or depending on others, uncomfortable with the level of closeness people expect of them</li>
<li>Anxious attachment - hard time with worry, fear of abandonment</li>
</ul>
<p>It feels like that depending on the context of whatever situation you&rsquo;re in, and people you&rsquo;re with, you slide in and out of each attachment style. Say for example, with your best friend you feel secure around. But a mean teacher you feel avoidance toward.</p>
<p>The book reinforces the impact childhood has on attachment styles in adulthood. And until we learn skills to react differently to the world around us, we won&rsquo;t really be able to move past our attachment styles.</p>
<p>I knew these words, but I didn&rsquo;t realise the succinct meaning behind them:</p>
<ol>
<li>Narcissistic - excessive self-interest in self and appearance</li>
<li>Histrionic - excessively theatrical or dramatic in character or style</li>
</ol>
<blockquote>
<p>The minute we start paying attention to our own bullshit is the minute it starts to change.
On noticing your own state and not externalising your problems on the world.</p>
</blockquote>
<blockquote>
<p>Best way to respond to High Confilct Individuals, is to first ask if you even need to respond? What are the consequences of not responding, what about if you respond?</p>
</blockquote>
<p>The final bits of the book for me remind me that effective communication is possible. It shows a fair few examples and says everyone has a responsibility to help make the world a better place. Decent read. I think if I was more strict on the questions and spending time on them I might have got more out of this book.
The questions are worth picking up and going through at various stages of your life.</p>
]]></content:encoded></item><item><title>The Great Mental Models</title><link>https://westernwilson.com/books/the-great-mental-models/</link><pubDate>Sat, 21 Mar 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/books/the-great-mental-models/</guid><description>&lt;p&gt;This book starts off explaining why mental models are required and gives many examples of situations show its importance. A lot of books do this, it&amp;rsquo;s like they try to entice you into reading the book. But I&amp;rsquo;d already decided to read it, so its hard to get through the start of the book.&lt;/p&gt;&#10;&lt;p&gt;This book leans on Charlie Munger&amp;rsquo;s philosophy. Early on he shares a Munger quote:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;I believe in the discipline of mastering the best of what other people have figured out&#10;When mental models are not applied then you probably have failed understanding of a given situation.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This book starts off explaining why mental models are required and gives many examples of situations show its importance. A lot of books do this, it&rsquo;s like they try to entice you into reading the book. But I&rsquo;d already decided to read it, so its hard to get through the start of the book.</p>
<p>This book leans on Charlie Munger&rsquo;s philosophy. Early on he shares a Munger quote:</p>
<blockquote>
<p>I believe in the discipline of mastering the best of what other people have figured out
When mental models are not applied then you probably have failed understanding of a given situation.</p>
</blockquote>
<p>One example is the context of an elephant and a blind person who has never experienced an elephant. They might mistake an elephant ear to be a fan, the trunk tusks might be mistaken as a spear, or the trunk as a snake.</p>
<p>The main chunk of the book explains general thinking concepts that are akin to common sense. For example:</p>
<ol>
<li>Map is not the territory</li>
<li>First order principles (using 5 whys or scratch method)</li>
<li>Thought experiments</li>
<li>Circle of competence</li>
<li>Inversion - begin with the end in mind. Work both ways.</li>
<li>Falsifiability</li>
<li>Second order thinking. Literally just thinking about the consequences of consequence. Reminded me of playing chess or even 8-ball pool.</li>
</ol>
<p>Probabilistic thinking is a mental model I naturally lean towards. I avoid thinking in black &amp; white or definite yes/no thinking. Instead, things tend to be more of a grey area, belonging to a spectrum of possibilities. In reality, probabilities should update based the most up to date information too.</p>
<p>I enjoyed learning about Occams razor; the simplest explanation, if most probable, is the most likely.
Hanlons razor was equally facinating; it&rsquo;s not malice, it&rsquo;s probably stupidity or ignorance.</p>
<p>There are a few supporting ideas, one that stuck out is necessity and sufficiency. Where sometimes something might be necessary, or sufficient, and any combination of either or.</p>
<p>I learnt about blood-letting in this book, and how it was the suggested medical practice for over 2000 years. The idea that if you get sick you can bleed it out of you. But losing blood actually makes you weaker and might end up making your more sick.</p>
<p>I was reminded of Causation vs. Correlation in this book. The classic example of shark attacks increasing in correlation with ice cream sales. Though buying an ice cream doesn&rsquo;t cause a shark attack. I think it was more likely summer time, which means it&rsquo;s both hot enough for ice cream but also means more people are swimming.</p>
<p>Think through whether an idea is required or not before jumping to conclusions. Sometimes we have a bias toward ideas that support the conclusion we want, over the conclusion that actually is.</p>
<h3 id="quotes" class="group relative scroll-mt-20">
  Quotes
  <a
    href="#quotes"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<blockquote>
<p>The skill for finding the right solutions for the right problems is one form of wisdom</p>
</blockquote>
<blockquote>
<p>Our failures to update from interacting with reality spring primarily from three things: not having the right perspective or vantage point, ego-induced denial, and distance from the consequences of our decisions.</p>
</blockquote>
<blockquote>
<p>Admitting that we&rsquo;re wrong is tough. It&rsquo;s easier to fool ourselves that we&rsquo;re right at a high level than at the micro level, because at the micro level we see and feel the immediate consequences.</p>
</blockquote>
<blockquote>
<p>There is an old adage that encapsulates this: &ldquo;To the man with only a hammer, everything starts looking like a nail.&rdquo;</p>
</blockquote>
<blockquote>
<p>In order to use a map or model as accurately as possible, we should take three important considerations into account: Reality is the ultimate update. Consider the cartographer. Maps can influence territories.</p>
</blockquote>
]]></content:encoded></item><item><title>C4 Models for software system diagrams</title><link>https://westernwilson.com/reflections/c4models/</link><pubDate>Fri, 20 Mar 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/c4models/</guid><description>&lt;p&gt;In 2022, a colleague introduced me to the C4 Model framework for architecture diagrams. At the time, I&amp;rsquo;d seen nothing like it. I&amp;rsquo;d often been in whiteboard sessions, struggling to make sense of all the boxes and lines people drew. Diagrams were drawn completely different depending on the person holding the marker.&#10;This colleague was a star communicator and an inspiring solutions architect. Their ability to convey complex ideas clearly was a huge influence in me picking up the framework and running with it for my own diagrams.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In 2022, a colleague introduced me to the C4 Model framework for architecture diagrams. At the time, I&rsquo;d seen nothing like it. I&rsquo;d often been in whiteboard sessions, struggling to make sense of all the boxes and lines people drew. Diagrams were drawn completely different depending on the person holding the marker.
This colleague was a star communicator and an inspiring solutions architect. Their ability to convey complex ideas clearly was a huge influence in me picking up the framework and running with it for my own diagrams.</p>
<h3 id="what-is-the-c4-model" class="group relative scroll-mt-20">
  What is the C4 Model?
  <a
    href="#what-is-the-c4-model"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>The C4 Model is a structured, uniform approach to describe the architecture of a software system at different level of detail. Instead of throwing everything onto a single diagram and hoping people get it, it organises abstractions in a way that mirrors how developers or software architects tend to think about systems.</p>
<p>The 4 levels are:</p>
<ol>
<li>System Context - the big picture: your system and how it interacts with users and other systems.</li>
<li>Containers - the applications and data stores that make up your system.</li>
<li>Components - the building blocks of each of our containers.</li>
<li>Code - the classes, interfaces, and functions that implement each component.
The official definition explains it best:
<blockquote>
<p>A software system is made up of one or more containers (applications and data stores), each of which contains one or more components, which in turn are implemented by one or more code elements (classes, interfaces, objects, functions, etc). And people (actors, roles, personas, named individuals, etc) use the software systems that we build.
Source: <a href="https://arc.net/l/quote/kvovhsbn">https://arc.net/l/quote/kvovhsbn</a>
Visit the official website to read more detail about this framework.</p>
</blockquote>
</li>
</ol>
<h3 id="the-map-is-not-the-territory" class="group relative scroll-mt-20">
  The map is not the territory
  <a
    href="#the-map-is-not-the-territory"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>A book called The Great Mental Models: General Thinking Concepts has a chapter called &ldquo;the map is not the territory&rdquo;. The idea being that the granularity of Google Maps, does not show the same level of detail as someone walking around on the ground. This is a similar line of reasoning useful when thinking about the C4 diagram.
For most intents and purposes, the maps context is enough to be useful at a high-level. But sometimes, we need more detail to understand what is going on under the hood. That&rsquo;s why we have these layers of abstraction, and at each abstraction we are targeting a different audience.</p>
<h3 id="the-caveat-at-the-code-level" class="group relative scroll-mt-20">
  The caveat at the code level
  <a
    href="#the-caveat-at-the-code-level"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>The creator of this framework, Simon Brown, recommends never manually building the lowest level C4 class diagram. It&rsquo;s not worth the effort of building, and definitely not worth keeping updated; especially with the pace at which software changes nowadays. The level of detail is normally so closely related to code that it can (and should) be generated automatically.
This is a pragmatic acknowledgement that diagrams are tools, not artefacts to be maintained for their own sake. If a diagram can&rsquo;t keep pace with reality, it quickly becomes a source of confusion rather than clarity.</p>
<h3 id="examples-from-a-personal-project" class="group relative scroll-mt-20">
  Examples from a personal project
  <a
    href="#examples-from-a-personal-project"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>I&rsquo;ve been applying this framework in a recent pet project. If you&rsquo;d like to see the diagrams in action, head over to the demo project here: devops-profile-coffee-card-app-demo.</p>
<h4 id="context" class="group relative scroll-mt-20">
  Context
  <a
    href="#context"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h4>
<p><img src="CoffeeCards-C4-Context.jpg" alt="C4 Context Diagram"></p>
<h4 id="containers" class="group relative scroll-mt-20">
  Containers
  <a
    href="#containers"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h4>
<p><img src="CoffeeCards-C4-Container.jpg" alt="C4 Container Diagram"></p>
<h4 id="components-tbd" class="group relative scroll-mt-20">
  Components TBD
  <a
    href="#components-tbd"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h4>
<p><em>Coming soon&hellip;</em></p>
]]></content:encoded></item><item><title>Reflection vs. Blog</title><link>https://westernwilson.com/reflections/2026-03-20-blogs/</link><pubDate>Fri, 20 Mar 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/2026-03-20-blogs/</guid><description>&lt;p&gt;A &lt;a href="https://www.dictionary.com/browse/blog"&gt;blog&lt;/a&gt; is a truncation of &amp;ldquo;weblog&amp;rdquo; and refers to&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;a website containing a writer&amp;rsquo;s or group of writers&amp;rsquo; own experiences, observations, opinions, etc., and often having images and links to other websites.&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;A &lt;a href="https://www.dictionary.com/browse/reflection"&gt;reflection&lt;/a&gt; has many meanings, but the two I latch onto are:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;a fixing of the thoughts on something; careful consideration.&lt;/li&gt;&#10;&lt;li&gt;a thought occurring in consideration or meditation.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;The word &lt;em&gt;reflections&lt;/em&gt; carries a quieter, more inward energy for me. It implies that you&amp;rsquo;re writing for the process of thinking, not &lt;em&gt;for&lt;/em&gt; an audience or an outcome. I love that there is no implicit pressure to grow a following, monetise, or be consistent on a schedule.&lt;/p&gt;</description><content:encoded><![CDATA[<p>A <a href="https://www.dictionary.com/browse/blog">blog</a> is a truncation of &ldquo;weblog&rdquo; and refers to</p>
<blockquote>
<p>&ldquo;a website containing a writer&rsquo;s or group of writers&rsquo; own experiences, observations, opinions, etc., and often having images and links to other websites.&rdquo;</p>
</blockquote>
<p>A <a href="https://www.dictionary.com/browse/reflection">reflection</a> has many meanings, but the two I latch onto are:</p>
<blockquote>
<ul>
<li>a fixing of the thoughts on something; careful consideration.</li>
<li>a thought occurring in consideration or meditation.</li>
</ul>
</blockquote>
<p>The word <em>reflections</em> carries a quieter, more inward energy for me. It implies that you&rsquo;re writing for the process of thinking, not <em>for</em> an audience or an outcome. I love that there is no implicit pressure to grow a following, monetise, or be consistent on a schedule.</p>
<p>My perception of <em>blogs</em> is that they come loaded with baggage nowadays. It asks for SEO optimisation, calls to action, newsletter signups, and the hustle of content creation. That&rsquo;s a very different thing from sitting down and genuinely processing your thoughts on paper.</p>
<p>This is why I choose to use <em>reflections</em>, to give me freedom of thought while writing thoughts on a page.</p>
]]></content:encoded></item><item><title>Yellowface</title><link>https://westernwilson.com/books/yellowface/</link><pubDate>Wed, 18 Mar 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/books/yellowface/</guid><description>&lt;p&gt;&lt;strong&gt;Contains spoilers.&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;This book was amazing read for me. I barely read anything fictional thats not got some element of fantasy or super powers. But I couldn&amp;rsquo;t stop myself from turning pages. Thoroughly enjoyed it and I&amp;rsquo;ll be reading her other works.&lt;/p&gt;&#10;&lt;p&gt;This book was gripping, the pacing of each sentence was masterful. It feels like it was written in its most condensed form without losing any meaning from the text. It jumped from plot to plot, and I like that it didn&amp;rsquo;t waffle on about unessesary bits.&lt;/p&gt;</description><content:encoded><![CDATA[<p><strong>Contains spoilers.</strong></p>
<p>This book was amazing read for me. I barely read anything fictional thats not got some element of fantasy or super powers. But I couldn&rsquo;t stop myself from turning pages. Thoroughly enjoyed it and I&rsquo;ll be reading her other works.</p>
<p>This book was gripping, the pacing of each sentence was masterful. It feels like it was written in its most condensed form without losing any meaning from the text. It jumped from plot to plot, and I like that it didn&rsquo;t waffle on about unessesary bits.</p>
<p>The book was broken into many main themes as the story progressed. Basically, it shows a lonely character Juniper Song (Hayward) go through the envy, then stardom, then the fall because of her silly decisions.</p>
<p>She wants to be a great writer, but isn&rsquo;t ever that good at creating her own ideas. She keeps trying to draw parallels between her own messed up situations and what Athena, the successful author of the book (in the novel), and herself. I wonder if she might have done better as an editor of other peoples works (though envy and her mean inner voice probably wouldn&rsquo;t have helped her case).</p>
<p>There are significant moments in the book that make your insides curl. Basically, June was racist and couldn&rsquo;t understand why. We see her struggling to understand what its like to be a person of colour, and how she continues to miss the mark, and unfortunately believes in her mind that she&rsquo;s in the right. There is one scene where she chats to her mentee, a young aspiring Asian author and says something along the lines of: &ldquo;don&rsquo;t worry, you&rsquo;ll be fine. You could write anything, you&rsquo;re diverse and publishers eat that up!&rdquo;.</p>
<p>R.F.Kuang showed June as a bad writer by showing us the bad decisions she made while writing, especially the parts June decided to cut out.</p>
<p>I enjoyed seeing the difference between having a star publishing team vs an independant agent who doesn&rsquo;t have the resources to support a writer. I wonder how much of this is true in the real world? Marketing is such a skill, and if you aren&rsquo;t able to get your book, product, or tech in front of people then I doubt they&rsquo;ll use it.</p>
<p>June&rsquo;s whole life changed when she got a great publishing team. They were able to publicise her book and get out to the best sellers list. They had connections, and methods, and a team of publicists to help her. Though June&rsquo;s success is probably due to the foundations of the story she stole.</p>
<p>Juniper struggles with her social media addiction, and her incessant need to be popular, also maybe her ego. These all warp any effective decision making she would ever be able to make, she fixes the momentary problems as they come up but doesn&rsquo;t think about long-term consequences.</p>
<p>By the end of the book she is caught out on her lies, and even at the end, it finishes by June coming up with a delusional plan to flip the narrative on its head and go against the person who has video taped proof of her admitting her guilt.</p>
]]></content:encoded></item><item><title>How I use Tags</title><link>https://westernwilson.com/reflections/how-i-use-tags/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/how-i-use-tags/</guid><description>&lt;p&gt;This page is a personal reference so I remember the tags I use on this site, what they mean, and when to apply them. Max 1–3 tags per post.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;meta&lt;/code&gt;&lt;/strong&gt; &amp;ndash; Posts about this site itself &amp;ndash; announcements, changes, meta type entries.&#10;&lt;em&gt;Use when:&lt;/em&gt; the post is about the website or process specifically, not a topic within it.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ai&lt;/code&gt;&lt;/strong&gt; &amp;ndash; Artificial intelligence, large language models, and how I&amp;rsquo;m using them in work or writing.&#10;&lt;em&gt;Use when:&lt;/em&gt; the post is primarily about AI tools, AI tech, or AI behaviour&lt;/p&gt;</description><content:encoded><![CDATA[<p>This page is a personal reference so I remember the tags I use on this site, what they mean, and when to apply them. Max 1–3 tags per post.</p>
<p><strong><code>meta</code></strong> &ndash; Posts about this site itself &ndash; announcements, changes, meta type entries.
<em>Use when:</em> the post is about the website or process specifically, not a topic within it.</p>
<p><strong><code>ai</code></strong> &ndash; Artificial intelligence, large language models, and how I&rsquo;m using them in work or writing.
<em>Use when:</em> the post is primarily about AI tools, AI tech, or AI behaviour</p>
<p><strong><code>career</code></strong> &ndash; Work, professional growth, team dynamics, and lessons from the industry.
<em>Use when:</em> the post reflects on the people side of software &ndash; roles, culture, collaboration.</p>
<p><strong><code>communication</code></strong> &ndash; Explaining ideas about communicating, for example to non-technical audiences, across teams, or in documentation.
<em>Use when:</em> the post is about how we convey information to other people.</p>
<p><strong><code>definitions</code></strong> &ndash; Posts that define how I use a specific term, phrase, or concept &ndash; personal vocab entries.
<em>Use when:</em> the post is structured around explaining what something means to me.</p>
<p><strong><code>devops</code></strong> &ndash; Platform engineering, infrastructure, CI/CD, and the operational side of software.
<em>Use when:</em> the post is about systems, pipelines, or keeping things running.</p>
<p><strong><code>devtools</code></strong> &ndash; Tools that shape how I work - editors, CLIs, SSH, local setup.
<em>Use when:</em> the post is hands-on with a specific tool or workflow.</p>
<p><strong><code>writing</code></strong> &ndash; Reflections on the writing process, publishing, and finding my voice.
<em>Use when:</em> the post is about writing itself, not just a post that happens to be well-written.</p>
]]></content:encoded></item><item><title>Making Music - Day 2 GarageBand</title><link>https://westernwilson.com/reflections/2025-08-13-music-day-2/</link><pubDate>Wed, 13 Aug 2025 00:00:00 +0000</pubDate><guid>https://westernwilson.com/reflections/2025-08-13-music-day-2/</guid><description>&lt;p&gt;This blog aims to convince you of two things:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Start that “thing” today—not tomorrow, and not next week.&lt;/li&gt;&#10;&lt;li&gt;Getting started doesn’t require structure, an official tutorial, or any special gear.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="setting-the-scene" class="group relative scroll-mt-20"&gt;&#10; ‍Setting the scene&#10; &lt;a&#10; href="#setting-the-scene"&#10; class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"&#10; aria-label="Link to this heading"&#10; &gt;&lt;i class="fa-solid fa-link" aria-hidden="true"&gt;&lt;/i&gt;&lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;I’ve got no idea what I’m doing. I don’t know what software to use, but I do know that I don’t want to pay for anything—at least not until I’m sure making music is something I actually want to pursue.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This blog aims to convince you of two things:</p>
<ol>
<li>Start that “thing” today—not tomorrow, and not next week.</li>
<li>Getting started doesn’t require structure, an official tutorial, or any special gear.</li>
</ol>
<h2 id="setting-the-scene" class="group relative scroll-mt-20">
  ‍Setting the scene
  <a
    href="#setting-the-scene"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>I’ve got no idea what I’m doing. I don’t know what software to use, but I do know that I don’t want to pay for anything—at least not until I’m sure making music is something I actually want to pursue.</p>
<p>First things first, I set out to find the &ldquo;best&rdquo; software to experiment with, without breaking the bank. For me, &ldquo;best&rdquo; meant: no cost, offline mode, a UI that isn’t ugly or archaic, easy-ish to experiment with, and something with learnings that transfer to a more advanced product later on.</p>
<p>In the past I tried Spotify&rsquo;s web UI music creation tool, but it was only online so could only be used while I had internet, the lag wasn&rsquo;t fun and it cost money quite quickly after the trial.</p>
<p>I like <a href="https://www.fredagain.com/">Fred again&rsquo;s</a> music, so I wondered what software he uses - apparently he&rsquo;s a Logic Pro pro. Perplexity reckons Logic Pro is basically an overpowered and extended version of GarageBand. GarageBand comes for free with Macbook&rsquo;s OS…so I went with GarageBand.</p>
<h2 id="getting-started" class="group relative scroll-mt-20">
  ‍Getting started
  <a
    href="#getting-started"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h2>
<p>At this point, I would normally pause there and think &ldquo;job done&rdquo;, lets continue tomorrow.
I have two reasons why I didn&rsquo;t want to pause:</p>
<ol>
<li>Recent content I&rsquo;ve been consuming has encouraged more personal agency. Books like 4-hour workweek or Modern Wisdom&rsquo;s podcast interview with Naval Ravikant.</li>
<li>I read numerous articles of people starting with the only the simplest tools, making music only off their phone</li>
</ol>
<p>So, using Atomic Habits tricks, I told myself to make music for 5 minutes.
Without needing to even downloaded the software, I spend over an hour, hopping around the interface, trying to make a simple beat with the software MIDI controller.</p>
<p>No theory, no structured learning, no tutorials, just dive in and try it out.
This worked well for me, and here is the first beat I made:</p>
<p><a href="https://soundcloud.com/westernwilson/music-day-1">https://soundcloud.com/westernwilson/music-day-1</a></p>
<p>Before this, I didn&rsquo;t know what a MIDI controller was or what a DAW was. (MIDI stands for Musical Instrument Digital Interface and DAW is what GarageBand is, and it stands for Digital Audio Workstation). Now I kinda do???</p>
<p>Honestly, taking a course or chatting with someone more experienced might be a good idea soon. I’ve thought about reaching out to Dera Meelan—someone I’ve enjoyed since hearing his track “Fire Sale” with Church &amp; AP.</p>
<p>A few days past and here was my second beat:</p>
<p><a href="https://soundcloud.com/westernwilson/music-day-2">https://soundcloud.com/westernwilson/music-day-2</a>
‍
If you have any pointers, reach out on any socials or at <a href="mailto:kiaora@westernwilson.com">kiaora@westernwilson.com</a> 🙏</p>
<h3 id="final-note" class="group relative scroll-mt-20">
  ‍Final note
  <a
    href="#final-note"
    class="heading-anchor ml-2 text-sm no-underline! opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 [@media(hover:none)]:opacity-100 [color:var(--c-muted)] hover:[color:var(--c-accent)] transition-opacity"
    aria-label="Link to this heading"
  ><i class="fa-solid fa-link" aria-hidden="true"></i></a>
</h3>
<p>There are many successful podcasters whose early podcasts sounded like they were recorded in a bedroom closet, sounding unpolished with bad scripts/questions. But no shade, I think that’s how it should be, that’s how you should start.</p>
<p>Point is, you can start today, and you can bootstrap it with minimal investment. Don’t spend on the best equipment if you’re not even remotely sure you’ll want to stick with it in a year; heck, you might quit after a couple of weeks.</p>
<p>Persevere with whatever you&rsquo;ve got going on. Good luck, and stay curious!</p>
]]></content:encoded></item></channel></rss>