Skip to content

Archive

Golang

82 articles
Go 19 Sep 2026 7 min read

Email OTP in Go: Single-Use Codes, Expiration, and Replay Protection

Email OTP in Go: Single-Use Codes, Expiration, and Replay Protection An email OTP looks simple: generate six digits, send them, and compare what the user types. The security boundary is not the email API, though. It is the server-side challenge lifecycle. A correct implementation has to make the code unpredictable, expire it quickly, limit guesses, invalidate older challenges when appropriate, and guarantee that a successful code cannot be consumed twice. Those properties are different from TOTP, even though both mechanisms are commonly described as OTP.

Go 16 Sep 2026 5 min read

Go WaitGroup Reuse Requires Completed Wait Boundaries

A sync.WaitGroup can be reused after a wait phase completes, but a new independent task set cannot begin while calls to Wait from the prior phase are still active. The boundary is the return of every earlier Wait, not merely the instant at which the internal task counter reaches zero. This constraint matters when one WaitGroup instance is retained across batches, epochs, request waves, or repeated coordination cycles. Reuse is supported, but phases must not overlap at the zero-to-positive counter transition.

Go 16 Sep 2026 5 min read

Go sync.Pool Items Can Disappear Across Garbage Collection

A value placed in a Go sync.Pool is not guaranteed to remain there until a later Get. The runtime may remove pooled items automatically, so the pool acts as a reuse opportunity rather than durable storage. That property shapes both the performance profile and the correctness boundary of sync.Pool. Code can benefit when an object survives long enough to be reused, but it must remain correct when every Get behaves as if no prior item were available.

Go 16 Sep 2026 4 min read

Go sync.Cond Wait Rechecks Shared State

sync.Cond.Wait resumes after a notification, but the notification does not assert that a caller-specific condition is still true when the goroutine reacquires the lock. The shared predicate remains the source of truth, so a waiter checks it again after every return from Wait. This boundary separates notification from state. Signal and Broadcast announce that relevant state may have changed; they do not transfer ownership of that state or reserve it for a particular waiter.

Go 16 Sep 2026 5 min read

Go singleflight Shares Only In-Flight Results

singleflight.Group suppresses duplicate function executions only while an operation for the same key is in flight. Concurrent callers can receive one shared result, but a caller arriving after completion starts a new execution. The group is therefore a request-coalescing mechanism, not a result cache. This boundary affects cache fills, metadata refreshes, backend reads, and other keyed operations that can attract bursts of identical concurrent work. A group can reduce simultaneous pressure on the backing operation without extending the lifetime of its returned value.

Go 16 Sep 2026 4 min read

Go Runtime Finalizers Delay Object Reclamation

A Go object with a finalizer is not reclaimed when the garbage collector first determines that it is unreachable. The runtime must retain the object for the finalizer call, and reclamation can occur only after a later collection finds the object unreachable again. That behavior makes runtime.SetFinalizer materially different from ordinary garbage collection. It adds an asynchronous lifecycle phase between loss of application reachability and memory reclamation. Finalization temporarily restores reachability runtime.SetFinalizer(obj, f) associates f with obj. When the collector detects an unreachable object with that association, it clears the association and arranges a call to f(obj).

Go 16 Sep 2026 5 min read

Go Nil Channels Disable Select Cases

A send or receive on a nil Go channel can never proceed. Inside a select, that property removes the associated communication case from the set of cases eligible to run, without requiring a separate condition around the select. This behavior follows directly from the channel contract. The zero value of a channel is nil, and a nil channel is never ready for communication. A standalone send or receive therefore blocks indefinitely. A select treats the same operation as a case that cannot currently proceed.

Go 16 Sep 2026 4 min read

Go Map Iteration Order Is Not Stable

A range over a Go map can visit the same entries in a different order on consecutive iterations. The language specification leaves map iteration order unspecified and gives no guarantee that a later pass over an unchanged map will repeat an earlier sequence. That contract is stronger than saying that maps are merely unsorted. An unsorted container could still expose a stable insertion-dependent or storage-dependent sequence. A Go program cannot assign such meaning to map traversal.

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

Go Defer Saves Call Arguments Before Return

A Go defer statement evaluates its function value and call parameters when execution reaches the statement, even though the deferred function runs only as the surrounding function returns. Mutations between those two moments do not retroactively change already saved argument values. This split between evaluation and invocation is part of the language semantics rather than an optimization detail. Each executed defer records a call with values established at that point. Return processing later invokes recorded calls in reverse registration order.

Go 16 Sep 2026 3 min read

Go context.AfterFunc Stop Does Not Wait for Callback Completion

