<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Thinking on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/thinking/</link><description>Recent content in Thinking on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 04 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/thinking/index.xml" rel="self" type="application/rss+xml"/><item><title>Site Navigation — Beyond the Menu</title><link>https://backend-engineering-strategy-tools.github.io/site/thinking/site-navigation/</link><pubDate>Thu, 04 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/thinking/site-navigation/</guid><description>&lt;p&gt;A menu works when the content is shallow and the audience knows what they want. It breaks down when the content grows into something more like a knowledge base — when you have 80 notes across 12 sections and the useful thing is not finding a specific page but discovering that two ideas are connected.&lt;/p&gt;
&lt;p&gt;There are a few distinct problems here and they need different tools.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="retrieval-vs-exploration"&gt;Retrieval vs exploration
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Retrieval&lt;/strong&gt; is when you know what you want: &amp;ldquo;where did I write about Terragrunt?&amp;rdquo; A search box solves this. Fuzzy matching, title weighting, done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exploration&lt;/strong&gt; is when you do not know what you want, or want to rediscover something you wrote a while ago. A menu does not help. Search does not help — you cannot search for something you have forgotten exists.&lt;/p&gt;
&lt;p&gt;The menu is for retrieval by navigation. Search is for retrieval by keyword. Neither is for exploration.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-does-not-work"&gt;What does not work
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Most recently used / most popular&lt;/strong&gt; — this feels like someone keeps moving your things. The notes that surface are the ones you or others have looked at lately, not the ones that are useful to you now. Navigation should not have memory that works against you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alphabetical listing&lt;/strong&gt; — fine as a fallback, not useful as a primary navigation mode. Proximity means nothing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="two-experiments"&gt;Two experiments
&lt;/h2&gt;&lt;h3 id="mindmap"&gt;Mindmap
&lt;/h3&gt;&lt;p&gt;Every note as a node. Edges connect notes that share tags or belong to the same section. Lay it out with a force-directed simulation and you get a visual map of the knowledge base — clusters emerge naturally, isolated notes stand out, and you can trace paths between related ideas.&lt;/p&gt;
&lt;p&gt;The interesting property: clicking on one note shows which other notes it is connected to. This is not search — you are not looking for something specific, you are seeing the neighbourhood of an idea.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/mindmap/" &gt;Try it →&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="word-cloud"&gt;Word cloud
&lt;/h3&gt;&lt;p&gt;Sections and tags, sized by how many notes carry them. The big words are the areas where there is the most material. Click a word and it feeds directly into the search.&lt;/p&gt;
&lt;p&gt;This is an overview of the shape of the knowledge base. Good for answering &amp;ldquo;what is actually in here?&amp;rdquo; in a few seconds.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/wordcloud/" &gt;Try it →&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-shared-data-model"&gt;The shared data model
&lt;/h2&gt;&lt;p&gt;Both visualisations run off the same JSON index that powers search — a build-time output from Hugo listing every note with its title, URL, section, tags, and summary. One data source, three interfaces.&lt;/p&gt;
&lt;p&gt;The technical implementation is in &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/docs-as-code/site-navigation/" &gt;Notes: Site Navigation&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-is-still-missing"&gt;What is still missing
&lt;/h2&gt;&lt;p&gt;Tags are sparse — most notes do not have them, so the mindmap edges are mostly section-based rather than cross-section. Adding tags to notes as they are written would make the mindmap significantly more interesting over time.&lt;/p&gt;
&lt;p&gt;The word cloud currently surfaces sections more than tags for the same reason. As tags accumulate, it will shift toward showing the actual concepts rather than just the categories.&lt;/p&gt;
&lt;p&gt;Both are experiments. The interesting question is whether they change how notes get written — if knowing that a note will appear in a connected graph encourages tagging, or whether tagging feels like overhead.&lt;/p&gt;</description></item><item><title>These Are Not the Pipelines You Are Looking For</title><link>https://backend-engineering-strategy-tools.github.io/site/thinking/one-command-any-pipeline/</link><pubDate>Thu, 04 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/thinking/one-command-any-pipeline/</guid><description>&lt;p&gt;There are a lot of &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/platforms/" &gt;CI/CD platforms&lt;/a&gt;. GitHub Actions, Tekton, Jenkins, Jenkins X, Harness, Bitbucket Pipelines, GitLab CI, CircleCI, Argo Workflows. The choice between them is a perennial argument — and mostly the wrong one.&lt;/p&gt;
&lt;p&gt;The mistake is putting your build logic inside the pipeline. Once you do that, the pipeline owns your build. Reproducing a failure locally means setting up the CI environment. Switching platforms means rewriting all the steps. Testing the pipeline means pushing a commit and waiting. The platform becomes load-bearing.&lt;/p&gt;
&lt;p&gt;The fix is straightforward: keep all logic in &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/make/" &gt;Make&lt;/a&gt; or &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/dagger/" &gt;Dagger&lt;/a&gt;. The pipeline calls one command. &lt;code&gt;make build&lt;/code&gt;. &lt;code&gt;make test&lt;/code&gt;. &lt;code&gt;dagger call publish&lt;/code&gt;. That&amp;rsquo;s it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The pipeline shrinks to thin orchestration: trigger on a git event, check out the code, call the command, report the result. It does not know what the build does. It does not care. You can swap GitHub Actions for Tekton or Tekton for Argo Workflows and the only thing that changes is the YAML wrapper around the same command.&lt;/p&gt;
&lt;p&gt;More importantly: you can run the same command on your laptop. No CI environment to replicate, no &amp;ldquo;it works locally but fails in CI&amp;rdquo; gap to debug. The build is the build, wherever it runs.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This matters more the longer a project lives. Build tools consolidate and diverge. Platforms get acquired, deprecated, or repriced. Jenkins pipelines from five years ago are maintained by people who weren&amp;rsquo;t there when they were written and can&amp;rsquo;t run them outside of Jenkins. The projects that survive these transitions cleanly are the ones where the build logic was never in the pipeline to begin with — it was in a Makefile or a build tool that could run anywhere.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/build-systems/" &gt;Gradle&lt;/a&gt;, Maven, Make — these predate CI/CD platforms by decades and will outlast the current generation of them. Put your logic there. The pipeline is a trigger and a reporter. Keep it that way.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The pattern in practice:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-makefile" data-lang="makefile"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; docker build -t myimage:&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;VERSION&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; go test ./...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;publish&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; docker push myimage:&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;VERSION&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# GitHub Actions — the entire pipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;make build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;make test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;make publish&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Tekton — same command, different wrapper&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;build-test-publish&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;golang:1.22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;script&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; make build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; make test
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; make publish&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Same logic. One command. Any pipeline.&lt;/p&gt;</description></item><item><title>Shared Tooling Images — One Image, Three Contexts</title><link>https://backend-engineering-strategy-tools.github.io/site/thinking/shared-tooling-images/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/thinking/shared-tooling-images/</guid><description>&lt;p&gt;The problem with per-context tooling: CI uses one version of a linter, a developer&amp;rsquo;s machine has another, a colleague has a third. Someone upgrades locally, the pipeline fails. Someone pins the pipeline, local runs drift. The maintenance surface is the number of places you install tools multiplied by the number of people on the team.&lt;/p&gt;
&lt;p&gt;The fix is one Docker image that travels across all three contexts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CI/CD&lt;/strong&gt; — the pipeline pulls the image, runs the same tools&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local development&lt;/strong&gt; — developers run the image instead of installing tools natively&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Colleagues&lt;/strong&gt; — same image, same versions, no setup docs to go stale&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you need to upgrade a tool, you change one Dockerfile and cut a new tag. Everyone gets it on next pull. No coordination required.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-docker"&gt;Why Docker?
&lt;/h2&gt;&lt;p&gt;There are other ways to solve this — dotfiles, Nix, OS package managers, VMs, cloud-init. All valid in different contexts.&lt;/p&gt;
&lt;p&gt;The reason Docker makes sense here is that we are already working in a Kubernetes ecosystem where containers are the starting point. Most CI systems support Docker and OCI-compatible images natively. So the question becomes: is it worth introducing a different tool management approach alongside containers, or is it less costly to stay consistent?&lt;/p&gt;
&lt;p&gt;Personally, I lean toward staying consistent. Your mileage may vary depending on your stack.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-goes-in-the-image"&gt;What Goes in the Image
&lt;/h2&gt;&lt;p&gt;The principle: include anything that needs to be consistent across contexts. Linters, formatters, build tools, CLI utilities.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Not the runtime — that is the application&amp;rsquo;s own image.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What specifically goes in depends on the target platform. A Kubernetes-only image looks different from one that also includes a cloud provider CLI. Adding tools increases the maintenance burden, so keep each image focused. That is also why there are multiple images — see &lt;a class="link" href="#the-repos" &gt;The Repos&lt;/a&gt; below.&lt;/p&gt;
&lt;p&gt;Typical candidates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;helm&lt;/code&gt;, &lt;code&gt;kustomize&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Linters and formatters for the languages in use&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;yq&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt; and similar utilities&lt;/li&gt;
&lt;li&gt;Cloud provider CLI where needed (&lt;code&gt;aws&lt;/code&gt;, &lt;code&gt;openstack&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="versioning"&gt;Versioning
&lt;/h2&gt;&lt;p&gt;Follow SemVer — image tag equals version. Merge to main triggers a release. No need to complicate it beyond trunk-based development.&lt;/p&gt;
&lt;p&gt;Before merging, build and publish an image from the branch so the change can be tested and verified before it lands on mainline. The branch image is short-lived and not promoted to &lt;code&gt;latest&lt;/code&gt; until the merge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="usage-patterns"&gt;Usage Patterns
&lt;/h2&gt;&lt;p&gt;A shell alias or Makefile target wraps the &lt;code&gt;docker run&lt;/code&gt; so nobody has to remember the full invocation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias toolbox&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;docker run -it --rm -v &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;pwd&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:/work -w /work image:latest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then &lt;code&gt;toolbox helm lint .&lt;/code&gt; or &lt;code&gt;toolbox kubectl apply -f .&lt;/code&gt; works the same locally as it does in CI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Interactive sessions&lt;/strong&gt; are where this pattern pays off beyond CI. Drop into a persistent terminal with the right context already loaded — kubeconfig mounted in, cloud credentials available, working directory set:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -it --rm &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -v &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;pwd&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;:/work &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -v ~/.kube:/root/.kube:ro &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -w /work &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image:latest bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You get a shell that looks the same for everyone on the team, regardless of what is installed on their machine.&lt;/p&gt;
&lt;p&gt;For interactive use it is worth including a help script that runs on entry — either via &lt;code&gt;ENTRYPOINT&lt;/code&gt; or by sourcing it from &lt;code&gt;.bashrc&lt;/code&gt; inside the image. It should print what tools are available, their versions, and any common usage examples. Keeps the image self-documenting: a new colleague drops in and immediately knows what they have to work with without reading a README.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;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;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# example /usr/local/bin/toolbox-help&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;=== Toolbox ===&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;kubectl &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl version --client -o json | jq -r &lt;span style="color:#e6db74"&gt;&amp;#39;.clientVersion.gitVersion&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;helm &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;helm version --short&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;aws &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;aws --version 2&amp;gt;&amp;amp;1&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Run &amp;#39;toolbox-help&amp;#39; at any time to see this again.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In CI the step just references the image directly — no installation step, no version pinning in the pipeline config beyond the image tag.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-repos"&gt;The Repos
&lt;/h2&gt;&lt;p&gt;Three tooling images, each a superset of the previous, plus two supporting images:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;GitHub repo&lt;/th&gt;
 &lt;th&gt;Docker Hub&lt;/th&gt;
 &lt;th&gt;Contents&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;image-tooling&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/tooling-k8s&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;kubectl, helm, kustomize, argocd, k9s, jq, yq&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;image-tooling&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/tooling-k8s-aws&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;tooling-k8s&lt;/code&gt; + AWS CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;image-tooling&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/tooling-k8s-openstack&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;tooling-k8s&lt;/code&gt; + OpenStack CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;image-buildx&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/buildx&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;CI builder — Docker buildx, AWS CLI, Dagger CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;image-pandoc&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/pandoc&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PDF generation — pandoc + TeX Live&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Repo names use &lt;code&gt;image-&amp;lt;purpose&amp;gt;&lt;/code&gt; in the &lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools" target="_blank" rel="noopener"
 &gt;Backend-Engineering-Strategy-Tools&lt;/a&gt; org. Docker Hub names drop the prefix and just describe the tool. The three tooling flavours share one &lt;code&gt;image-tooling&lt;/code&gt; monorepo so dependency updates and scanning are configured once.&lt;/p&gt;
&lt;p&gt;Pipelines are written in Go using &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/dagger/" &gt;Dagger&lt;/a&gt; — the same pipeline runs locally and in CI, publishing multi-arch images (amd64 + arm64) triggered by a version tag.&lt;/p&gt;
&lt;hr&gt;</description></item></channel></rss>