The Applied Go Weekly Newsletter logo

The Applied Go Weekly Newsletter

Subscribe
Archives
August 28, 2025

Handle With Care • The Applied Go Weekly Newsletter 2025-08-24

AppliedGoNewsletterHeader640.png

Your weekly source of Go news, tips, and projects

2025-08-24 Newsletter Badge.png

Handle With Care

Hi ,

It's been a crazy one and a half weeks for me, and this issue has taken much too long to complete (piece by piece, step by step). But I'm excited to know it's in your inbox... finally!

I hope you enjoy it!

–Christoph

Featured articles

From Python to Go: Why We Rewrote Our Ingest Pipeline at Telemetry Harbor

I like this characterization of Go: "Near-Rust performance levels with much simpler development workflows and faster iteration cycles."

GitHub - cvilsmeier/go-sqlite-bench: Benchmarks for Golang SQLite Drivers

New SQLite driver benchmarks! A helpful Redditor created an overview of driver features (CGO-based, WASM-based, database/sql-compatible).

Embedded Go

Embedded Go, a Go variant for microcontroller targets based on the reference Go compiler, is now available as an installable toolchain—good bye, binary downloads.

Podcast corner

Cup o' Go | The X/Tools Files

Your weekly Go news mixtape! This time, Jonathan and Shay talk about Go 1.25, x/tools compile failures with the new Go version, Anton Zhiyanov's channel operations, Outrig, the Go server monitoring tool, and more.

Fallthrough | What's New in Go 1.25?

Kris and Matt go over the new things in Go 1.25.

Spotlight: Prompt injection, LLM hallucinations: Your code may be vulnerable

Remember little Bobby Tables? Since this comic came out, no one has an excuse for not sanitizing user input anymore. Carefully crafted input into a form field of a database application can cause the database to execute disastrous operations instead of saving or returning data.

LLM-powered applications are even more vulnerable: Attackers don't need to meticulously craft input that would inject a syntactically valid, executable SQL statement with the exact names of the tables and columns to manipulate. Anything that an LLM can misinterpret as a prompt instead of passive data to process can alter its behavior; just ask an LLM to do something and it'll figure out how to do it with the available tools—and do it.

Imagine asking a database in plain English to wipe out all passwords, rather than having to guess or reverse-engineer the table name, column name, and the credentials of a sufficiently privileged DB account.

Unlike a database system, you cannot explicitly forbid certain commands or shield them with access control, because there are no exact commands; all input is plain English (or Spain, German, Chinese—any language the LLM is trained on).

Moreover, you cannot even predict what particular activity a prompt triggers; even with a model's temperature set to 0 to have it always return the same output for a given input, you cannot prevent attackers from slightly modifying a prompt until the model behaves in the way they want it to.

By integrating an LLM in your apps, you leave the realm of deterministic, predictable (let alone provable) algorithms.

