The Applied Go Weekly Newsletter logo

The Applied Go Weekly Newsletter

Subscribe
Archives
September 23, 2025

Where Code Bloat Goes To Die • The Applied Go Weekly Newsletter 2025-09-21

AppliedGoNewsletterHeader640.png

Your weekly source of Go news, tips, and projects

where-code-bloat-goes-to-die.png

Where Code Bloat Goes To Die

Hi ,

Small is beautiful! This is true for code, too. The less code you have, the less maintenance you have to do. To prove that code bloat isn't something that can't be avoided, I set out to build a minimalist SaaS platform where every line of code counts. The first part starts in this issue's Spotlight section. I wonder where this will go...

–Christoph

Featured articles

how fast is go? simulating millions of particles on a smart tv

What? Running benchmarks to measure your app's speed? Nah. Better launch a firework of pixels on your TV set and watch them spinning and swirling.

Memory Allocation in Go | Melatoni

Not the kind of knowledge you'd need for daily programming, but it's super interesting to learn what's going on under the hood.

Terminating elegantly: a guide to graceful shutdowns

Graceful Shutdown [n]: The generous act of properly releasing resources and letting ongoing tasks finish before ceasing operations. Now with Kubernetes peculiarities!

It's survey time! How has Go has been working out for you? - The Go Programming Language

Take it! Take it! Give the Go world useful stats! The Go Developer Survey 202 is open.

Podcast corner

Cup o' Go | 🪖 Gab's Notes on this week's Go news and an interview with Matt Boyle of Ona and ByteSizeGo

Gabriel Augendre as co-host and Matt Boyle as interviewee—quite hard to not click the play button!

Fallthrough | Building For The Future

Software that runs for years... without frequent rewrites? Can this be possible? The Fallthrough board tries to uncover the secrets of building software that lasts.

Spotlight: Go SaaS Lab 01: A Not-So-Minimal Web Server

Software bloat often begins with architectural decisions. The software shelves at GitHub spill over with ready-made components of all kind, ready for the convenience shopper to grab. In no time, a software project that started out as a simple idea turns into a behemoth of services, infrastructure, and interdependencies.

And no one understands anymore what's going on.

Go minimal

With this project, I want to go the opposite route: To build a platform with just enough features to serve as a web application platform or a web presence for a small business, and to actually understand what the code does.

So here is a counterexample to the plethora of feature-loaded content management systems (CMSs): A super-minimal web server.

Well, it's not so super-minimal, as I added three features that the standard minimal web server examples omit. Two are crucial for any web server: proper timeouts and graceful shutdown. The third is a drastic simplification for the deployment.

But let's dig into the code.

A web server with an embedded file system

The web server shall serve just static HTML files, and nothing else. So I'll start with one of the aforementioned extra features: an embedded file system. The embed package lets you bake files and directories into the binary, for true single-binary deployment even if you need to deploy some non-Go files along with it. A //go: directive and the declaration of an embedded file system variable are all that's needed:

//go:embed web/public/*
var fileSys embed.FS

Well, there's one more thing to do: In func main, I need to extract the public directory as a sub-filesystem; otherwise, the files would be served at /web/public rather than at the base URL /:

    publicFS, err := fs.Sub(fileSys, "web/public")

Now I can set up a multiplexer and a handler that serves the sub-filesystem:

    mux := http.NewServeMux()
    mux.Handle("GET /", http.FileServerFS(publicFS))

Adding timeouts

Next step: create the HTTP server. I will not use the default server in net/http, as it lacks one of the crucial functionalities mentioned above: proper timeouts.

Timeouts are possibly the most dangerous edge case to overlook.

–Filippo Valsorda

Adding them is simple, though:

    srv := &http.Server{
        Addr:         ":8080",
        Handler:      mux,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 30 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

(Source)

Done! These three lines protect your server against slow clients and stalling connections. The settings are static and don't take varying payloads for different URL paths into account, but it's a start.

Now the server is ready to run:

    go func() {
        log.Printf("Server starting on http://localhost%s", srv.Addr)
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server failed: %v", err)
        }
    }()

Be nice to clients when shutting down

Finally, I'll add some logic for gracefully shutting down the server if the operating system sends a SIGTERM signal. A SIGTERM signal is sent to an app running in a terminal when you hit Ctrl+C, or to a background app through calling kill <pid> or killall <process_name>.

