The Applied Go Weekly Newsletter logo

The Applied Go Weekly Newsletter

Archives
Subscribe
September 9, 2026

The Joy of Hand-Knitting Go Code

AppliedGoNewsletterHeader640.png

Your weekly source of Go news, tips, and projects

The joy of hand-knitting Go code

Hi ,

Back from the summer break (or winter break, if you live in the southern hemisphere)! The most important thing that happened during the break was the release of Go 1.27 that brought generic methods, goroutine leak profiling, simpler struct literals for embedded or nested structs, generalized type inference for generic functions, and more. (The Go blog covers the first two update in their latest posts.)

For me, the newsletter break was also a temporary pause of Go activities; I had to run too many other errands such as helping to renovate my parents' house or going on multiple business-related travels. But now, my focus is back on Go, and I have collected a number of ideas for upcoming Spotlight articles, so stay tuned. For this release, I pick up the most recent Spotlight article, where I shared my Microsandbox script for generating a sandboxed Go development environment (PoC). I turned the script into a Dockerfile so that the whole setup runs at image creation time. Whenever I want to create a sandbox for a local repository, the sandbox is up and running in an instant.

I have also been thinking a lot about how AI may influence the future of Go. I don't see that the choice of a language stops to matter in times of agentic coding; rather, I think that AI and Go will interfere with each other in new and interesting ways. I'll keep an eye on the developments around AI-assisted coding in general while running a few specific experiments for myself.

