Skip to content

Archive

Backend

36 articles
Go 16 Sep 2026 6 min read

Go errgroup SetLimit Blocks Submitters at the Concurrency Cap

errgroup.Group.SetLimit can block the goroutine that calls Group.Go. The limit is enforced before a new worker goroutine starts, so a full group applies backpressure at task submission rather than building an internal queue. That behavior matters when submission is part of another control path. A loop that appears to launch work asynchronously can itself stop at g.Go(...) until one active function returns. The limit sits on admission A zero-value errgroup.Group has no concurrency limit. After SetLimit(n), at most n functions started by Go are active at once. A negative limit restores unlimited admission, while a zero limit prevents every later Go call from starting a function.

Go 13 Sep 2026 4 min read

Split Once with strings.Cut in Go

strings.Cut separates a string around the first occurrence of a delimiter and reports whether that delimiter was present. That three-result contract matters in parsers where a missing separator is different from a separator followed by an empty value. before, after, found := strings.Cut(input, "=") When the separator exists, before contains the text preceding its first occurrence and after contains the remainder. When it does not exist, the function returns the original string, an empty second string, and false.

Go 13 Sep 2026 4 min read

Remove Explicit Suffixes with strings.CutSuffix in Go

strings.CutSuffix removes one exact trailing string and reports whether that suffix was present. The boolean result is the key distinction from operations that only return transformed text: callers can keep suffix recognition separate from the remaining content. base, found := strings.CutSuffix(name, ".json") If name ends in .json, base contains the preceding text and found is true. Otherwise, base is the original string and found is false. The operation does not scan for a matching fragment in the middle and does not repeatedly strip the suffix.

Go 12 Sep 2026 4 min read

Track Goroutine Lifetimes with sync.WaitGroup.Go in Go

sync.WaitGroup.Go combines goroutine creation with task accounting. Added in Go 1.25, the method removes a small but consequential gap between incrementing a wait-group counter and starting the goroutine that will eventually decrement it. The method does not change what a WaitGroup represents. It still tracks a set of tasks and lets another goroutine block until that set is complete. The difference is that registration and goroutine launch now share one operation.

Go 12 Sep 2026 5 min read

Run Cancellation Callbacks with context.AfterFunc in Go

context.AfterFunc attaches a callback to context cancellation without adding a goroutine that waits only on ctx.Done(). When the context becomes done, the callback starts in its own goroutine. The small API hides a concurrency boundary that matters when the callback mutates shared state, interrupts blocking I/O, or competes with normal completion. The function arrived in Go 1.21 and returns a stop function. That return value is not a general cancellation handle for the callback. It controls the association between the context and the callback, with precise behavior once cancellation and callback startup begin to race.

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 12 Sep 2026 4 min read

Detach Go Context Cancellation with context.WithoutCancel

A Go context usually ties a unit of work to the lifetime of its parent. Cancel an HTTP request context, and derived contexts observe that cancellation. This propagation is the normal contract, but some follow-up operations need a different lifetime while still carrying request-scoped values. Go 1.21 added context.WithoutCancel for that boundary. It returns a context that can resolve values through its parent but does not inherit the parent’s cancellation state or deadline.

Go 12 Sep 2026 5 min read

Cache Concurrent Initialization Results with sync.OnceValue in Go

sync.OnceValue turns a function into a concurrency-safe, one-time computation whose result is returned on every call. The first caller performs the computation. Concurrent callers wait for that call to finish, and later callers receive the stored result without running the function again. This differs from using sync.Once with a separate result variable. The value and its one-time initialization are packaged behind a function, which makes the lifetime of the cached result explicit in the place where that function is stored.

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 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 06 Sep 2026 9 min read

Handle Long Lines Safely in Go with bufio.Scanner

bufio.Scanner is one of the simplest ways to process line-oriented input in Go. It is a good fit for log files, command output, newline-delimited JSON, and other formats where one logical record fits in memory. The convenience has an important boundary: a Scanner will stop if the next token grows beyond the amount of buffering it is allowed to use. That default protects a program from growing memory without bound, but it can surprise code that works on small test files and later encounters one unusually long line in production.

