Get in Touch
Hire Talent
Tell us the role
Hire Golang Developers

Go Developers Who Know Where the Goroutines Went

A shortlist in 72 hours, contributing inside two weeks, and a 30-day guarantee if the fit is wrong.

72-hour shortlist · 30-day replacement guarantee · Month-to-month

Go Is Easy to Read. That Is Not the Same as Easy to Write Well.

Go was designed so somebody new to a codebase could read unfamiliar code and follow it within an hour. It succeeds. That success is also the hiring trap.

Because the syntax is small and the standard library covers most of what a service needs, a competent developer from any language is productive in Go inside a fortnight. Their code compiles, passes review from anyone at the same level, and behaves correctly under light load. The problems concentrate in concurrency and error handling — the two areas where Go hands you sharp tools and almost no guard rails.

A goroutine costs almost nothing to start and nothing at all to forget about. Nothing in the toolchain warns you that one is still parked on a channel nobody will ever write to. The compiler is satisfied, the tests pass, and the process grows.

Interviewing on Go syntax therefore separates nobody, because everybody passes. What separates candidates is whether they can explain, precisely, how a goroutine exits.

Screening

What We Screen For

Everybody passes a Go syntax round. These are the questions that actually separate people.

Goroutine lifecycle

Who starts it, what stops it, and what happens if the reader disappears. The single most useful Go screening question there is.

Context discipline

Whether context.Context is threaded through the call chain and genuinely honoured, or accepted as a parameter and then ignored.

Error wrapping

Use of %w, errors.Is and errors.As, and whether the errors they produce tell you which call actually failed.

Race detection

Whether go test -race is part of how they work or something they have only heard of. We ask what it last caught for them.

Knowing when not to use concurrency

Reaching for a mutex, or for nothing at all, when that is the simpler answer. Restraint is the senior signal in this language.

Communication

A live conversation in English, every time. Concurrency bugs get fixed faster when somebody can describe the failure precisely.

What This Costs You When It Goes Wrong

The signature Go failure is a service that is completely healthy right up until it is not.

A handler starts a goroutine per request to do some background work and sends the result on an unbuffered channel. On the normal path a receiver is waiting and everything is fine. Then a client disconnects, or a timeout fires, and the receiver goes away. The goroutine blocks on that send forever. It holds its stack, everything it closed over, and the request context. One per failed request. Memory climbs at a rate proportional to your error rate, which is precisely why it never appears in load testing, where nothing fails.

The naive version is caught instantly — send on an unbuffered channel with no receiver anywhere and the runtime prints fatal error: all goroutines are asleep - deadlock!. But it only detects that when every goroutine is blocked. In a real server there is always an active listener, so the detector never fires and the leak stays silent until the pod is OOM-killed.

Error handling degrades the same way, more slowly. if err != nil { return err } repeated up eight frames produces a production log line reading connection reset by peer with no indication of which call, which host, or which request. Wrapping with fmt.Errorf("fetching user %s: %w", id, err) costs one line and is the difference between a five-minute diagnosis and a lost afternoon. Teams that skipped it early almost never go back and add it.

Then there is the nil interface trap: a *MyError that happens to be nil, assigned into an error interface, produces a value that is not nil. The check passes, the caller takes the failure path, and the message is empty. It still reaches production regularly.

Seniority, Defined

Mid-level (3–5 years). Writes services and handlers against an established structure. Comfortable with the standard library, HTTP, and a database driver. Uses goroutines correctly in the patterns already present in the codebase, and is less sure outside them.

Senior (5–8 years). Designs the concurrency. Decides what runs in a goroutine, how it gets cancelled, and what the service does under back pressure. Sets the error-wrapping convention and enforces it in review. Reaches for go test -race without being asked to.

Lead (8+ years). Owns service boundaries and the shared internal libraries, makes the build-versus-import calls that determine your dependency surface, and has the standing to say a particular problem does not need concurrency at all.

Common Requests

Backend services and APIs. The bulk of Go hiring. gRPC or HTTP, usually behind a gateway, usually with Postgres underneath.