For example, I vibe-coded qm, a helper tool for Quarto, a text processor that wraps Pandoc (the swiss army knife of doc format conversion) nicely into a user-friendly publishing system. My tool adds a few commandline tools and a web UI for sorting chapters and editing content. (Quarto sorts chapters through an order field in the frontmatter, which makes the files appear unordered on disk unless you want to prefix each file name with an order number, but then, if you insert a new chapter, you'd have to rename all subsequent files to keep the file order in line with the order in the frontmatter. Geez, this approach calls for making errors, so I prefer having an intuitive web tool for sorting.)

The experiment has been going quite well so far, but I attribute a lot of the success to the project's simple structure and plain requirements. For small, probably throwaway tools that have to be whipped up with almost no time budget, vibe coding is an option. It remains to be seen if iterative use of AI in larger projects does generate the amounts of tech debt anticipated by many.

But to me, the more interesting angle of "AI and Go" is how to make use of Go as a driver of AI-equipped apps and tools. I already added an LLM call to my newsletter helper tool that summarizes podcast transcripts so that I know within seconds what a given episode is about. (I don't have any LLM deployed for actual writing, because LLMs are still, and most probably continue to be measly writers. (This being said, I am aware that as a non-native speaker, I am not going to collect Pulitzer prizes, either, but at least, I hope my writing style is at least recognizably human, and it surely is recognizably mine.)) (<- Yes, two closing parens. If you are a Real Programmer™, you probably even didn't consider my nesting of two parenthesized sentences as something unusual.) I can imagine that Go is a great basis for little helper apps that utilize LLMs for non-determinstic tasks like scanning and categorizing emails or email attachments, extracting information from unstructured data, and more, without creating a Lethal Trifecta situation.

But with all those AI developments around us, we should take care to not lose the joy of hand-knitting Go code.

In this spirit, happy coding!

–Christoph

Featured articles

Generic Methods in Go

Finally, generic methods made it into Go. More precisely, Go 1.27 supports type parameters on concrete methods. Generic interface methods aren't allowed to the party; they're next to impossible to support. Assume you write a (hypothetical) package with generic interface methods. The compiler cannot anticipate what concrete types the package client would use to instantiate your generic interface methods, so it would have to generate machine code for every possible parameter. The alternative, boxing, leads to runtime overhead: The code would be the same for all types, but it would add an extra decision point at runtime that the Go team isn't willing to support.

Goroutine Leak Profiles - The Go Programming Language

Design decisions can be hard: You could end up with a watertight design that is incredibly complex and cumbersome to use, or a design that's flexible but comes with one or two footguns. Go's concurrency primitives are quite flexible, so there's no guessing which camp they belong to. Goroutines can leak if not used properly, and it's quite easy to spawn thousands of goroutines that never end, consuming memory and resources for the rest of the process' lifetime. Go 1.27 equips the developer with a new tool to track down those undesired leaks.

Podcast corner

Everything You Always Wanted to Know About Go 1.27 But Were Afraid to Ask

Even if you're not afraid to ask, this Cup o' Go episode is worth checking out.

Go releases and events from EMEA and around the world

While the previous episode was all about Go 1.27, Jonathan and Shay now catch up with all the, well, Go releases and events from around the world, as the title says. My favorite update: TinyGo 0.42.

Spotlight: My Current Go-Dev Dockerfile for Microsandbox

In the previous Spotlight article, I examined Microsandbox, a microVM with a straightforward CLI and a Go SDK, for running Go development tasks in a microVM. Why using a microVM? In short, a microVM is an excellent security enhancement, as it isolates guest processes from the host system at a fairly high level and with a very small attack surface compared to other approaches like containers or hypervisors (only topped by WASM and isolates, but these cater to a different category of use cases).

The beginnings

My initial approach was about getting quick results, so I created the microVM with a standard image (ubuntu) and installed everything I needed through a setup script, after creating a VM instance. Then I could stop and start that instance without having to re-run this script. However, whenever I had to create a new instance because I needed an instance mounted to another local folder, I had to run the script again.

From script to Dockerfile

At one point, this became annoying enough to push the setup step to an earlier point in the setup chain: I turned the script into a Dockerfile and generated a custom image. Now I only have to rebuild the image when my setup changes. I can create a new Microsandbox VM in a second and start working with it right away.

Here is a quick rundown of the current Dockerfile:

I start with the usual FROM <image/> line. I use Ubuntu because that's what I used for my initial experiments; I could try OS images with a smaller footprint like Alpine but that's not a top priority for me at the moment. The DEBIAN_FRONTEND and TZ settings allow install steps to run through without user interactions and

FROM ubuntu:latest

ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Berlin

The default user in a Dockerfile is root. That is, when you run commands like RUN, they are executed on the base image as root. I use this to do the installations that require root rights:

RUN apt-get update && \
    apt-get install -y curl && \
    GO_VERSION=$(curl -Ls https://go.dev/VERSION?m=text | head -n 1) && \
    arch="$(uname -m)" && \
    case $arch in arm64|aarch64) arch=arm64 ;; esac && \
    case $arch in x86-64|x86_64) arch=amd64 ;; esac && \
    curl -so /tmp/go.tar.gz -L https://go.dev/dl/${GO_VERSION}.linux-$arch.tar.gz && \
    tar -C /usr/local -xzf /tmp/go.tar.gz && \
    rm /tmp/go.tar.gz && \
    apt-get install -y neovim --no-install-recommends && \
    apt-get install -y git && \
    apt-get install -y gcc && \
    rm -rf /var/lib/apt/lists/*

With these tools in place, I can swith to a non-root user and continue the setup. I name my user ubuntu. Then I set up a few environment settings and add some paths to $PATH that some installation scripts don't add by their own.

ENV HOME=/home/ubuntu
USER ubuntu
WORKDIR /home/ubuntu

ENV PATH=/usr/local/go/bin:$PATH

RUN echo 'export PATH=$PATH:/usr/local/go/bin:/home/ubuntu/go/bin' >> /home/ubuntu/.bashrc && \
    echo 'export PATH=$PATH:$HOME/.local/bin' >> /home/ubuntu/.bashrc \
    echo 'export PATH=$PATH:$HOME/.local/share/opencode/bin' >> /home/ubuntu/.bashrc \
    echo 'export COLORTERM=truecolor' >> /home/ubuntu/.bashrc


RUN mkdir -p /home/ubuntu/.local/share 

Now, the Dockerfile can run all install scripts that can (or need to) run in userspace. I always install a bunch of useful Go tools and also one or two AI agent harnesses. The uvx tool often comes in handy for running tools from the Python ecosystem without installing them; it's included in the uv package.

My Go tool selection usually includes tools for checking code: gopls, govulncheck, gosec, staticcheck, and more.

RUN curl -fsSL https://opencode.ai/install | bash
RUN curl -fsSL https://claude.ai/install.sh | bash
RUN curl -fsSL https://astral.sh/uv/install.sh | bash


RUN curl -SfL https://golangci-lint.run/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.12.2 && \
    go install golang.org/x/tools/gopls@latest && \
    go install golang.org/x/vuln/cmd/govulncheck@latest && \
    go install github.com/securego/gosec/v2/cmd/gosec@latest && \
    go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest && \
    go install github.com/go-critic/go-critic/cmd/go-critic@latest && \
    go install honnef.co/go/tools/cmd/staticcheck@latest && \
    go install github.com/fzipp/gocyclo/cmd/gocyclo@latest && \
    go install github.com/samber/godig/cmd/godig@latest && \
    go clean -cache && go clean -modcache

Finally, I set the workspace to the directory that the Dockerfile bind-mounted to the current local directory earlier, and have the microVM invoke bash on startup.

WORKDIR /home/ubuntu/workspace

CMD ["bash"]

From Dockerfile to Microsandbox image

Now, there are only a few steps between this point and a working Microsandbox image. Typically, you would build the image from the Dockerfile, upload the image to a registry, and create a Microsandbox VM with that image.

But not everyone has access to a remote container registry service (or doesn't want to). Luckily, both Docker and Microsandbox allow creating and consuming local container images.

Here are the steps to build a Microsandbox image locally:

1. Build the container image

Run

docker build --tag go-dev .

(If the latest changes don't seem to show up in the image, docker might have skipped a cached step; you can see this in the Docker build output. In this case, add the --no-cache flag.)

2. Transfer the image from Docker to Microsandbox

Microsandbox (at version 0.6.17) cannot see images of a local Docker installation. To enable access, I save the image from Docker into a tar file and load this file into Microsandbox.

docker image save go-dev:latest -o go.dev.tar
msb load -i go.dev.tar

These steps take a few seconds, depending on the image size.

3 Instantiate a microVM

Now you can instantiate a new microVM with a bind-mount to the current local directory and some other settings as desired.

The following example creates a VM with name myrepo and a bind mount (-v) to the current file, default user ubuntu, six CPUs at max and 4G memory at max, and a secret GITHUB_TOKEN environment variable read from gopass (that has to be unlocked beforehand).

A word on secrets: Secrets are a security measure to avoid exposing sensible environment variables to the guest system. Instead, processes inside the mivroVM only see a dummy value. When they send a request containing that dummy value, Microsandbox replaces it on the fly with the actual value taken from an environment variable set on the host system. A smart and quite efficient security measure! In the following example, I set a GITHUB_TOKEN secret that commands can use to interact with GitHub.

Tip: for the export command, I usually don't paste the actual token to the command, to avoid that it is saved to the Bash history.

Putting a space before the export command usually prevents it from being added to the history; however, this is easy to forget, so I use a secrets manager named gopass to inject the secret like so: export GITHUB_TOKEN=$(gopass github_token). This works with any secrets manager that has a suitable CLI command for printing out secrets (and that is unlocked when the export command runs).

cd myrepo
 export GITHUB_TOKEN=...

msb create --name myrepo \
    --secret GITHUB_TOKEN@api.github.com \
  -u ubuntu \
  -w /home/ubuntu/workspace \
  -v "$(pwd)":/home/ubuntu/workspace \
  -c 6 \
  -m 4G \
  go-dev

Done! Now the Microsandbox instance is ready to use.

> msb exec qm
ubuntu@qm:~/workspace$ whoami
ubuntu
ubuntu@qm:~/workspace$ go version
go version go1.27.0 linux/arm64
ubuntu@qm:~/workspace$  

More articles, videos, talks

The new Go JSON API: twice as fast, or 1.5x slower? – Daniel Lemire's blog

A showdown between three JSON packages: "legacy" (that is, Go's JSON package up to Go 1.27, the Go 1.27 re-implementation of it, and json/v2 that ships in the standard library by default since Go 1.27. No one would expect the legacy version to win any medal, but how does its modernized alter ego compare against json/v2?

The Bucket Is the Log: Building an Append-Only Log on Object Storage in Go — Ankur Anand

Or: How UnisonDB replication problems led to a brokerless logging package for object storage (read: S3 buckets).

DTLS 1.3 in Go: An Implementer’s Perspective - Pion

Put TLS, the transport encryption protocol for the web (and more), on top of UDP instead of TCP, and you get DTLS. This TLS variant sparked the interest of Theodor Midtlien, Jo Turk, Adriano Sela Aviles, R Chiu, and Sean DuBois, makers of Pion, a real-time media and data communication stack (written in Go, in case anyone asks).

TinyGo 0.42 - Recover Is Real

This seems a good time to draw some attention to TinyGo: The latest release has (finally!) implmented defer and recover; a long-standing request by TinyGo users. And do you know what I love about this release? I can finally pull my old ESP32 boards out again; I never really used them for any project (not even an unserious one), but now that TinyGo added support for WIFI (among other things like interrupt support, an ADC driver, etc), there's no more reason to let my ESP32s rot in the drawer.

How to Handle Errors in Go

Oooh, now that's a nice surprise: The blog article I once wrote for JetBrains got polished up to include the latest additions to Go error handling, such as errors.AsType(). Good to see that JetBrains doesn't let older but still relevant Blog articles fade away.

How Go’s Built-in Map Works with Swiss Tables

Technical articles that dig deep into language internals are often somewhere between boring and intimidating. This article wants to be more accessible by using lots of diagrams for a visual approach.

Debian Code Search: Fast TurboPFor with Go SIMD

How Go's new SIMD support helped eliminating the last CGO dependency from Debian Code Search.

Projects

Libraries

Go-Gen-Ecosystem/halolog: Zero-allocation structured logging for Go - 0 allocs/op on the hot path, guarded by committed tests

The author lists a few reasons why someone would use HaloLog over slog, and they do go beyond bare performance.

Tools and applications

GoCraft-MC/GoCraft: Minecraft Server fully Written in GO ORIGINAL REPO

If you ever wanted to write plugins for Minecraft but weren't looking forward to dive into the Java world, watch this project.

maxbotlabs/discord-volume-toggle

It's "Discord", not "disco", and being loud isn't the goal. What's more natural, then, than a button that turns down the volume in 25% steps from 100% to 0.

Completely unrelated to Go

Using Scoped Coverage to Prune an AI-Generated Go Test Suite

Classic test coverage metrics work like traffic surveillance that counts vehicles without distinguishing between types: If function F is called in scenario A but never in scenario B, coverage measure for function F would never reach 100%. Scoped coverage takes different calling scenarios into account.

This article explores an interesting use case for scoped coverage: Finding out if AI coding assistants over-eagerly generated tests that overlap or are outright duplicates.

De-Googling: Replacing Google Search with Kagi

I share this article not only because it reflects my own positive experience with Kagi (I'm a happy customer for months), but also because it is a reminder of the inherent danger of relying on the services of a large corporation—especially if you're not their customer.

Never put all your eggs in one basket.

How to protect yourself from workslop

Tired of the increasing amounts of AI slop your coworkers pour over you instead of taking a minute to write a few, clear lines by hand? Defend yourself from their disrespect of your time and nerves! Here are some tips to get you started.

What B2B SaaS taught me about having kids

Ha! I would have expected the reverse title: "What kids taught me about managing a B2B SaaS." But in fact, Swizec Teller experienced this the other way round.

Babies are like [SaaS startups]. They fuck up your relationship, throw a bomb at your routines, and disrupt everything in your house. I didn't even know it was possible to own so many tools and appliances. There's baby bottles everywhere.

The irrational effectiveness of the Pi harness

With the raise of AI coding agents, the choice of an agent harness has become quite relevant. Contrary to what one might think, the most feature-rich harness isn't necessarily the one that fits a developer's needs.

Happy coding! ʕ◔ϖ◔ʔ

Questions or feedback? Drop me a line. I'd love to hear from you.

Best from Munich, Christoph

Not a subscriber yet?

If you read this newsletter issue online, or if someone forwarded the newsletter to you, subscribe for regular updates to get every new issue earlier than the online version, and more reliable than an occasional forwarding. 

Find the subscription form at the end of this page.

How I can help

If you're looking for more useful content around Go, here are some ways I can help you become a better Gopher (or a Gopher at all):

On AppliedGo.net, I blog about Go projects, algorithms and data structures in Go, and other fun stuff.

Or visit the AppliedGo.com blog and learn about language specifics, Go updates, and programming-related stuff. 

My AppliedGo YouTube channel hosts quick tip and crash course videos that help you get more productive and creative with Go.

Enroll in my Go course for developers that stands out for its intense use of animated graphics for explaining abstract concepts in an intuitive way. Numerous short and concise lectures allow you to schedule your learning flow as you like.

Check it out.


Christoph Berger IT Products and Services
Dachauer Straße 29
Bergkirchen
Germany

Don't miss what's next. Subscribe to The Applied Go Weekly Newsletter:
← Newer Depth • The Applied Go Weekly Newsletter 2026-09-13 Older → A (Sand)Box Of Go • The Applied Go Weekly Newsletter 2026-08-11
Share this email:
Share on LinkedIn Share on Mastodon
Mastodon
LinkedIn
Powered by Buttondown, the easiest way to start and grow your newsletter.