Skip to content

Archive

Golang

56 articles
Go 12 Sep 2026 5 min read

Preserve Cancellation Causes in Go Contexts

A canceled Go context normally reports one of two broad states through ctx.Err(): context.Canceled or context.DeadlineExceeded. That is enough to stop work, but it can discard the event that triggered cancellation. A worker failure, shutdown request, quota rejection, and explicit abort can all collapse into the same context.Canceled value. Go provides cause-aware context functions for cases where the cancellation signal and the diagnostic error need to travel together. context.WithCancelCause creates a derived context whose cancel function accepts an error, while context.Cause retrieves the recorded cause.

Go 10 Sep 2026 7 min read

Set Per-Response Write Deadlines in Go with http.ResponseController

A server-wide WriteTimeout is a useful safety net, but some handlers have different timing needs. A small JSON response and a streaming export shouldn’t necessarily share the same write budget. When the deadline belongs to one response rather than the whole server, Go’s http.ResponseController gives the handler a direct way to set it. http.ResponseController.SetWriteDeadline applies a write deadline to the current response. It also solves a practical middleware problem: instead of asserting a concrete optional interface at every call site, a handler can ask the controller to find the capability through compatible ResponseWriter wrappers.

Go 09 Sep 2026 8 min read

Control Structured Log Output in Go with slog.LogValuer

Passing a struct directly to slog is convenient until that struct grows a field that should never appear in logs. An access token, session secret, internal note, or large payload can turn an ordinary diagnostic line into a security problem or an expensive blob of noise. Go’s slog.LogValuer interface gives a type control over its own structured log representation. Instead of teaching every call site which fields are safe, you can define that representation next to the type and let slog use it wherever the value is logged.

Go 08 Sep 2026 7 min read

Contain Untrusted File Access in Go with os.Root

Applications often combine a trusted directory with a file name that came from somewhere less trusted: an HTTP request, archive entry, manifest, job message, or database row. The obvious implementation is also a common security boundary mistake: path := filepath.Join("./uploads", userName) f, err := os.Open(path) If userName can select a path outside ./uploads, the application may expose files it never intended to touch. Even careful string validation becomes harder when symbolic links and concurrent filesystem changes enter the picture.

Go 07 Sep 2026 8 min read

Stream Data Between Go Components with io.Pipe

Many Go APIs meet at io.Reader and io.Writer. That makes components easy to compose until both sides want to drive the operation. A compressor may want an io.Writer where it can emit bytes incrementally, while an uploader wants an io.Reader from which it can pull those bytes. One tempting solution is to write everything into a bytes.Buffer first and upload it afterward. That works, but it turns a streaming pipeline into a whole-payload allocation.

Go 07 Sep 2026 7 min read

Reject Unknown JSON Fields in Go API Requests

Go makes it pleasantly simple to decode JSON into a struct. That convenience also hides a compatibility decision that matters at API boundaries: by default, fields that do not map to the destination struct are ignored. For internal data this can be useful. For an HTTP request, it can turn a typo into a silent behavior change. A client may send "expires_inn": 3600 while the server expects "expires_in". The JSON is valid, decoding can succeed, and the server may continue with a zero value or a default. The caller receives no direct signal that the field it thought it supplied was never used.

Go 07 Sep 2026 8 min read

Handle Buffered Write Errors Correctly in Go

Buffering output can reduce write overhead, but it also changes when an I/O failure becomes visible. With an unbuffered writer, a call to Write normally reaches the underlying destination immediately. With bufio.Writer, a successful call may only mean that the bytes were accepted into memory. The actual write to a file, socket, pipe, or other destination can happen later, often during Flush. That distinction creates a common production bug: code checks every apparent write, defers Flush, and still returns success after the final flush fails.

Go 04 Sep 2026 9 min read

Understand Escape Analysis and Heap Allocations in Go

