





















































Join Patrice Roy — ISO C++ Standards Committee member and author of C++ Memory Management — for a 2-day live masterclass on writing safe, efficient, and robust C++ code.
What you’ll learn (hands-on):
✔Smart pointers and RAII for predictable ownership
✔Exception-safe, high-performance techniques
✔Debugging leaks, alignment, and ownership issues
✔Building memory-safe code that performs under pressure
Patrice has taught C++ since 1998, trained professional programmers for over 20 years, and speaks regularly at CppCon. This masterclass distills his experience into practical skills you can apply immediately in production.
Use code DEEPENG30
for 30% off.
Hi
Welcome to the fourteenth issue of Deep Engineering.
Go 1.25 has arrived with container-aware GOMAXPROCS
defaults—automatically sizing parallelism to a container’s CPU limit and adjusting as limits change—so services avoid kernel throttling and the tail-latency spikes that follow. This issue applies the same premise at the code level—structure concurrency to real capacity with request-scoped contexts, explicit deadlines, and bounded worker pools—so behavior under load is predictable and observable.
For today’s issue we spoke with Mihalis Tsoukalos, a UNIX systems engineer and prolific author of Go Systems Programming and Mastering Go (4th ed.). He holds a BSc (University of Patras) and an MSc (UCL), has written for Linux Journal, USENIX ;login:, and C/C++ Users Journal, and brings deep systems, time-series, and database expertise.
We open with a feature on request-scoped concurrency, cancellations, and explicit limits—then move straight into the complete Chapter 8: Go Concurrency from Mastering Go. You can watch the interview and read the complete transcript here, or scroll down for today’s feature.
📢 Important: Deep Engineering is Moving to Substack
In two weeks, we’ll be shifting Deep Engineering fully to Substack. From that point forward, all issues will come from [email protected].
To ensure uninterrupted delivery, please whitelist this address in your mail client. No other action is required.
You’ll continue receiving the newsletter on the same weekly cadence, and on Substack you’ll also gain more granular control over preferences if you wish to adjust them later.
We’ll send a reminder in next week’s issue as the cutover approaches.
Go’s structured concurrency model represents a set of disciplined practices for building robust systems. By tying goroutines to request scopes with context, deadlines, and limits, engineers can prevent leaks and overload, achieving more predictable, observable behavior under production load.
In production Go services, concurrency must be deliberate. Structured concurrency means organizing goroutines with clear lifecycles—so no worker is left running once its purpose is served. This prevents common failure modes like memory leaks, blocked routines, and resource exhaustion from runaway goroutines.
As Mihalis Tsoukalos emphasizes, concurrency in Go “is not just a feature—it’s a design principle. It influences how your software scales, how efficiently it uses resources, and how it behaves under pressure”.
Unstructured use of goroutines (e.g. spawning on every request without coordination) can lead to unpredictable latencies and crashes. In contrast, a structured approach ensures that when a client drops a request or a deadline passes, all related goroutines cancel promptly. The result is a system that degrades gracefully instead of accumulating ghosts and locked resources.
Go’s context.Context
is the cornerstone of request-scoped concurrency. Every inbound request or task should carry a Context
that child goroutines inherit, allowing coordinated cancellation and timeouts. By convention, functions accept a ctx
parameter and propagate it downward.
As Tsoukalos advises, “always be explicit about goroutine ownership and lifecycle” by using contexts for cancellation—this way, goroutines “don’t hang around longer than they should, avoiding memory leaks and unpredictable behavior”.
A common pattern is to spawn multiple sub-tasks and cancel all of them if one fails or the client disconnects. The golang.org/x/sync/errgroup
package provides a convenient way to manage such groups of goroutines with a shared context. Using errgroup.WithContext
, each goroutine returns an error, and the first failure cancels the group’s context, immediately signaling siblings to stop. Even without this package, you can achieve similar structure with sync.WaitGroup
and manual cancellation signals, but errgroup
streamlines error propagation.
The following is a snippet from Mastering Go, 4th Ed. demonstrating context cancellation in action. A goroutine is launched to simulate some work and then cancel the context, while the main logic uses a select
to either handle normal results or react to cancellation:
c1, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
time.Sleep(4 * time.Second)
cancel()
}()
select {
case <-c1.Done():
fmt.Println("Done:", c1.Err())
return
case r := <-time.After(3 * time.Second):
fmt.Println("result:", r)
}
Listing: Using context.WithCancel
to tie a goroutine’s work to a cancelable context.
In this example, if the work doesn’t finish before the context is canceled (or a 3-second timeout elapses), the Done
channel is closed and the function prints the error (e.g. “context canceled”). In real services, you would derive the context from an incoming request (HTTP, RPC, etc.), use context.WithTimeout
or WithDeadline
to bound its lifetime, and pass it into every database call or external API request. All goroutines spawned to handle that request listen for ctx.Done()
and exit when cancellation or deadline occurs. This structured approach prevents goroutine leaks – every launched goroutine is tied to a request context that will be canceled on completion or error. It also centralizes error handling: the context’s error (such as context.DeadlineExceeded
) signals a timeout, which can be logged or reported upstream in a consistent way.
Another key to structured concurrency is bounded work. Go’s goroutines are cheap, but they aren’t free – unchecked concurrency can exhaust memory or overwhelm databases.
Tsoukalos warns that just because goroutines are lightweight, you shouldn’t “spin up thousands of them without thinking. If you’re processing a large number of tasks or I/O operations, use worker pools, semaphores, or bounded channels to keep things under control”.
In practice, this means limiting the number of concurrent goroutines doing work for a given subsystem. By applying backpressure (through limited buffer channels or tokens), you avoid queueing infinite work and crashing under load.
One simple pattern is a worker pool: maintain a fixed pool of goroutines that pull tasks from a channel.
This provides controlled concurrency — “you’re not overloading the system with thousands of goroutines, and you stay within limits like memory, file descriptors, or database connections,” as Tsoukalos notes.
The system’s behavior under load becomes predictable because you’ve put an upper bound on parallel work.
Another powerful primitive is a weighted semaphore. The Go team provides golang.org/x/sync/semaphore
for this purpose. You can create a semaphore with weight equal to the maximum number of workers, then acquire a weight of 1 for each job. If all weights are in use, further acquisitions block – naturally throttling the input. The following code (from the Mastering Go chapter) illustrates a semaphore guarding a section of code that launches goroutines:
Workers := 4
sem := semaphore.NewWeighted(int64(Workers))
results := make([]int, nJobs)
ctx := context.TODO()
for i := range results {
if err := sem.Acquire(ctx, 1); err != nil {
fmt.Println("Cannot acquire semaphore:", err)
break
}
go func(i int) {
defer sem.Release(1)
results[i] = worker(i) // do work and store result
}(i)
}
// Block until all workers have released their permits:
_ = sem.Acquire(ctx, int64(Workers))
Listing: Bounded parallelism with a semaphore limits workers to Workers
at a time.
In this pattern, no more than 4 goroutines will be active at once because any additional Acquire(1)
calls must wait until a permit is released. The final Acquire
of all permits is a clever way to wait for all workers to finish (it blocks until it can acquire Workers
permits, i.e. until all have been released). Bounded channels can achieve a similar effect: for example, a buffered channel of size N can act as a throttle by blocking sends when N tasks are in flight. Pipelines, a series of stages connected by channels, also inherently provide backpressure – if a downstream stage is slow or a channel is full, upstream goroutines will pause on send, preventing unlimited buildup. The goal in all cases is the same: limit concurrency to what your system resources can handle. Recent runtime changes in Go 1.25 even adjust GOMAXPROCS
automatically to the container’s CPU quota, preventing the scheduler from running too many threads on limited CPUgo.dev. By design, structured concurrency forces us to think in terms of these limits, so that a surge of traffic translates to graceful degradation (e.g. queued requests or slower processing) rather than a self-inflicted denial of service.
Structured concurrency not only makes systems more reliable during normal operation, but also improves their observability and shutdown behavior. With context-based cancellation, timeouts and cancellations surface explicitly as errors that can be logged and counted, rather than lurking silently. For instance, if a database call times out, Go returns a context.DeadlineExceeded
error that you can handle – perhaps logging a warning with the operation name and duration.
These error signals let you differentiate between a real failure (bug or unavailable service) and an expected timeout. In metrics, you might track the rate of context cancellations or deadlines exceeded to detect slowness in dependencies. Similarly, because every goroutine is tied to a context, you can instrument how many goroutines are active per request or service. Go’s pprof and runtime metrics make it easy to measure goroutine count; if it keeps rising over time, that’s a red flag for leaks or blocked goroutines. By structuring concurrency, any unexpected goroutine buildup is easier to trace to a particular code path, since goroutines aren’t spawned ad-hoc without accountability.
Shutdown sequences also benefit. In a well-structured Go program, a SIGINT (Ctrl+C) or termination signal can trigger a cancellation of a root context, which cascades to cancel all in-flight work. Each goroutine will observe ctx.Done()
and exit, typically logging a final message. Using deadlines on background work ensures that even stuck operations won’t delay shutdown indefinitely – they’ll timeout and return. The result is a clean teardown: no hanging goroutines or resource leaks after the program exits.
As Tsoukalos puts it, “goroutine supervision is critical. You need to track what your goroutines are doing, make sure they shut down cleanly, and prevent them from sitting idle in the background”.
This discipline means actively monitoring and controlling goroutines’ lifecycle in code and via observability tools.
Production Go teams often implement heartbeat logs or metrics for long-lived goroutines to confirm they are healthy, and use context to ensure any that get stuck can be cancelled. In distributed tracing systems, contexts carry trace IDs and cancellation signals across service boundaries, so a canceled request’s trace clearly shows which operations were aborted. All of this contributes to a system where concurrency is not a source of mystery bugs – instead, cancellations, timeouts, and errors become first-class, visible events that operators can understand and act upon.
context.Context
to every goroutine and function handling a request. Derive timeouts or deadlines to avoid infinite waits.defer cancel()
after context.WithTimeout/Cancel
so resources are freed promptly. Never leave a context dangling.errgroup
or sync.WaitGroup + channels to wait for goroutines and propagate errors. If one task fails, cancel the rest to fail fast.select
with a default
or timeout case in critical loops to prevent global stalls.In Go, by consciously scoping and bounding every goroutine – and embracing cancellation as a normal outcome – engineers can build services that stay robust and transparent under stress. The effort to impose this structure pays off with systems that fail gracefully instead of unpredictably, proving that well-managed concurrency is a prerequisite for reliable production Go.
In this comprehensive chapter, Tsoukalos walks you through the production primitives you’ll actually use: goroutines owned by a Context
, channels when appropriate (and when to prefer mutex/atomics), pipelines and fan-in/out, WaitGroup
discipline, and a semaphore-backed pool that keeps concurrency explicitly bounded.
The key component of the Go concurrency model is the goroutine, which is theminimum executable entityin Go. To create a new goroutine, we must use thego
keyword followed by a function call or an anonymous function—the two methods are equivalent. For a goroutine or a function to terminate the entire Go application, it should callos.Exit()
instead ofreturn
. However, most of the time, we exit a goroutine or a function usingreturn
because...
Ray – Open-Source, High-Performance Distributed Computing Framework
Ray is an open-source distributed execution engine that enables developers to scale applications from a single machine to a cluster with minimal code changes.
Highlights:
@ray.remote
decorator) to turn ordinary functions into distributed tasks, running across cores or nodes with minimal code modifications and hiding the complexity of threads or networking behind the scenes.encoding/json/v2
package.singleflight
package addresses the cache stampede problem by ensuring that only one request for a given key is in-flight at any time, while other concurrent requests wait and reuse the result.That’s all for today. Thank you for reading this issue ofDeep Engineering. We’re just getting started, and your feedback will help shape what comes next. Do take a moment tofill out this short surveywe run monthly—as a thank-you, we’ll addone Packt creditto your account, redeemable for any book of your choice.
We’ll be back next week with more expert-led content.
Stay awesome,
Divya Anne Selvaraj
Editor-in-Chief,Deep Engineering
If your company is interested in reaching an audience of developers, software engineers, and tech decision makers, you may want toadvertise with us.