The following code uses the os/signal package to catch a SIGTERM signal. Function signal.Notify() sends a value through a channel once it receives a signal.

The code after the Notify() call waits on the channel before it calls the Shutdown() method from net/http.Server to close all listeners and idle connections and then wait indefinitely for active connections to return to idle. Well, indefinitely unless the context passed to it has a timeout. I add a 15-second timeout to be on the safe side for most connections (YMMV):

    stop := make(chan os.Signal, 1)
    signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
    <-stop

    log.Println("Shutdown signal received, initiating graceful shutdown...")

    ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
    defer cancel()

    if err := srv.Shutdown(ctx); err != nil {
        log.Fatalf("Forced shutdown due to error: %v", err) 
    }
    log.Println("Server exited gracefully")
}

Done!

This completes the first part of this lab. Try out the code: Clone the repository and call go run . in the root of the repository. Then open http://localhost:8080 to verify that the server works as expected.

To test the file embedding, call go build and move the resulting binary to any directory of your liking, then invoke it from there. It'll work like it does inside the repo, as all required files in web/public are inside the binary.

While this server is pretty bare-bones, it runs and serves HTML files! There are still a few things to add to make it ready for production tasks. Up next: a minimal UI.

Dig deeper

Find out more about the packages used here:

  • embed package - embed
  • http package - net/http
  • signal package - os/signal

More articles, videos, talks

Making a game with Pion | Pion

How to (mis-)use WebRTC data channels for multiplayer games.

Announcing Genkit Go 1.0 and Enhanced AI-Assisted Development - Google Developers Blog

Genkit, Google's answer to langchain-go, has reached v1.0 (and hence, API stability). (See also this section of a Go Blog article on building LLM-powered apps.

Architecture of the Ebitengine Game Engine - YouTube

Complements this tutorial.

Develop native iOS apps in Go, on any platform, without the SDK! · quaadgras/graphics.gd · Discussion #184 · GitHub

Be sure to check the details; you'd need a sideloader on your phone, and a free developer account would allow you to install only a couple of apps.

Go channels to solve interface impedance mismatch

How channels helped solve a problem that isn't even a concurrency problem.

How do you check for proper resource closing in code? Is there a universal analyzer?

Why there can't be a linter that catches all resource leaks in Go (or any language).

Building a Simple Virtual Machine | Breakpoint 🛑

What comes out when you're inspired by WebAssembly and the Ethereum VM? This stack-based virtual machine.

The Day the Linter Broke My Code | Fillmore Labs Blog

The author probably ran into this bug. After sharing his article on Reddit, the co-author of the go-err113 linter identified this bug and raised this issue.

rqlite 9.0: Real-time Change Data Capture for Distributed SQLite – Vallified

Philip O'Toole's distributed SQLite system rqlite turns into a live event source through a new Change Data Capture feature that allows streaming database changes to other systems.

Profiling in Go: from guesswork to data. A practical guide | by | Sep, 2025 | Medium

Profiling should always start with a question and a metric. Then work your way from symptom to cause, with the help of this article.

How to implement the Outbox pattern in Go and Postgres

Imagine you have an event and you want to store it in a database and send it to a message queue. Now you have the dual-write problem: The event may be stored successfully while the queue breaks, and now your system is in an inconsistent state. Solution: Let your database do the messaging inside the DB transaction. If the messaging fails, the whole transaction rolls back. This is PostgreSQL's outbox pattern.

Projects

Tools and applications

Three tiny Go web-server experiments: exposing, executing, and extending

Simon Waldherr shares three experimental web server projects: a reverse proxy, a server that loads and runs WebAssembly modules on demand, and a PHP-style "dynamic" server using Go plugins (works on some Unixes only).

GitHub - Dobefu/gofutz: Run tests incrementally and interactively.

Yes, your IDE probably can run tests on saving, but maybe you want to run your tests independently of a specific IDE (or IDE instance)—then try out this project.

GitHub - ehabterra/apispec: APISpec - Generate OpenAPI 3.1 specs from Go code with intelligent framework detection and call graph analysis. Supports Gin, Echo, Chi, Fiber, and net/http.

Another code-first approach to API creation (see this Spotlight for a comparison).

Completely unrelated to Go

A New Kind of Code - by Thorsten Ball - Register Spill

Getting useful answers requires asking the right questions.

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:
https://c.im/@chris… LinkedIn
Powered by Buttondown, the easiest way to start and grow your newsletter.