A Go program can create many values without you choosing whether each value lives on a goroutine stack or in heap memory. The compiler usually makes that decision for you. This becomes important when a hot path allocates more than expected. A small helper function may look harmless, yet a value it creates can outlive the function call and require heap storage. More heap allocation can mean more work for the garbage collector, but changing code blindly to avoid the heap can make a program harder to understand without producing a measurable benefit.

Go 04 Sep 2026 10 min read

Coordinate Shared State with sync.Cond in Go

A goroutine sometimes cannot make progress until shared state changes. A worker may need to wait until a queue contains an item. A producer may need to wait until that queue has free capacity. Several goroutines may need to sleep until a service becomes ready. Polling the state in a loop wastes CPU or forces you to invent arbitrary sleep intervals. Channels solve many coordination problems more directly, but they are not always a natural fit when several goroutines already share state protected by a mutex and need to wait for predicates over that state.

Go 03 Sep 2026 11 min read

Use sync.Pool for Temporary Object Reuse in Go

Repeatedly allocating short-lived helper objects can become expensive in a hot path. A formatter may create temporary buffers for every request, an encoder may allocate scratch space for every record, or a parser may repeatedly construct helper objects that are discarded immediately after use. Go’s sync.Pool can reuse some of those temporary objects across independent operations. That can reduce allocation work and garbage-collector pressure when the same kind of object is created frequently under load.

Go 03 Sep 2026 10 min read

Use defer for Reliable Cleanup in Go

Resource cleanup is easy to get right on the happy path and easy to miss on an early return. A function opens a file, acquires a lock, or starts a trace span; a later check fails; the function returns before reaching the cleanup statement. Go’s defer statement addresses this by scheduling a function call to run when the surrounding function returns. Used well, it places cleanup next to acquisition and makes every return path easier to reason about.

Go 03 Sep 2026 12 min read

Understand Nil Interface Values in Go

A Go program can print an error that looks empty, enter an if err != nil branch, and still be holding a nil pointer underneath. This behavior surprises developers because it seems to violate the simple rule that “nil means no value.” The rule is still consistent. The missing piece is that an interface value has two parts: a dynamic type and a dynamic value. An interface is nil only when neither part is set.

Go 02 Sep 2026 4 min read

Lazy Initialization in Go with sync.OnceValue and sync.OnceValues

Lazy initialization is useful when a value is expensive to build and may never be needed. The difficulty is making that initialization safe when several goroutines request the value at the same time. Go has long provided sync.Once. Since Go 1.21, the sync package also includes OnceValue and OnceValues, helpers that return functions which compute results once and reuse them for later calls. The manual sync.Once pattern A classic implementation looks like this:

Go 01 Sep 2026 7 min read

Token Bucket Rate Limiting in Go

Rate limiting protects a service from traffic spikes, accidental client loops, and workloads that consume more resources than the system can safely handle. A useful limiter should do more than enforce a fixed request count: it should allow small bursts while keeping the long-term request rate bounded. The token bucket algorithm provides exactly that behavior. This guide implements a small, concurrency-safe token bucket using only Go’s standard library and then shows how to use it in an HTTP service.

Go 01 Sep 2026 8 min read

Testing Go HTTP Handlers with httptest

HTTP handlers are one of the easiest parts of a Go service to test well. You usually do not need to bind a real network port, start the entire application, or depend on an external test framework. Go’s standard library provides net/http/httptest, which can construct HTTP requests, capture handler responses, and even start temporary HTTP servers when a real client-server round trip matters. This guide builds a small JSON endpoint and tests it at several useful levels.

Go 01 Sep 2026 7 min read

Structured Logging in Go with slog

Plain text logs are easy to print but difficult to query reliably. Once an application runs across multiple processes or containers, operators usually need to filter events by fields such as HTTP status, request path, customer ID, or latency rather than search arbitrary strings. Go’s standard library includes log/slog for structured logging. It was added in Go 1.21, so the examples in this article require Go 1.21 or newer. What structured logging changes A traditional log message often embeds data inside prose:

Go 01 Sep 2026 5 min read