Migrating a service off Python or Node for throughput. Worth being honest about: the win is often real and often smaller than forecast, because the database turns out to be the constraint.

Platform and internal tooling. CLIs, Kubernetes operators, controllers. This overlaps heavily with DevOps engineers, and the two are routinely confused in job descriptions.

Concurrency rescue. A service leaking memory or goroutines that nobody internally can pin down. Bounded work, usually one to two months rather than an ongoing seat.

Go alongside an existing Java or .NET estate. Common in teams breaking up a monolith. See Java and .NET.

What a Good First Month Looks Like

Week one. They read the concurrency before they read the business logic. Expect questions about which goroutines are long-lived, what cancels them, and whether context.Context is threaded through the call chain or dropped at the first layer.

Weeks two to four. They own a service or a substantial feature. Somewhere in that window, expect them to run the race detector across the test suite and report what came back. On a codebase that has never had it enabled, it finds something. Expect a view on error wrapping too — it is cheap to establish early and unpleasant to retrofit.

The warning sign is enthusiasm for channels. Go's own guidance is to use a mutex when a mutex is what you mean. Reaching for a channel to protect a counter, or building a four-stage pipeline where a loop would do, produces code that is harder to reason about for no gain. Senior Go tends to look boring.

Mistakes We See

Interviewing on syntax. Go's surface area is small enough that a strong developer from any language passes a syntax round after a week of preparation. Ask instead how a goroutine gets cancelled, or what happens to a send on a channel whose reader has gone. That cannot be revised for.

Assuming Go experience implies concurrency experience. A large share of production Go is request in, query the database, response out, with no concurrency beyond what net/http does for you. That is legitimate work and it teaches none of the things that break under load. Ask what they have actually run concurrently and what went wrong when they did.

Skipping the race detector because the tests are green. Data races are timing-dependent by definition, so a passing suite tells you nothing. go test -race is a single flag and catches a class of bug otherwise discovered in production at three in the morning. A candidate who has never run it has told you something.

Ignoring the loop-variable change. Before Go 1.22, a range loop reused one variable across iterations, so a goroutine closing over it usually observed the final value. Go 1.22 made loop variables per-iteration and that entire class of bug went away for new code. Ask about it: developers who learned earlier still write the workaround reflexively, and developers who learned after often have no idea why it litters your codebase.

How It Works

  1. Discovery, 30 minutes. What the services do, where the concurrency actually is, your deployment target, and whether this is new build or rescue work.
  2. Shortlist within 72 hours. Three to five profiles, screened on concurrency rather than on syntax.
  3. You interview. Your process, your technical test. We suggest handing over a leaking handler and asking what is wrong with it.
  4. Onboarded in two weeks. Contracts, NDA, IP assignment, repository and deployment access.
  5. 30-day guarantee. One email, replacement candidates within 48 business hours, no cost. Full terms →

Rates depend on seniority, systems experience, and engagement length. Tell us the role and we will give you a firm number.

Tell us the role and we will send a shortlist within 72 hours.

Tell Us the Role
FAQs

Questions

Can a strong Java or Python developer move to Go?

Faster than into most languages, because the syntax really is small. The gap is concurrency, and closing it takes months of production exposure rather than a course. We will tell you plainly which profiles are converted developers and which have run Go under load.

Do we need Go, or is this actually a database problem?

Worth asking before you commit. A service rewritten from Python to Go often gets faster because it was rewritten, not because it is Go. If your latency is dominated by queries, you will move the bottleneck and keep the number.

What about generics?

Available since Go 1.18 and unremarkable now. Not a screening topic in its own right. What matters is whether somebody reaches for a type parameter in a place where an interface was already fine.

Which framework do you screen for?

Mostly none. Go teams commonly build on net/http and the standard library, sometimes with chi or Gin for routing. Framework familiarity is a week of ramp-up; concurrency judgement is not.

How do we test for goroutine leaks?

Ask a candidate and listen. Good answers mention checking runtime.NumGoroutine around a test, the goleak package, or reading /debug/pprof/goroutine from a running service. A weak answer is that memory usage looks fine.

Get Your Shortlist

Tell us the role. Three to five profiles within 72 hours.