But not all is lost! Before digital algorithms entered this world (and cybersecurity wasn't even an idea on the horizon), societies have been able to deal with malicious behavior that doesn't follow algorithmic rules. Police, secret services, private investigators, even politicians and teachers had tools at their disposals to fight crime: Prevention, protection, and investigation.

Your LLM-based app can have these, too. Like "classic" cybersecurity, LLM security rests on three columns: prevention, protection, and investigation.

Prevention

Design your system to be inherently secure, with a minimal attack surface.

Added a prompt file with a quirky request, To write commit messages in limerick style, the best. Ignore the 50-char rule, We’ll keep it cool, And let the JSON output be the rest.

As I laid out in the intro section, the most dangerous way to attack LLMs is prompt injection. Dangerous, because attackers don't have to reverse-engineer exact command syntax, and because commands in plain English/French/Japanese are nearly impossible to predict and block.

I tried prompt injection with the [previous Spotlight's]( project: appliedgocode/git-cmt. I added a new file "prompt.md" containing a prompt to write the commit message as a limerick. git-cmt sends the current changes to an LLM for creating a commit message from the changes. Would the LLM consider the prompt as a prompt or as passive data?

My guess was that particularly older, small models would fall prey to this attack. I tried models from Qwen3 4B to Llama 3.2 1B Instruct. Even the 1B model resisted the attack. But to my surprise, a new and not-so-small model executed the injected prompt: GPT-OSS 20B generated the limerick you've read at the beginning of this section.

prompt_injection_gpt_oss_20b.png

To be clear: I don't mean to finger-point at a particular model; my point is that any model can be vulnerable, not just the "older" or "smaller" ones.

How to prevent prompt injection?

This question has multiple answers, depending on the context, and it might make sense to deploy multiple prevention measures, due to the imprecise nature of input and output of an LLM:

  1. Advise the LLM to treat data as data and never as a prompt. This is the fastest and most direct measure you can take. So take it! But be aware that the easier a model falls victim to prompt injection, the more likely it wouldn't understand the advice.
  2. Verify the response. For example, the git-cmt code could add checks to ensure the commit message doesn't exceed 50 characters (a restriction imposed by the prompt). Or, imagine a prompt for generating a SQL query. The code could check the returned query for anything that's not a query, such as insert, update, delete, alter table, and other statements.

These measures don't look like exact science because they aren't. The probabilistic nature of LLMs makes precise preventive measures nearly impossible.

Time for protective measures.

Protection

Minimize the blast radius if a preventive measure fails.

An LLM can be regarded as a function that accepts any input and can output anything. As you cannot reliably validate neither input nor output, the next best thing you can do is limit the number and impact of actions under the LLM's control. Whether your code acts directly on an LLM's output or lets the LLM access tools via the Model Context Protocol (MCP)—like the moonphase tool from this Spotlight—the approach is the same:

  • Strict access controls: Use the Principle of Least Privilege to give each and every tool only the minimum necessary access to resources.
  • Sandboxing: Let all AI-supported code run inside isolated environments: OCI containers, microVMs, and maybe even unikernels. Let agentic AI work on a copy of the data, then verify and merge the results. A great example for this strategy is Container Use, an MCP server that sandboxes all LLM actions inside a container. Primarily designed for AI-assisted coding, container-use works with git to let the human supervisor review the changes before merging the changes from inside the container into the git repo.
  • Rate limiting and monitoring: LLMs running amuck can cause denial-of-service situations. Good old rate limiting is a countermeasure that comes almost for free, as rate limiting middlewares for net/http are a dime a dozen. Or implement your own. Monitor the systems to catch anomalies early. (Ironically, AIs are quite good at spotting anomalies within the "noise of normality".)

Investigation

Detect, understand, and recover from a security event.

When damage is done, all you can do is pick up the pieces—if you didn't prepare for disaster to happen! You can do quite a few things before The Real Thing happens, in order to quickly recover and build better protection for the future.

  • Robust logging: Log data is an invaluable as a tool for forensics. I assume your apps always have proper logging in place, so all you need to do is to amend your logging to capture relevant LLM activities.
  • Regular backups: Did I just list "regular backups" here? C'mon, folks, this one should be a no-brainer! No backups, no mercy.
  • Incident response for LLM-specific issues: Create an incident response playbook, no matter how small. List the most important tasks to do when an incident is detected, such as revoking keys or shutting down servers. Ideally, these responses can be automated into a single kill switch.

Dig deeper

The above should give you a good idea of where to start securing your LLM-powered app. I didn't go deep because this is just a small, focused Spotlight, and others have researched and written about this way more (and better):

  • The page OWASP Top 10 for Large Language Model Applications of the renowned OWASP Foundation is certainly a solid place to start. Check the "Latest Top 10" but also the "OWASP GenAI Security Project" that the original top-ten list has evolved into.
  • Principles for coding securely with LLMs suggests to consider LLM output as insecure as user input, and develops strategies based on this premise.
  • The Ultimate Guide to LLM Security: Expert Insights & Practical Tips

More articles, videos, talks

How I Use pprof: A Structured 4-Stage Profiling System | by Alexsander Hamir | Aug, 2025 | Medium

pprof is like a kitchen knife: Buying one is one thing, but learning how to cut, chop, slice, and dice is an entirely different matter. Likewise, the optimal use of pprof comes from experience rather than reading the manpage. Alexsander Hamir shares the profiling framework he developed from learning by doing.

Understanding Go Error Types: Pointer vs. Value | Fillmore Labs Blog

Confusing error types with non-error types is usually something the compiler can catch, but with errors.As(), the fault is "compilable" and may be difficult to track down.

GopherCon UK 2025 · Jamie Tanna | Software Engineer

Jamie Tanna took notes while visiting GopherCon.

Injection-proof SQL builders in Go | Oblique

How to create SQL builders that are inherently injection-proof, making insecure code fail to compile?

Easy, with a clever Go type system trick using private string types and constants.

Building blocks for idiomatic Go pipelines

This is clever: Rather than building some thick wrapper around channels that make the original channel semantics almost unrecognizable, Anton Zhiyanov created a set of channel operations that you can use like topping on a salad. Nutritious but not hiding the salad's taste (or the channel's nature).

Gateway pattern for external service calls | Redowan's Reflections

Stow away your external dependencies so that the business logic can't mess with them!

(1) The Go Blog · FreshRSS

Go 1.25 has increased container-awareness. The GOMAXPROCS setting no longer assumes access to physical CPU cores running at full speed and a world that never changes. A Go program running inside a container may receive limited CPU time (that roughly maps to CPU cores available to the container), and the limit may vary over time. In Go 1.25, GOMAXPROCS is adapted to this (but ensure to not set the GOMAXPROCS env var or the value set there takes precedence over the dynamic, CPU-limit-aware behavior).

Projects

Libraries

GitHub - vvvvv/dlg: Printf-Style Debugging with Zero-Cost in Production Builds

Reminds me of my own appliedgo.net/what. Printf-style debugging isn't quite dead yet! 💪

GitHub - saravanasai/goqueue: GoQueue - The flexible Go job queue that grows with you. Start with in-memory for development, scale to Redis for production, or deploy to AWS SQS for the cloud. Built-in retries, DLQ, middleware pipeline, and observability - everything you need for reliable background processing.

Use in-memory queues for development, Redis for production, and AWS SQS when deploying to the cloud, without changing your code.

GitHub - junioryono/godi: Microsoft-style dependency injection for Go with scoped lifetimes and generics

The need for DI frameworks in Go is debatable, and consequently, this DI module sparked some controversy on Reddit. But decide for yourself.

Tools and applications

noCap: A programming language for GenZ

Write code as you speak (if you're GenZ):

cook tipsCalculator(orders) {
  fr totalTips = 0;
  fr hasValidOrders = cap;

  stalk(order in orders) {
    vibe(order is ghosted) {
      pass;
    }

Featurevisor - Feature management for developers

Manage feature flags with Git and style.

GitHub - VincenzoManto/Datacmd: Datacmd is the fastest, coolest way to turn raw data into stunning terminal dashboards. No setup, no fluff — just run a command and boom, your CSV or API becomes a live data experience. ⚡ CLI dashboards have never been this fun.

Turn boring data into fancy charts—in the terminal!

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:
LinkedIn
Powered by Buttondown, the easiest way to start and grow your newsletter.