Go API Boilerplate
ArchivedMarch 1, 2022
A fork of Vardius' Go API boilerplate — the start of a brief but intense phase of wanting to do everything in Go. Renowned as the optimal server language, Go was the backend for Tenlach, a real-time Mega Man Battle Network-inspired game that never shipped. A look at why Go earned its reputation, what Mega Man Battle Network got right, and the value of languages you explore but never deploy.
Purpose
Forked this Go boilerplate during a phase where Go seemed like the answer to every backend problem. It was fast, compiled to a single binary, had goroutines for concurrency, and Google built it — what more could you want? For a while, it was the backend powering Tenlach, a real-time tactical combat game inspired by Mega Man Battle Network. I experimented extensively but never deployed anything in Go to production.
Stack
What I Learned
- Go earned its server-language reputation for specific reasons: it compiles to a single static binary (no runtime dependencies, no "works on my machine"), goroutines make concurrency trivially easy (spawn thousands of lightweight threads without managing thread pools), the standard library includes a production-grade HTTP server (no framework required for most APIs), and garbage collection is tuned for low-latency server workloads. For microservices, APIs, and any workload where many things happen simultaneously, Go is genuinely excellent.
- Goroutines are Go's killer feature. In most languages, handling 10,000 concurrent connections means thread pools, async/await, or event loops. In Go, you write "go handleConnection(conn)" and the runtime manages the rest. Each goroutine uses about 2KB of stack space (vs. 1-8MB per OS thread). Channels provide safe communication between goroutines without shared memory locks. The concurrency model is elegant enough that it changes how you think about server architecture.
- Go's opinionated design is polarizing: no generics (until Go 1.18), no exceptions (errors are returned values), no classes (structs with methods), enforced formatting (gofmt), and unused imports are compile errors. These decisions feel restrictive coming from JavaScript or Dart. Over time, they reveal themselves as productivity features — the codebase stays consistent, error handling is explicit, and you never argue about style.
- Tenlach was going to be a real-time tactical combat game inspired by Mega Man Battle Network — the GBA game where you fight on a 3x3 grid in real time, selecting chip attacks from a deck. Battle Network was brilliant because it combined the strategic depth of a card game (deck building, chip combos, elemental weaknesses) with the twitch skill of an action game (dodging attacks on a grid in real time). Go was the backend because real-time multiplayer combat needs low-latency state synchronization, and Go's concurrency model seemed purpose-built for it.
- Tenlach used Nakama as the game server framework — an open-source, Go-based server designed specifically for multiplayer games. Nakama provides matchmaking, real-time multiplayer (WebSocket and UDP), leaderboards, chat, user accounts, storage, and server-authoritative game logic out of the box. It is essentially Firebase for games. Writing game logic as Nakama server runtime functions in Go felt natural, and the matchmaking and lobby systems meant I did not have to build that infrastructure from scratch.
- Even with Nakama handling the infrastructure layer, the game never shipped. The gap was not the server framework — Nakama solved matchmaking, lobbies, and real-time networking. The gap was in the game design completion and the client-side polish needed to make a real-time combat game feel good. The Go/Nakama backend worked. The game on top of it was not ready.
Key Insights
- Languages you explore but never deploy to production still change how you think. Go's error handling (return the error, handle it immediately, no try/catch) permanently altered how I think about errors in every language since. Go's goroutines made me understand concurrency as a first-class concept, not an afterthought. You do not need to ship Go to benefit from understanding Go.
- Mega Man Battle Network is one of the most underappreciated game designs in history. The 3x6 grid (3 rows, 6 columns, split between you and the opponent) creates spatial strategy in a tiny space. The chip system (select up to 5 chips from your deck, but only matching codes or types can be selected together) creates deck-building strategy under time pressure. The real-time combat on the grid creates action-game skill. No other game has combined these three layers as cleanly. It is the reason Tenlach existed as a concept, and the reason similar games (One Step From Eden, Lancer Tactics) keep being made.
- The pattern of reaching for a new language when you want to level up is natural but sometimes counterproductive. Go was not the bottleneck — infrastructure knowledge was. Learning Go to solve a deployment problem is like buying a faster car to fix a flat tire. The right move would have been to deploy a Node.js or Dart server (languages I already knew) and learn infrastructure on familiar terrain. New language + new domain = two unknowns. One unknown at a time is the faster path.
- Tenlach as a concept connects directly to Hot Potato Games and the Sod Tori tactical grid game currently in development. The tactical-grid-combat fascination has persisted across multiple years and multiple tech stacks. Like Intervalition (three attempts before shipping), Tenlach may find its form eventually — just in Flutter/Flame instead of Go.
This post was composed through a conversation between Brett Owers and Claude Code (Anthropic). The content reflects Brett's recollection of each project and the lessons drawn from it. Some details may be approximate or omitted — the purpose is to paint an honest picture of a software engineer's development over time, not to serve as a precise historical record.