Most guides I found on Go backend services cover the code inside the service, with little on the production problems around it: configuration, lifecycle, observability, packaging, deployment.
My new guide, Building a Production REST API in Go, covers all of these alongside a working example service: https://tecnick.com/guides/go-rest-api/
#golang #restapi #backend #softwareengineering
Nicola Asuni
Author of popular Open-Source Software.
Startup and shutdown run twice a deploy, so they tend to get debugged by incidents instead of traffic. In nurago, the httpserver package spends its care there: misconfigurations fail in New, the listener binds early so :0 suits tests, requests inherit values but not cancellation, and shutdown stays idempotent whichever of its three triggers fires first.
Ways random ID code goes quietly wrong: alphabet[b % len] skews the distribution, a UUIDv7 built version-4 style throws away half its entropy, a failing custom reader gets silently swapped for another source, and (0, nil) from an io.Reader can spin forever. The random package in nurago walks each trap with its fix and the test that pins it.
Retrying is easy; deciding is not. Between two HTTP attempts sit the real decisions: whether that failure was worth another try, whether the body has been released, whether it can be replayed at all, and who gets a say on the wait. In nurago, the httpretrier package makes each explicit, including why a 404 can be worth retrying on reads and how Retry-After is honoured with a ceiling.
About 2 ns to hex-encode a uint64: that is what knowing the width at compile time buys. Precompute 256 packed uint16 pairs, unroll, and hand the encoder a pointer to a fixed array so the compiler drops the bounds checks. The uhex package in nurago derives it step by step; when the result heap-allocates anyway the advantage mostly evaporates.
Many JWT exploits are format tricks, not cryptanalysis: alg=none, RSA keys reused as HMAC secrets, claims parsed before signatures. In nurago's jwt package each classic is addressed by construction: three HMAC methods, no asymmetric mode to confuse, verify before parse, key floors, size caps, bounded sessions. What no library can do for you is covered too.
Software Quality Is a System, Not an Act.
Quality isn't a final inspection step you bolt on before release. It's the cumulative result of dozens of small, boring, automated decisions that happen on every commit, or not at all.
My latest article makes it concrete: the real quality pipeline of nurago, my open-source collection of production Go packages. Everything is public and runnable with a single command.
I just checked Google Scholar and found that I hit 1,000 citations this year!
I find this particularly amusing because I don't hold a research role. My papers are mostly born out of trying to find solutions for practical software engineering problems. It's great to see that work having an impact!
Ref.: https://scholar.google.com/citations?user=xWXFupoAAAAJ
A new version of #gogen is here!
Started over a decade ago, #gogen has been a solid foundation for numerous production Go services.
It provides boilerplate code for quickly building a production-ready web server and is an #opensource collection of high-quality, 100% unit-tested Go (#golang) packages. Each package follows common conventions and can be imported individually.
Check out and star #gogen: https://github.com/tecnickcom/gogen
Full documentation: https://pkg.go.dev/github.com/tecnickcom/gogen
Debian 13 "trixie" released:
https://www.debian.org/News/2025/20250809
#debian #debiantrixie #debian13
A proxy earns trust by what it refuses. The httpreverseproxy package in nurago: no following upstream redirects (request-forgery bait), no whole-request timeout (streams must flow), no paths escaping the configured base, no 502 for a client that hung up (that is a 499), no log noise for successful round trips.
Bridging slog to zerolog sounds like an afternoon of glue. Then the seams appear: nine syslog severities against a fixed level set, open groups against pre-baked attributes, pooled events that must go home, marshalers that panic mid-buffer. In nurago, the logsrv package resolves each one, and on every benchmarked path the handler adds no allocations per record beyond what slog itself costs.
MySQL's GET_LOCK hands you a distributed lock; the session can take it back without telling you. Pools swap connections, idle reapers kill sessions, a wedged connection just looks open. In nurago, the mysqllock package pins one connection, pings it on a bounded clock, and reports presumed loss. Also covered: cases where no lock is the better tool.
1-800-FLOWERS still routes: letters fold to keypad digits mid-walk in nurago's numtrie, a longest-prefix trie for numeric keys with ten child slots per node, descent by array index, and a packed int8 status that says in one traversal whether the input matched, overshot, or stopped where more specific rules exist.
Every "filter=" parameter is a tiny programming language you hand to strangers. So start from the hostile programme: megabyte payloads, rule floods, regex bombs, integers past 2^53 rounding through float64, malformed shapes. In nurago, the filter package caps or rejects each class by default. And it filters only; authorisation stays yours.
DNS trivia with consequences: strings.ToLower can change which host you resolve. Turkish İSTANBUL, fullwidth letters, and the Kelvin sign all fold to different DNS names, so nurago's dnscache folds ASCII only, per RFC 4343. Then the dial path: dedupe by parsed address and interleave IPv6 with IPv4, so one dead family costs one bounded attempt.
One GET request carrying five hex characters of a locally computed SHA-1. That is all a Have I Been Pwned check needs to send: one bucket out of about a million, matched locally, with padding masking response sizes. The other half of nurago's passwordpwned client is refusing to read a captive portal's HTML page as 'this password is safe'.
Wrapping singleflight in a cache reveals five gaps, often one incident at a time: nothing remembered, nothing expired, nothing bounded, no context, no failure policy. The sfcache package in nurago grew out of closing all five, including not handing one caller's cancellation to forty-nine innocent waiters.
Password storage comes with a long checklist: a memory-hard algorithm, sensible costs, fresh salts, a format that survives parameter changes, a migration path. In nurago, the passwordhash package wraps the checklist into three method pairs (hash, verify, rehash), with RFC 9106 defaults from a bare New() and self-describing hashes, so raising the cost later is an if-statement at login rather than a migration project.
A party trick with a practical side: an entire ISO 3166 country record packed into one uint64. Country codes draw on 26 letters, so 5 bits per letter, and the whole record decodes with masks and shifts. The countrycode package in nurago derives all its reverse indexes from those integers. The article unpacks a real stored key, bit range by bit range.
Secrets slip into logs sideways. A dumped request carries the Authorization header; an error payload brings a password field along. The redact package in nurago scrubs text at the last moment before it is written, in one pass, cheap enough to run on every line. Token-exact matching masks api_key but leaves monkey alone, and re-redacting output is designed not to reveal more.
Some bugs pick their moment. A flaw that needs dozens of consecutive failures to surface stays hidden through ordinary operation and appears mid-outage, when the retry loop is the only code still running. Delay arithmetic is that kind of code, so the backoff package in nurago keeps it boring: a small, pure calculator for jittered, bounded delays, written and tested once, reused by every retry path in the library.
