Amazon Backend Engineer Interview: Go Language and Microservice Architecture Deep Dive
A 3-year Go backend developer shares their Amazon experienced hire interview, covering Go fundamentals (GMP/memory allocation/GC/channels), microservices (service discovery/distributed transactions/circuit breaking/gRPC), and system design (distributed rate limiting), ultimately landing the offer.
Background
Let me start with my background: 3 years of Go backend development experience. I currently work at a cloud services startup, mainly responsible for microservice architecture backend development. My tech stack is Go + gRPC + Kubernetes — daily work involves writing APIs, doing service decomposition, and handling various distributed systems challenges. This time I applied for a backend developer position (Go direction) at Amazon, because Amazon's tech stack aligns closely with my experience, and Amazon has one of the best Go ecosystems in the industry.
Amazon's experienced hire process goes: resume screening → Technical Round 1 → Technical Round 2 → Technical Round 3 → HR round. The entire process took about three weeks — a fairly tight pace. Amazon's interview style is known for being hardcore: every round has a coding component, and interviewers will keep pushing until you can't answer anymore. Honestly, I was completely drained after the interviews.
Below, I'll break down each round in detail.
Interview Process Breakdown
Technical Round 1: Go Fundamentals Deep Dive (About 70 Minutes)
The first round was a video interview. The interviewer was a Go backend developer on the target team, and all questions revolved around the Go language itself.
1. How does Go's GMP scheduling model work?
I started from the three roles: G (goroutine), M (machine/thread), and P (processor/logical processor). G is a user-space lightweight thread, M is an OS thread, and P is the intermediary between G and M, containing the resources needed to run G. Scheduling flow: Gs in P's local queue are handed to M for execution; when the local queue is empty, Gs are fetched from the global queue; when the global queue is also empty, Gs are stolen from other Ps (work stealing). The interviewer asked why P is needed — I said P's existence means G scheduling doesn't require a global lock; each P has its own local queue, reducing lock contention. He also asked what happens to GMP during system calls — I said when G makes a system call, M detaches from P, P binds to a new M to continue executing other Gs, and after the system call completes, G tries to acquire an idle P; if none is available, G goes into the global queue and M goes to sleep.
2. How does Go's memory allocation work?
I described the TCMalloc-inspired approach: memory is divided into three levels — span, cache, and central. Each P has an mcache; when allocating small objects, they're fetched directly from mcache without locking. When mcache runs low, it fetches from mcentral; when mcentral runs low, it fetches from mheap. The interviewer asked about object size classifications — I mentioned tiny objects (<16B, coalesced allocation), small objects (16B-32KB, from mcache), and large objects (>32KB, directly from mheap).
3. How does Go's garbage collection work?
I explained that Go uses a tri-color marking algorithm + hybrid write barrier. The three colors are white (unvisited), gray (visited but references not scanned), and black (visited and references scanned). When GC starts, all objects are white; root objects are marked gray, then gray objects are continuously dequeued to scan their references — scanned gray objects become black, and referenced white objects become gray. Ultimately, white objects are garbage. The hybrid write barrier ensures no objects are missed during GC. The interviewer asked about STW timing — I said there's only a very brief STW during the stack scanning phase at the start of marking and at the end of marking; the concurrent marking phase runs concurrently with user code.
4. How are channels implemented under the hood?
I explained that a channel is fundamentally an hchan struct containing a ring buffer, send wait queue (sendq), receive wait queue (recvq), and a mutex (lock). Unbuffered channels require both sender and receiver to be ready; otherwise, the goroutine blocks in the wait queue. Buffered channels block sends when the buffer is full and block receives when the buffer is empty. The interviewer asked about select's randomness — I said when multiple cases in a select are simultaneously ready, one is chosen at random, which prevents starvation.
5. Coding: Implement a goroutine pool in Go.
I implemented a fixed-size worker pool: creating a specified number of goroutines as workers, receiving tasks through a channel, with workers taking tasks from the channel for execution. The interviewer asked me to add graceful shutdown logic — I implemented it using context and WaitGroup: notifying all workers to exit via context, and using WaitGroup to wait for all workers to finish their current tasks.
Technical Round 2: Microservices Deep Dive (About 80 Minutes)
The second round was a video interview. The interviewer was the team's tech lead, and all questions revolved around microservice architecture.
1. How do you implement service discovery in microservices?
I described our use of Consul for service registration and discovery. Services register with Consul on startup and deregister on shutdown. Clients query Consul for available service lists and select instances with load balancing strategies. The interviewer asked about the difference between Consul and Etcd — I said Consul has built-in service discovery and health checking, while Etcd is more of a general-purpose distributed KV store where service discovery needs custom implementation. He also asked about gRPC service discovery — I said we use gRPC's resolver interface for custom service discovery logic, fetching address lists from Consul and creating connections.
2. How do you handle distributed transactions?
I described two main approaches: for scenarios requiring strong consistency, we use the Saga pattern, where each step has a corresponding compensating action executed in reverse order on failure; for scenarios where eventual consistency suffices, we use message queues + local message tables, where the sender places the message and business operation in the same transaction, and consumers consume idempotently. The interviewer asked about the difference between Saga and TCC — I said Saga uses business-level compensation without resource reservation, while TCC (Try-Confirm-Cancel) requires resource reservation, providing stronger consistency but more complex implementation.
3. How do you implement circuit breaking and degradation?
I described using Hystrix-Go for circuit breaking — when the error rate exceeds a threshold, the circuit automatically breaks, and after breaking, degradation logic returns default values or cached data. Degradation strategies vary by business scenario: core APIs degrade to cached data, non-core APIs return errors directly. The interviewer asked about the circuit breaker's three states — I said Closed (normal pass-through), Open (direct rejection), and Half-Open (let a small number of requests through as probes; if successful, return to Closed; if failed, return to Open).
4. What's the difference between gRPC and HTTP?
I covered several core differences: gRPC uses Protocol Buffers for serialization — smaller size and faster speed; HTTP typically uses JSON — more readable but larger. gRPC is based on HTTP/2, supporting multiplexing and streaming; HTTP/1.1 requires a new connection for each request. gRPC has strongly-typed IDL interface definitions; HTTP's interface definitions are more flexible but lack constraints. The interviewer asked about gRPC's streaming RPC — I described four types: Unary (request-response), Server Streaming, Client Streaming, and Bidirectional Streaming.
5. How do you ensure API idempotency?
I mentioned several approaches: unique request ID + deduplication table, database unique constraints, optimistic locking (version numbers), and state machine constraints. The interviewer asked for a specific example — I described using unique request IDs for payment API idempotency: the client generates a unique paymentId, the server first checks the deduplication table for prior processing, and if already processed, returns the existing result; if not, executes the payment logic and writes to the deduplication table.
Technical Round 3: System Design (About 60 Minutes)
The third round was with the department's technical director, who asked one major system design question.
1. Design a highly available distributed rate limiting system.
I designed it from several dimensions:
First, rate limiting algorithm selection: token bucket algorithm, supporting burst traffic, suitable for most scenarios. Each service instance maintains a local token bucket for per-instance rate limiting.
Then, distributed rate limiting: a centralized counter is needed to ensure global rate limiting. I designed using Redis + Lua scripts for atomic token dispensing, where the Lua script ensures checking and deduction are an atomic operation. The interviewer asked what happens if Redis goes down — I gave two solutions: first, Redis cluster for high availability; second, degrade to local rate limiting, which is less precise but at least protects the service.
Next, rate limiting granularity: supporting multiple dimensions like per-user, per-IP, per-API, per-service. Each dimension corresponds to a token bucket.
Finally, configuration management: rate limiting rules are stored in a configuration center (like etcd), loaded on service startup, and hot-updated on configuration changes without service restart.
The interviewer was satisfied with this design and asked about several details: how to handle clock skew (I said use logical clocks instead of physical clocks) and how to monitor rate limiting effectiveness (I said expose Prometheus metrics and use Grafana dashboards to monitor pass rates and rejection rates).
2. What do you think is the biggest challenge with microservices?
I mentioned three: first, the complexity of inter-service communication — networks are unreliable and latency is uncontrollable, requiring proper timeout, retry, and circuit breaking strategies; second, distributed data consistency — cross-service transactions are hard to guarantee with strong consistency, requiring appropriate solutions based on business scenarios; third, observability — when microservices have issues, debugging is difficult, requiring comprehensive logging, distributed tracing, and monitoring systems. The interviewer asked about my views on Service Mesh — I said Service Mesh extracts inter-service communication logic (load balancing, circuit breaking, distributed tracing) from business code into Sidecars, reducing business code complexity but introducing additional network overhead and operational complexity.
Complete Question List
1. Go GMP scheduling model and system call handling
2. Go memory allocation mechanism (TCMalloc approach)
3. Go garbage collection (tri-color marking + hybrid write barrier)
4. Channel underlying implementation and select randomness
5. Implement a goroutine pool in Go (hand-written)
6. Service discovery solutions (Consul vs Etcd)
7. Distributed transaction handling (Saga vs TCC)
8. Service circuit breaking and degradation (Hystrix three states)
9. gRPC vs HTTP differences and streaming RPC
10. API idempotency guarantee approaches
11. Distributed rate limiting system design
12. Biggest microservices challenges and Service Mesh
Key Takeaways
1. Go fundamentals must include understanding the underlying implementation. Amazon's Go interview doesn't ask "how to use" but "how it's implemented underneath." GMP model, memory allocation, and GC mechanisms must be explainable at the source code level. I recommend reading Go's source code, especially schedule.go, malloc.go, and mgc.go in the runtime package.
2. Microservices questions should be backed by real experience. Microservice interview questions easily devolve into rote memorization, but Amazon interviewers will keep pushing for details. If you've only read theory without hands-on experience, it's easy to get exposed. Prepare 2-3 microservice problems you've encountered in real projects, explaining the symptoms, investigation process, and solutions.
3. System design answers should be structured. Don't jump straight into technical solutions for system design questions — start with requirements analysis, then overall architecture, then expand layer by layer. For each technology choice, explain why you chose it over alternatives and what trade-offs are involved.
4. Practice coding in Go style. Amazon's coding questions require writing in Go. If you mostly write business code, you might not be fluent enough with Go's concurrency primitives (goroutine, channel, select, context). I recommend practicing LeetCode in Go to get familiar with Go's coding style.
5. The interview pace is fast — adapt to follow-up questions. Amazon interviewers' follow-ups are very intensive — one question can lead to 3-4 layers of追问. Don't panic — answer what you can, and honestly say "I'm not deeply familiar with this area" for what you can't. Interviewers push to explore your depth, not to make things difficult.
6. Prepare for the reverse Q&A. At the end of each round, interviewers ask "do you have any questions?" This is your opportunity to learn about the team. Prepare 2-3 in-depth questions, such as the team's technical challenges or Go's application scenarios at Amazon.
FAQ
Q: How many rounds does Amazon's backend interview typically have?
A: 3 technical rounds + HR round, totaling 4 rounds. Some teams may finish with just 2 technical rounds — it depends on the department.
Q: What language should I use for coding?
A: If you're applying for a Go position, write in Go. Interviewers will look at your Go coding style, such as error handling and concurrency patterns.
Q: Will the Go interview include algorithm questions?
A: Yes, but not in every round. My first round had a coding question (goroutine pool), and the second and third rounds were mainly system design. Algorithm difficulty is roughly LeetCode medium.
Q: Are there education requirements for Amazon's experienced hires?
A: Bachelor's degree or above, but project experience and technical depth matter more. 3 years of experience is the basic threshold for L5.
Q: What exactly is Amazon's Go tech stack?
A: Primarily Go + gRPC + Kitex (Amazon's in-house RPC framework) + Hertz (Amazon's in-house HTTP framework) + K8s. Knowing Kitex and Hertz is a plus.