Go, the language, was designed so that a competent engineer coming from Java or Python can read it on day one and write it in week one. That is a genuine achievement and it wrecks your interview.
Because the things that go wrong in production Go are not things you can see by reading a page of it. A goroutine that never returns looks exactly like a goroutine that returns. A context that is not threaded through looks tidier than one that is. A mutex protecting the wrong thing compiles, passes review and is fine for eleven months.
So a Go assessment that asks somebody to write a function is testing the part of Go that takes a week. Here is how to test the part that takes two years.
The four things worth testing
Concurrency lifecycle, not concurrency syntax. Anybody can write go doThing(). The question is who stops it, what happens to it when the request is cancelled, and where its error goes. Most real Go bugs live here: leaked goroutines, a WaitGroup counted wrong, a send on a channel nobody is receiving from any more, a worker pool that deadlocks only when the queue is full.
Context discipline. Whether context.Context is threaded to the places that do IO or dropped at the first convenient boundary, whether cancellation actually cancels anything, whether a timeout on the handler means anything downstream. This is the difference between a service that sheds load and one that falls over holding every request it ever accepted.
Error handling as design. Not "do they check err". Whether errors are wrapped with something a person on call could use, whether a sentinel is compared with errors.Is rather than a string match, whether an error crossing a package boundary still means anything. Go pushes this decision onto the author more than most languages, which is exactly why it separates people.
Reading and interfaces. Small interfaces defined by the consumer, accepting an interface and returning a struct, and above all whether they can navigate somebody else's package layout. Most of a Go job is reading Go somebody else wrote in a hurry.
Notice that none of these four are visible in a function-writing exercise, and all four are visible within twenty minutes on a running service.
Four scenarios that surface it
These are built to run on a real machine with a real service, because every one of them depends on the program actually executing.
1. The service that gets slower and never recovers. Give them an HTTP service that handles requests fine, then degrades under load and never comes back, because each request spawns a goroutine that blocks on a channel read after the client has gone. pprof is available. Ask them to find out why memory climbs.
What you learn: whether they reach for the goroutine profile at all. Somebody who has run Go in production types /debug/pprof/goroutine?debug=1 almost reflexively and reads the stack counts. Somebody who has only written Go starts reading the handler code top to bottom. Both may get there. The order tells you which one they are.
2. The cancellation that does not cancel. A handler with a five second timeout that calls a downstream service, and the downstream call ignores the context. Load it and watch the goroutine count climb even though every request has returned. Ask why the timeouts are not helping.
What you learn: whether they have a real model of what a context does. A surprising number of engineers believe a timeout on a handler stops the work. Watching someone discover that it does not, live, is very informative.
3. The race that only shows under -race. A cache with a read path that looks safe and is not. It passes tests. Ask them to make the tests trustworthy.
What you learn: whether -race is in their fingers, and what they do with the report. The report is easy to read and points at two lines. The judgment is in the fix: a mutex, a sync.Map, a channel, or restructuring so the shared state is not shared. All four are defensible and the reasoning separates them.
4. Read and extend an unfamiliar package. Hand them a repository of a few thousand lines and a small feature request that touches three files. No bug, no trick.
What you learn: how they navigate, whether they follow the existing idioms or import their previous language's patterns, and whether they notice the codebase's conventions before overwriting them. This is the closest thing to the actual job.
Why this needs a machine, not an editor
Every one of those four scenarios requires running the program: profiling it, loading it, racing it, building it. In a browser-based code pad the candidate can describe what they would do, and describing is a different skill that correlates with interview practice rather than with the work.
In EasyEnv the candidate gets a real box with the service, the toolchain, pprof, a load generator and the repository already there. They work the way they would on a Tuesday, and the session is recorded, so the review is over what they actually ran and in what order. For Go specifically that record is the assessment, because the interesting part is the search: how quickly the goroutine profile occurs to somebody is a much stronger signal than whether they eventually explain leaks correctly.
It also lets you allow AI assistance honestly. A model will explain goroutine leaks well in general and cannot tell you which of the seventeen goroutines in this dump is the one that matters. Watch which they check first.
Calibrating what you see
Has written Go. Correct syntax, err != nil everywhere, goroutines started and never accounted for, context taken as a parameter and not passed on. Reads code linearly. This is a competent engineer who has not yet run Go under load, and that is a training gap, not a no.
Has shipped Go. Reaches for the goroutine profile early. Talks about who owns a channel's close. Wraps errors with %w without being prompted. Notices a missing defer cancel() while reading past it. Has a preference between a mutex and a channel and can say what it depends on.
Has been on call for Go. Everything above, plus asking what the deploy looks like, whether there is a readiness probe, what happens on SIGTERM, and whether the pool size is tuned for the downstream service or copied from a blog post. These questions are the tell, and no assessment can prompt them without giving them away.
What not to test
Generics, unless the job has them. Micro-optimisation of allocations, unless the job is that. Anything about the runtime scheduler's internals, which is trivia unless somebody is writing a profiler.
And do not ask them to implement a data structure. It is a fine exercise in some languages and in Go it mostly tests whether they enjoy typing.
Reading legacy code live covers the fourth scenario in more depth, and how to assess debugging skill covers the search behaviour that the first three are really measuring.
When your Go service last fell over, would the version of your interview you run today have found the person who fixed it?