The stop function returned by Go’s context.AfterFunc does not wait for a callback that has already started. A false result therefore marks a state boundary, not a completion barrier: the callback may be running concurrently when stop returns. context.AfterFunc(ctx, f) associates f with cancellation of ctx. Cancellation starts f in its own goroutine. If the context is already canceled at registration time, the callback is started promptly in a new goroutine rather than being invoked synchronously by the caller.

Go 16 Sep 2026 4 min read

Go Context Cancellation Cause Follows the First Cancellation

A Go context records its cancellation cause when cancellation first reaches that context. Later cancellation attempts do not replace the recorded cause. This makes context.Cause a record of the winning cancellation event rather than a mutable error slot. The distinction matters in context trees because parent and child cancellation can race. The first event to cancel a given node fixes that node’s cause, while another node in the same tree can retain a different cause.

Go 13 Sep 2026 4 min read

Transform Unicode Text with strings.Map in Go

strings.Map applies one function to every rune in a UTF-8 string and builds a string from the returned runes. A mapping function can preserve a rune, replace it, or remove it entirely. That makes the API a compact fit for transformations whose rule is naturally expressed one Unicode code point at a time. mapped := strings.Map(func(r rune) rune { if r == '_' { return '-' } return r }, input) The operation is rune-oriented rather than byte-oriented. ASCII input still follows the same contract, but multibyte UTF-8 sequences arrive at the callback as decoded rune values.

Go 13 Sep 2026 4 min read

Split Text with Custom Rune Boundaries Using strings.FieldsFunc in Go

strings.FieldsFunc treats selected Unicode code points as boundaries and returns the non-empty text between them. That behavior fits inputs where separators belong to a class rather than one fixed substring: commas and semicolons, several punctuation marks, or any rune accepted by a deterministic predicate. fields := strings.FieldsFunc(input, func(r rune) bool { return r == ',' || r == ';' }) For alpha,,beta;gamma;, the result is []string{"alpha", "beta", "gamma"}. Consecutive matching runes form a boundary region, and matching runes at either edge do not produce empty elements.

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 5 min read

Set Per-Request Read Deadlines with http.ResponseController in Go

A server-level read timeout applies one policy across connections, but a particular handler can have a narrower request-body budget. Go’s http.ResponseController exposes SetReadDeadline for that case. The deadline covers reading the request, including its body, and gives handler code a direct boundary for input that arrives too slowly. This control is different from limiting body size. A byte limit constrains how much data a handler accepts; a read deadline constrains how long reads may continue. Endpoints that accept streamed or uploaded data often need both dimensions considered separately.

Go 13 Sep 2026 5 min read

Replace Non-Overlapping Substrings with strings.ReplaceAll in Go

strings.ReplaceAll replaces every non-overlapping occurrence of one literal string with another. There is no regular-expression syntax, callback, or token model involved: matching is based on the exact byte sequence supplied as old. result := strings.ReplaceAll("api/v1/users", "/v1/", "/v2/") The result is api/v2/users. This small contract makes the function suitable for fixed substitutions where every match receives the same replacement.

Go 13 Sep 2026 3 min read

Remove Prefixes with strings.CutPrefix in Go

Prefix removal often carries two pieces of information: the remaining text and whether the expected prefix was present. strings.CutPrefix represents both results in one operation instead of separating a prefix test from the removal that follows it. That distinction matters when an unchanged string is a valid result. strings.TrimPrefix returns the input unchanged when the prefix is absent, so its return value alone cannot report presence. strings.CutPrefix returns the remainder plus a boolean that preserves that fact explicitly.

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

Bound HTTP Request Bodies with http.MaxBytesReader in Go

An HTTP handler that decodes a request body without a byte limit can consume far more input than its application-level schema suggests. A JSON object with three fields may still arrive inside a multi-gigabyte body. Decoder validation controls structure; it does not establish a transport-sized boundary. Go’s http.MaxBytesReader places that boundary directly around the request body. It returns an io.ReadCloser that permits reads up to a configured limit and reports an error when code attempts to read beyond it.

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 5 min read

Interleave HTTP Request Reads and Response Writes in Go

An HTTP handler that writes its response before finishing the request body has a protocol-sensitive edge case. With HTTP/1, Go’s server normally consumes the unread request body before it begins writing the response. That default keeps ordinary handlers simple, but it conflicts with handlers that intentionally exchange data in both directions at the same time. http.ResponseController.EnableFullDuplex changes that behavior for the current request. It tells the server that the handler intends to interleave reads from Request.Body with writes to the ResponseWriter.