Hire Go developers
Go is a small, deliberately boring language built for services and infrastructure, and hiring for it means valuing clarity and concurrency discipline over cleverness.
What Go actually is
Go is a statically typed compiled language developed at Google, designed around a specific set of priorities: fast compilation, simple syntax, built-in concurrency, and a single static binary as the output. It has a small specification that a competent developer can hold in their head, a strong standard library, and a culture that actively discourages cleverness.
Those choices make it unusually well suited to network services and infrastructure software. A Go service compiles to one binary with no runtime to install, starts in milliseconds, uses modest memory, and handles many concurrent connections comfortably. A great deal of the infrastructure the industry runs on is written in it, including container tooling, orchestration systems and observability platforms.
The same choices make it a poor fit for other things, and a good Go developer will tell you so. The language is deliberately spare. There is no deep inheritance, the error handling is repetitive by design, and the type system is less expressive than several alternatives. Teams that want maximum expressiveness find it frustrating; teams that want code a new joiner can read on their first day find it exactly right.
The part that separates seniors from mid-levels
Concurrency is the language's distinguishing feature and its main source of bugs. Goroutines are cheap enough that developers start thousands without thinking, and channels make coordination straightforward until they do not. The failures are specific and recognisable: goroutines that never exit because nothing reads from a channel, deadlocks from unbuffered channels used carelessly, and data races on shared state that only appear under load. A developer who has run the race detector in continuous integration and fixed what it found is describing a different level of experience from one who has only read about it.
Error handling is the second thing to probe, because Go's approach is explicit and repetitive and the shortcuts people take to avoid the repetition are where problems start. Errors are values, returned and checked at each step. Wrapping them with context so that a failure deep in a call chain can be understood at the top, and checking for specific error types rather than matching on strings, are the marks of someone who has debugged a production Go service.
Context is the third, and it is the mechanism by which Go services stay healthy under pressure. A context carries cancellation and deadlines through a call chain, so that when a client disconnects or a timeout expires the work stops rather than continuing to consume resources. Services written without it look fine until something upstream is slow, at which point goroutines accumulate until the process dies. Passing context properly is close to the definition of competent Go service code.
Where Go is used
The label “Go developer” covers several jobs that share a technology and little else. These are the settings the work usually turns up in, and the one you are hiring into should shape the whole process, because the judgement each demands is different.
Backend services and APIs
HTTP and gRPC services where throughput, predictable latency and low memory use matter.
Infrastructure and platform tooling
Container, orchestration and deployment tooling, where a single static binary is a major operational advantage.
Command-line tools
Internal and distributed tooling, where cross-compilation to a single binary removes an entire class of installation problem.
Observability and data plane systems
Agents, collectors and proxies that must be efficient and always running.
High-concurrency network services
Systems handling many simultaneous connections, which the concurrency model handles well.
Microservice estates
Organisations that chose Go for services specifically to keep them small, fast to start and cheap to run.
If a candidate's experience sits in a different row of that list from the work you have, that is not a reason to reject them, but it is the thing to probe. Ask what would be different about their approach in your setting. Someone who can answer that has transferable judgement. Someone who says it would be much the same has probably not thought about it.
Which Go versions are still supported
Go maintains an unusually strong backward compatibility promise and supports the two most recent major releases, which makes staying current one of the lowest-risk upgrades in any ecosystem.
The table is generated from public release data rather than written by hand, so it states what is supported today rather than what was true when any given article was published. Of the 8 most recent release lines, 2 are still maintained and 6 have passed their published end-of-life date. That distinction is the practical one when you read a job specification: a requirement written against a line that is now out of support tells you the specification is older than the codebase it describes, and it is worth asking which of the two the new developer will actually work in.
| Release | Released | End of life | Status | Latest patch |
|---|---|---|---|---|
| 1.27 | 2026-08-19 | none published | No published end-of-life date | 1.27.1 (2026-09-01) |
| 1.26 | 2026-02-10 | none published | No published end-of-life date | 1.26.8 (2026-09-01) |
| 1.25 | 2025-08-12 | 2026-08-19 | End of life | 1.25.14 (2026-08-19) |
| 1.24 | 2025-02-11 | 2026-02-10 | End of life | 1.24.13 (2026-02-04) |
| 1.23 | 2024-08-13 | 2025-08-12 | End of life | 1.23.12 (2025-08-06) |
| 1.22 | 2024-02-06 | 2025-02-11 | End of life | 1.22.12 (2025-02-04) |
| 1.21 | 2023-08-08 | 2024-08-13 | End of life | 1.21.13 (2024-08-06) |
| 1.20 | 2023-02-01 | 2024-02-06 | End of life | 1.20.14 (2024-02-06) |
Source: endoflife.date public release data, read 2026-09-25.
Two questions follow from this table in an interview. The first is which line the candidate has most recently shipped against, because someone whose last production work was on an unsupported line has not had to deal with the changes since. The second is how they have handled an upgrade. Version migrations are where you see whether a developer reads release notes, writes characterisation tests before changing anything, and knows how to stage a rollout, or whether they upgrade in place on a Friday and hope.
The toolchain around it
Nobody hires for Go alone. The surrounding tools are where most of the day-to-day work happens, and a gap in any of them costs more time than a gap in the core library. This is the set that turns up most often on real job specifications alongside it.
- The standard library
- Unusually capable, and idiomatic Go uses it far more than most ecosystems use theirs.
- chi, gin or the standard router
- HTTP routing. Recent standard library improvements have reduced the need for a framework at all.
- sqlc or pgx
- Database access. Go culture generally prefers explicit SQL over an ORM.
- PostgreSQL
- The most common data store behind Go services.
- gRPC and protocol buffers
- Service-to-service communication in larger estates.
- The race detector
- Built in, and its use in continuous integration is a direct indicator of team maturity.
- OpenTelemetry
- Tracing and metrics, with context propagation fitting the language's model naturally.
- golangci-lint
- Aggregated linting, standard in well-run Go projects.
Related skills that frequently appear on the same specification: Rust, SQL, AWS, DevOps, Kubernetes.
What to test in an interview
These are the topics that separate candidates in practice. Each one is given with why it discriminates, what a strong answer sounds like, and the response that should make you slow down. None of them requires a whiteboard.
Goroutine lifecycle
Goroutine leaks are the most common serious fault in production Go.
- Strong answer: Knows how every goroutine they start will end, uses context for cancellation, and has diagnosed a leak.
- Warning sign: Starts goroutines without a clear exit path, or has never considered that they might accumulate.
Channels versus mutexes
Tests judgement rather than knowledge, since both are correct in different situations.
- Strong answer: Uses channels for passing ownership and mutexes for protecting state, and can explain choosing each.
- Warning sign: Insists channels are always the answer, or avoids concurrency primitives entirely.
Error handling and wrapping
Go's explicitness means poor error handling is highly visible and highly consequential.
- Strong answer: Wraps with context, checks specific errors rather than strings, and does not discard errors silently.
- Warning sign: Ignores returned errors, or panics as a general error strategy.
Context propagation
The mechanism that keeps services healthy when something upstream degrades.
- Strong answer: Passes context through every call, honours cancellation, and sets deadlines on outbound requests.
- Warning sign: Uses a background context everywhere, or treats context as a way to pass values around.
The race detector
Direct evidence of engineering discipline.
- Strong answer: Runs it in continuous integration and can describe a race it found and how it was fixed.
- Warning sign: Has heard of it and never run it.
Interface design
Go's interfaces are satisfied implicitly, which enables good design and invites over-abstraction.
- Strong answer: Defines small interfaces at the point of use, and can explain accepting interfaces and returning concrete types.
- Warning sign: Writes large interfaces alongside every implementation out of habit.
Why Go for this problem
Reveals whether they have judgement or a preference.
- Strong answer: Names what Go is good at and what it is not, and can describe a case where another language would have been better.
- Warning sign: Treats Go as universally correct, or cannot name a weakness.
Warning signs in a Go codebase
The fastest way to read a candidate is to ask what they have found wrong in code they inherited. These are the patterns that come up most often, what they cost, and what fixing them looks like. A developer who recognises three or four of these from their own experience is worth more than one who can recite the documentation.
Leaked goroutines
- What you see: Goroutines started without a defined exit, often blocked forever on a channel.
- What it costs: Memory grows steadily until the process is killed, typically hours or days after deployment.
- The fix: Give every goroutine an exit path driven by context cancellation. Monitor the goroutine count as a first-class metric.
Ignored errors
- What you see: The blank identifier used to discard returned errors.
- What it costs: Failures pass silently and surface later as corrupt state with no trace of the origin.
- The fix: Handle or propagate every error. Where ignoring is genuinely correct, comment why, and let linting catch the rest.
Panic as control flow
- What you see: Panic used for ordinary error conditions.
- What it costs: A crashed service where an error return would have degraded gracefully.
- The fix: Return errors. Reserve panic for genuinely unrecoverable programmer error such as an impossible state at startup.
Background context everywhere
- What you see: context.Background() passed at every layer instead of the request's context.
- What it costs: Cancellation and deadlines never propagate, so work continues after clients have gone and the service degrades under exactly the conditions where it should be shedding load.
- The fix: Thread the request context through every call and honour it. Set explicit deadlines on outbound calls, and treat a background context outside main and startup code as a defect.
Over-abstracted interfaces
- What you see: An interface defined for every struct, in the package that implements it.
- What it costs: Indirection with no benefit, and code that is harder to follow for no gain in flexibility.
- The fix: Define interfaces where they are consumed, keep them small, and let them emerge from a second implementation rather than anticipating one.
Shared state without synchronisation
- What you see: Maps or slices written from several goroutines.
- What it costs: Data races producing corruption that appears only under load and cannot be reproduced.
- The fix: Run the race detector in continuous integration. Protect shared state or avoid sharing it.
What each level can own
Job titles are not comparable between companies, so it is more useful to describe levels by what a person can be left to own without supervision. These are the boundaries we use when we assess a Go developer.
- Junior
- Writes handlers and business logic in an existing service. Needs review on concurrency and error handling.
- Mid-level
- Owns a service including its tests and its concurrency. Understands context propagation and can read a profile.
- Senior
- Owns service architecture, the concurrency model, the observability that makes incidents diagnosable, and the boundaries between services.
- Staff
- Owns cross-service contracts, the shared library and tooling standards, and the decision about which workloads belong in Go at all.
How the work is usually scoped
Team shape follows the kind of work, not the headcount you happen to have budget for. These are the shapes that come up most often and the constraint that actually governs each one.
New API service
- Usual team: One to two developers.
- What governs it: Go's simplicity makes this fast. The data access approach and the error contract are the decisions worth taking time over.
Rewriting a service for performance
- Usual team: One senior developer.
- What governs it: Only justified when the existing runtime is genuinely the constraint, which is less often than proposed.
Internal command-line tooling
- Usual team: One developer.
- What governs it: Cross-compilation to a single binary is the reason to choose Go here, and it removes most distribution problems.
Infrastructure component
- Usual team: One to two senior developers.
- What governs it: Scoped by the operational requirements rather than the feature list. These systems are judged on behaviour under failure.
Concurrency and stability remediation
- Usual team: One senior developer with production access.
- What governs it: Starts with goroutine counts and profiles. Usually resolves to leaks and missing context propagation.
Migration work you may actually be hiring for
A large share of Go work is not new development. It is moving an existing system from one state to another while it stays in service. These are the migrations that come up most often, and each one asks for a different kind of experience from the person you hire.
A dynamic language service to Go
- Why teams do it: Predictable latency, lower memory use, and removing a runtime from the deployment.
- What to watch: Only worth it when the existing runtime is genuinely the constraint. Rewrite one service at a time behind a stable interface, and measure the before state properly so the claim can be checked afterwards.
A third-party HTTP router to the standard library
- Why teams do it: Recent standard library routing covers most cases, removing a dependency from the critical path.
- What to watch: Middleware written against a framework's own types will not port directly. Worth doing on new services first rather than converting existing ones for its own sake.
An older Go version to a supported release
- Why teams do it: Security fixes and language improvements, with unusually strong backward compatibility making this low risk.
- What to watch: Go's compatibility promise makes this one of the easier upgrades in any ecosystem. The usual blocker is a dependency, not the language.
Services without context propagation to context throughout
- Why teams do it: Cancellation and deadlines are what prevent one slow dependency from exhausting the whole service.
- What to watch: It touches every function signature in the call chain, so it is a wide but shallow change. Do it in one pass per service rather than incrementally, since a half-propagated context provides little benefit.
Migration work rewards a different temperament from greenfield work. The useful question in an interview is not whether someone has done the specific migration you face, but whether they have ever run one incrementally: behind a flag, with both paths live, and with a way back. Developers who have only done big-bang cutovers tend to propose them again.
What a good brief for this role contains
Most of the time lost in hiring a Go developer is lost before anyone is interviewed, in the gap between what the brief says and what the team actually needs. These are the points that, for this technology specifically, change who the right candidate is. A brief that answers them can be matched in days. One that does not produces a shortlist that looks reasonable and converts badly.
- What the service does and why Go was chosen, since the answer reveals whether the fit is genuine.
- Whether the work is service development, infrastructure tooling, or command-line tooling, which attract different people.
- What the concurrency requirements actually are, because this is where Go experience is either essential or irrelevant.
- How database access is handled, since Go teams differ sharply on ORMs.
- Whether the person will operate the service, including watching goroutine counts and diagnosing leaks.
- Whether gRPC is in use, since that is a distinct body of experience from HTTP services.
If you cannot answer some of these yet, that is normal and it is still worth writing down which ones are open. An unknown that is named can be worked around. An unknown that is papered over in a job specification turns into a rejected shortlist and a restart four weeks later.
What the US market pays for this work
Go work is counted by the US Bureau of Labor Statistics under Software Developers. That classification is broader than the technology itself, so treat the figures as the shape of the market a Go developer is hired into rather than as a rate card for the skill. Across the United States the Bureau counts 1,687,890 people in this occupation, with a median annual wage of $135,980.
The spread matters more than the midpoint. The 90th percentile is about 2.6 times the 10th, which is a wide band for a single occupation and tells you that the title on its own carries very little pricing information. Two people described as a Go developer can sit at $82,460 and $214,670 in the same national dataset. When a budget is set from a median without asking which end of that range the work actually needs, the hire that follows is usually the wrong one in one direction or the other.
Related classifications are worth reading alongside it, because teams hiring for Go frequently end up recruiting against these titles too:
| Occupation | Employed | 25th percentile | Median | 75th percentile | 90th percentile |
|---|---|---|---|---|---|
| Software Developers | 1,687,890 | $105,210 | $135,980 | $171,980 | $214,670 |
| Computer Programmers | 92,230 | $75,850 | $100,390 | $130,680 | $160,460 |
Source: BLS Occupational Employment and Wage Statistics, May 2025. Figures cover all US employers and are not FuturByte rates.
These are employer-side wage figures for people on a US payroll. They exclude employer taxes, benefits, recruitment cost and the months a seat sits empty, all of which are real and none of which appear in a salary line. The useful way to read the table is as the cost of the alternative you are comparing against, not as a number to match.
How US metro markets compare for this role
The same job is priced very differently across the country. Ranked by median annual wage for Software Developers, the gap between the highest and lowest of the 28 metro areas covered here is a factor of about 1.7. San Jose sits at the top with a median of $213,110; Pittsburgh sits at the bottom with $124,500. A budget built from a national median will be wrong in both of those markets, in opposite directions.
| Metro area | Employed | Median wage | vs US median | Location quotient |
|---|---|---|---|---|
| San Jose, CA | 87,350 | $213,110 | +57% | 7.09 |
| San Francisco, CA | 69,030 | $186,640 | +37% | 2.68 |
| Seattle, WA | 92,770 | $167,280 | +23% | 4.10 |
| New York, NY | 121,000 | $166,830 | +23% | 1.17 |
| Boston, MA | 42,310 | $166,090 | +22% | 1.44 |
| San Diego, CA | 20,610 | $163,270 | +20% | 1.23 |
| Los Angeles, CA | 55,540 | $160,920 | +18% | 0.82 |
| Portland, OR | 18,260 | $156,000 | +15% | 1.39 |
| Washington, D.C. | 69,060 | $154,930 | +14% | 2.03 |
| Baltimore, MD | 16,850 | $138,900 | +2% | 1.14 |
| Denver, CO | 27,010 | $137,610 | +1% | 1.55 |
| Charlotte, NC | 20,820 | $135,920 | 0% | 1.41 |
| Chicago, IL | 40,370 | $134,380 | -1% | 0.82 |
| Austin, TX | 31,960 | $134,120 | -1% | 2.28 |
| Dallas-Fort Worth, TX | 67,030 | $133,290 | -2% | 1.52 |
| Philadelphia, PA | 28,480 | $133,040 | -2% | 0.91 |
| Atlanta, GA | 36,300 | $132,960 | -2% | 1.16 |
| Raleigh, NC | 12,580 | $132,770 | -2% | 1.56 |
| Miami, FL | 18,900 | $132,650 | -2% | 0.62 |
| Phoenix, AZ | 29,380 | $131,750 | -3% | 1.14 |
| Minneapolis-St. Paul, MN | 27,410 | $130,920 | -4% | 1.29 |
| Detroit, MI | 24,870 | $130,760 | -4% | 1.20 |
| Tampa, FL | 14,230 | $130,450 | -4% | 0.91 |
| Orlando, FL | 13,440 | $129,620 | -5% | 0.88 |
| Salt Lake City, UT | 19,040 | $129,600 | -5% | 2.12 |
| Houston, TX | 22,940 | $129,440 | -5% | 0.64 |
| Kansas City, MO | 12,160 | $124,990 | -8% | 1.02 |
| Pittsburgh, PA | 10,320 | $124,500 | -8% | 0.85 |
Location quotient compares how concentrated this occupation is in the metro against the national average. A value above 1 means the metro has more of this work than its size would predict.
The location quotient column is the more useful one for hiring. A high median tells you what a role costs; a high quotient tells you whether the people exist. San Jose, San Francisco, Seattle, Washington, D.C., Denver, Austin each have a quotient of 1.5 or above, meaning the work is concentrated there well beyond what the size of the local economy would predict. Those are the markets where a search is likely to be quick and competitive at the same time, and where a counter-offer is most likely to take a candidate off the table late in the process.
The opposite case is worth planning for too. In a metro with a low quotient, the total pool is small even when wages look reasonable, so the realistic options are to widen the search radius, accept a longer time to hire, or bring the capability in from outside the local market entirely. That last option is what most teams are weighing when they come to us.
Hiring risks worth naming
Every one of these has produced a bad hire somewhere. They are written down so that the process tests for them deliberately rather than discovering them in month three.
Syntax familiarity without concurrency discipline. Go is easy to learn and its concurrency is not. Test goroutine lifecycle and context explicitly.
Importing patterns from other languages. Developers from heavily object-oriented backgrounds often build abstraction Go does not need. Ask them to critique an over-abstracted example.
No production operating experience. Leaks and races only teach you in production. Ask about an incident they diagnosed.
Choosing Go for the wrong workload. Ask when they would not use it. Someone with no answer will not push back when it is the wrong choice.
Hiring Go developers by metro area
Wages for this occupation vary more between US metro areas than most budget models assume. Each page below sets out the published employment and wage figures for that market, how it compares with the national picture, and what the local industry mix means for the kind of Go developer who will be available.
- New York, NY $166,830 median
- Seattle, WA $167,280 median
- San Jose, CA $213,110 median
- Washington, D.C. $154,930 median
- San Francisco, CA $186,640 median
- Dallas-Fort Worth, TX $133,290 median
- Los Angeles, CA $160,920 median
- Boston, MA $166,090 median
- Chicago, IL $134,380 median
- Atlanta, GA $132,960 median
- Austin, TX $134,120 median
- Phoenix, AZ $131,750 median
- Philadelphia, PA $133,040 median
- Minneapolis-St. Paul, MN $130,920 median
- Denver, CO $137,610 median
- Detroit, MI $130,760 median
- Houston, TX $129,440 median
- Charlotte, NC $135,920 median
- San Diego, CA $163,270 median
- Salt Lake City, UT $129,600 median
- Miami, FL $132,650 median
- Portland, OR $156,000 median
- Baltimore, MD $138,900 median
- Tampa, FL $130,450 median
- Orlando, FL $129,620 median
- Raleigh, NC $132,770 median
- Kansas City, MO $124,990 median
- Pittsburgh, PA $124,500 median
Frequently asked questions
Why would we choose Go over Node.js or Python?
For predictable performance under concurrency, low memory use, fast startup and a single deployable binary with no runtime to install. Those matter most for infrastructure components, high-throughput services and anything that has to be distributed and run somewhere you do not control. For a data-heavy application with lots of business rules, a more expressive ecosystem is often the better trade.
Is Go hard to learn?
The language is genuinely small and a competent developer writes useful Go within a week. What takes longer is the idiom, particularly around interfaces and error handling, and the concurrency model, where mistakes are easy to make and hard to see. Budget weeks for productivity and months for the judgement that prevents goroutine leaks.
Do we need a framework?
Usually not. Go's standard library covers most of what a web framework provides elsewhere, and recent improvements to routing have narrowed the gap further. Lightweight routers are common and reasonable. A heavy framework in Go is generally a sign that someone is importing habits from another ecosystem.
Should we use an ORM with Go?
Most experienced Go teams do not, preferring explicit SQL with a tool that generates typed code from queries. That fits the language's preference for clarity over magic, and it avoids the class of performance surprise that ORMs produce. ORMs exist and work; they are simply less culturally dominant here than in Python or Ruby.
Why does our Go service use more memory over time?
The overwhelmingly common cause is leaked goroutines: work started that never finishes, usually blocked on a channel nobody reads or an HTTP call with no timeout. The goroutine count is the metric that reveals it, and most teams are not watching it. Once it is on a dashboard the problem usually becomes obvious quickly.
Is the error handling really that repetitive?
Yes, and it is deliberate. The trade is verbosity in exchange for every failure path being visible in the code rather than implied. Teams that find it intolerable are usually right that it is tedious and wrong that it is pointless; the visible error path is a large part of why Go services tend to fail predictably.
Can Go developers be hired from other backgrounds?
Readily, and many Go developers came from elsewhere, because the language is small enough to learn on the job. The thing to test for is not Go knowledge but systems thinking: concurrency, failure handling and operational awareness. Those transfer and matter far more than familiarity with the syntax.
Is Go a good choice for a small team?
Often yes, particularly for services. Its main advantage for a small team is that the code is easy to read months later and there is little dialect variation between developers. Its main disadvantage is that you will write more code for the same feature than in a more expressive language, which matters more when the product is still changing shape than when it is stable.