Streaming Pipelines with io.Reader and io.Writer in Go

Go’s io.Reader and io.Writer interfaces are intentionally tiny, but they enable a large class of streaming programs. Files, HTTP bodies, compression streams, hashes, encoders, sockets, and in-memory buffers can all participate in the same pipeline without loading the entire payload into memory. The key design principle is to pass streams through components instead of converting them to []byte or string at every boundary. Start with the two core interfaces The standard library defines the essential contracts as methods equivalent to:

Go 01 Sep 2026 7 min read

Request Coalescing in Go Without Extra Dependencies

When many requests ask for the same expensive resource at the same time, running identical work for every caller can overload a database, API, or filesystem. A cache can help after a result exists, but it does not necessarily prevent several concurrent cache misses from triggering the same backend operation. Request coalescing solves a different problem: while one operation for a key is already running, later callers wait for that operation and share its result. After the operation finishes, the result is forgotten. The next request starts fresh work.

Go 01 Sep 2026 5 min read

Reliable Application Configuration from Environment Variables in Go

Environment variables are a convenient way to configure deployed Go services, but calling os.Getenv throughout an application makes configuration difficult to validate and test. A stronger pattern is to load configuration once at startup, parse it into typed fields, validate all invariants, and pass the resulting value to the components that need it. The examples below use only the Go standard library and work with modern supported Go releases. Keep configuration in a typed struct Suppose a service needs a listen address, request timeout, and optional log level:

Go 01 Sep 2026 7 min read

Graceful HTTP Server Shutdown in Go

Stopping a web server with Ctrl+C looks harmless during development, but production deployments need a more careful shutdown process. If a process exits immediately, active HTTP requests can be interrupted, clients may receive connection errors, and in-flight work can be left unfinished. Go’s standard library already provides the pieces needed for a clean shutdown. The main tools are os/signal, context, and http.Server.Shutdown. This guide shows a practical pattern for shutting down an HTTP server when the process receives SIGINT or SIGTERM.

Go 01 Sep 2026 4 min read

Go Error Wrapping with errors.Is and errors.As

Errors often cross several layers of a Go application. A low-level function may know that a file is missing, while a higher-level function needs to add context about which operation failed. Go error wrapping lets you add that context without losing information callers need for reliable handling. Why error strings are fragile Do not make program logic depend on error wording. Adding a filename or changing punctuation can break string comparisons even when the underlying condition is unchanged. Prefer semantic checks:

Go 01 Sep 2026 8 min read

Go Context Timeouts and Request Cancellation

A Go HTTP handler can outlive the request that started it unless the work inside the handler pays attention to cancellation. That matters when a client disconnects, an upstream request takes too long, or a database query is no longer useful. Go solves this with context.Context. Every incoming *http.Request already has a context, and that context is canceled when the client connection closes, the request is canceled by HTTP/2, or the handler returns. You can also derive a shorter deadline for work that should not consume the entire request lifetime.

Go 01 Sep 2026 6 min read

Exponential Backoff with Jitter in Go

Retries can make distributed systems more resilient, but immediate retries can also make an outage worse. If thousands of clients retry at the same moment, a recovering dependency receives another synchronized burst of traffic before it has time to stabilize. A common solution is exponential backoff with jitter: increase the maximum delay after each failure, then randomize the actual wait. This article builds that pattern with Go’s standard library and shows where retry logic belongs—and where it does not.

Go 01 Sep 2026 4 min read

Coalesce Duplicate Work with Single-Flight Patterns in Go

Concurrent services often receive bursts of requests for the same expensive value: configuration, a database row, a rendered artifact, or a remote API response. A cache helps after the first request completes, but it does not stop ten simultaneous cache misses from doing the same work ten times. A single-flight pattern lets one caller perform the work while other callers wait for that result. The cache-miss stampede problem Without coordination, several callers can observe the same miss and all call the dependency. Single-flight changes that behavior so one request becomes the leader and later requests for the same key become followers.