Go 05 Sep 2026 8 min read

Bound Untrusted Stream Reads in Go with io.LimitReader

Reading an entire io.Reader is convenient, but convenience can become a resource problem when the reader is not fully under your control. A request body, uploaded file, decompressed stream, subprocess output, or protocol payload may be much larger than expected. Calling io.ReadAll directly asks Go to keep reading until EOF, growing memory as needed. The important mental model is that a size limit should sit in front of the consumer. Instead of trusting every caller to stop at the right point, wrap the source in a reader that exposes only a bounded prefix. Go’s io.LimitReader does exactly that.

Database 04 Sep 2026 10 min read

Traverse Hierarchical Data with Recursive SQL CTEs

Hierarchical data appears everywhere: employees report to managers, comments reply to other comments, folders contain folders, and categories form parent-child trees. The table structure is usually simple. The query is the hard part. A normal join follows a fixed number of relationships. A recursive common table expression, or recursive CTE, can follow the same relationship repeatedly until there are no more rows to visit. The key mental model is: start with an anchor set, repeatedly derive the next set from the previous one, then return the accumulated rows.

Go 02 Sep 2026 7 min read

Short Reads and Exact-Length I/O in Go

Reading bytes in Go looks simple: allocate a buffer, call Read, and inspect the error. The subtlety is that io.Reader does not promise to fill the buffer in one call. A valid reader may return fewer bytes than requested even when more data will arrive later. That behavior matters for network protocols, binary file formats, framed messages, and any code that expects an exact number of bytes. Correct stream handling starts by matching the API to the requirement: use ordinary Read when partial progress is acceptable, and use helpers such as io.ReadFull when a fixed-size field must be complete.

Python 02 Sep 2026 9 min read

Practical File Paths in Python with pathlib

Filesystem paths look simple until code has to run from a different working directory, support multiple operating systems, handle symbolic links, or distinguish path manipulation from actual filesystem access. Python’s pathlib module provides path objects that make those distinctions explicit. Instead of repeatedly joining and splitting strings, code can express operations such as “the parent directory,” “this file’s suffix,” or “this path relative to that directory” directly. The most useful habit is not merely replacing os.path calls with methods. It is understanding which pathlib operations are lexical, which consult the filesystem, and when resolving a path changes its meaning.

Database 01 Sep 2026 5 min read

Transaction Isolation and Safe Database Retries

Database transactions make groups of reads and writes atomic, but atomicity alone does not answer what concurrent transactions are allowed to observe. That is the job of isolation. The practical challenge appears when correct transactions conflict. Strong isolation can intentionally abort one transaction rather than allow an invalid interleaving. Applications need to distinguish those retryable concurrency failures from ordinary errors. Isolation protects invariants, not just statements Consider two concurrent requests that reserve the last available item.

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:

Web Development 01 Sep 2026 5 min read

Safe Database Migrations with the Expand-and-Contract Pattern

A schema migration can be syntactically correct and still cause an outage. Production deployments often run old and new application versions at the same time, background workers may lag behind, and a large table can turn a simple-looking DDL statement into a long lock. The expand-and-contract pattern reduces those risks by splitting an incompatible change into compatible stages. Why one-step schema changes are risky Suppose an application wants to rename users.full_name to users.display_name.

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:

Python 01 Sep 2026 4 min read

Python Context Managers for Reliable Resource Cleanup

Python’s with statement is more than convenient file syntax. It is a general protocol for pairing setup with guaranteed cleanup, including when code exits early or raises an exception. Understanding context managers makes resource lifetimes visible and prevents a broad class of leaked files, locks, connections, and temporary state. Why try/finally is the foundation Without a context manager, safe cleanup often looks like this: file = open("input.txt", encoding="utf-8") try: data = file.read() finally: file.close() The finally block runs whether the read succeeds or raises. A with statement packages that pattern: