Top Go Interview Questions Every Backend Engineer Should Master
This article compiles essential interview questions covering Go fundamentals, concurrency patterns, runtime internals, microservice concepts, container technology, Redis data store, and MySQL database design, providing a comprehensive study guide for developers preparing for technical interviews.
Go Basics
Advantages of Go – native compilation, low latency startup, built‑in lightweight concurrency (goroutine, channel), strong standard library, static typing with type inference, automatic memory management, and simple module system.
Data types – basic types (bool, string, numeric types), composite types (array, slice, struct, map, channel), pointer types, function types, interface types.
Package – a unit of source code organization; each directory is a package, identified by a name and imported with import "path/to/pkg".
Type conversion – explicit conversion using type(value), e.g., float64(i) converts an int to float64. No implicit numeric conversion.
Goroutine – a lightweight thread managed by the Go runtime. Started with go f(). It stops when the function returns; you can also signal cancellation via context.Context or a channel.
Runtime type inspection – use the reflect package: reflect.TypeOf(v) returns the dynamic type.
Interface relationships – an interface can be satisfied implicitly by any type that implements its methods; interfaces can embed other interfaces to compose behavior.
Synchronization locks – sync.Mutex provides mutual exclusion; sync.RWMutex allows multiple readers or one writer. Locks are non‑reentrant and must be unlocked to avoid deadlock.
Channel characteristics – typed conduit for communication between goroutines; can be buffered or unbuffered. Sending blocks until a receiver is ready (unbuffered) or until buffer space is available (buffered). Closing a channel signals no more values.
Buffered channel – created with make(chan T, n). Sends are non‑blocking until the buffer is full; receives block when the buffer is empty.
CAP function – cap(slice) returns the capacity of a slice, array, or channel.
go convey – a testing framework for concurrent Go programs that provides assertions and deterministic execution of goroutine interactions.
new vs. make – new(T) allocates zeroed storage for type T and returns a pointer; make(T, ...) initializes slices, maps, and channels and returns the ready‑to‑use value.
printf family – fmt.Printf writes to standard output, fmt.Sprintf returns the formatted string, fmt.Fprintf writes to any io.Writer.
Array vs. slice – arrays have fixed length part of the type; slices are descriptors ( ptr, len, cap) that reference an underlying array and can grow.
Value vs. reference passing – Go passes arguments by value. For large structs or mutable data, pass a pointer ( *T) to avoid copying. Example: func modify(p *int) { *p = 5 }.
Array vs. slice passing – arrays are copied on pass; slices share the same underlying array, so modifications affect the original.
Slice expansion – when append exceeds capacity, the runtime allocates a new, larger array (usually double the capacity) and copies elements.
Defer execution order – defer statements are stacked LIFO; the last defer is executed first when the surrounding function returns. Defer is useful for resource cleanup.
Slice implementation – a slice header ( ptr, len, cap) points to an underlying array; operations manipulate the header without moving data unless reallocation occurs.
Slice expansion considerations – reallocation may cause existing pointers to the old array to become stale; avoid holding references across append that may trigger growth.
Slice identity before/after growth – after reallocation the slice points to a new array; the logical contents may be identical but the underlying memory differs.
Parameter passing and reference types – all parameters are passed by value; reference types (slices, maps, channels, pointers, functions) contain internal pointers, so the called function can affect shared data.
Map implementation – hash table with buckets; each bucket holds up to 8 key/value pairs and a pointer to overflow buckets.
Map growth – when load factor exceeds a threshold, the map allocates a larger bucket array and rehashes entries.
Map lookup – compute hash of the key, locate bucket, then linear search within the bucket.
Channel ring buffer – Go’s channel implementation uses a circular queue (ring buffer) for buffered channels, enabling lock‑free enqueue/dequeue in many cases.
Go Concurrency Programming
Mutex states – unlocked, locked, and (for sync.RWMutex) read‑locked.
Normal vs. starvation mode – normal mode allows a waiting goroutine to acquire the lock when it becomes available; starvation mode gives priority to a goroutine that has been waiting long enough to avoid indefinite postponement.
Spin conditions – a mutex may spin briefly on multi‑core CPUs when the lock holder is expected to release soon, reducing context switches.
RWMutex implementation – maintains a reader count and a writer flag; readers acquire the lock if no writer holds it, writers wait until both reader count and writer flag are zero.
RWMutex considerations – heavy read‑write contention can cause writer starvation; prefer sync.Mutex for simple exclusive access.
Condition variable (sync.Cond) – provides Wait, Signal, and Broadcast to coordinate goroutines around a shared condition, using an underlying Locker.
Signal vs. Broadcast – Signal wakes one waiting goroutine; Broadcast wakes all waiting goroutines.
Wait usage – call c.Wait() after acquiring the lock; the goroutine blocks until another goroutine calls c.Signal() or c.Broadcast().
WaitGroup – tracks a set of goroutines; Add increments the counter, each goroutine calls Done, and Wait blocks until the counter reaches zero.
WaitGroup implementation – a counter protected by an internal mutex and a semaphore to block Wait until the counter is zero.
sync.Once – guarantees that a function is executed only once, even when called from multiple goroutines.
Atomic operations – low‑level lock‑free primitives in sync/atomic (e.g., LoadInt64, StoreInt64, AddInt64) that operate on aligned 64‑bit values.
Atomic vs. lock – atomics avoid kernel scheduling overhead but are limited to simple read‑modify‑write; locks provide broader mutual exclusion.
CAS (compare‑and‑swap) – an atomic primitive that updates a value only if it matches an expected old value, enabling lock‑free algorithms.
sync.Pool – a per‑P (processor) cache of temporary objects to reduce allocation pressure; objects are reclaimed when not in use.
Go Runtime
Goroutine definition – a function executing concurrently, managed by the Go scheduler, with a small initial stack (2 KB) that grows as needed.
GMP model – the runtime uses a three‑level scheduler: G (goroutine), M (OS thread), P (processor context). Each P holds a run queue of G s and is attached to an M.
Pre‑1.0 scheduler – a simple one‑to‑one mapping of goroutine to OS thread without work‑stealing.
GMP scheduling process – when a P runs out of goroutines, it steals from other P s; idle M s may be parked and later re‑attached.
Work stealing – idle processors pull goroutines from the run queues of busy processors to balance load.
Hand‑off mechanism – when a goroutine blocks (e.g., on I/O), its M is detached and the G is placed on a wait queue; another M picks it up later.
Cooperative pre‑emptive scheduling – the runtime inserts pre‑emptive checks at function calls, loop back‑edges, and allocation points to pre‑empt long‑running goroutines.
Signal‑based pre‑emptive scheduling – on Unix, the runtime can send a SIGURG to a thread to force a pre‑emptive context switch.
Blocking issues in GMP – system calls, cgo, and network I/O can block an M, reducing parallelism unless the runtime parks the thread.
SYSMON – a background thread that monitors GC, thread creation, and other runtime metrics.
Three‑color marking – the garbage collector marks objects as white (unreachable), gray (reachable but not scanned), or black (scanned) to implement concurrent mark‑and‑sweep.
Write barriers – mechanisms that record pointer writes during GC to keep the collector’s view consistent; includes insertion, deletion, and mixed barriers.
GC trigger – GC runs when the heap size grows beyond a factor of the previous live size (controlled by GOGC).
GC process – consists of concurrent mark, stop‑the‑world mark, and concurrent sweep phases, aiming for low pause times.
GC tuning – adjust GOGC (heap growth factor), GODEBUG=gctrace=1 for tracing, and limit maximum heap size with runtime/debug.SetMemoryLimit.
Microservices
Definition – an architectural style that structures an application as a collection of loosely coupled services, each owning its data and business logic.
Advantages – independent deployment, technology heterogeneity, fault isolation, scalability per service.
Characteristics – bounded context, API‑driven communication (often HTTP/REST or gRPC), decentralized data management, automated DevOps pipelines.
Design best practices – keep services small, define clear contracts, use domain‑driven design (DDD) to model bounded contexts, avoid tight coupling, implement health checks and circuit breakers.
Operation – services communicate via lightweight protocols; service discovery and load balancing are handled by a mesh or registry.
Pros and cons – pros: scalability, resilience, faster iteration; cons: operational complexity, distributed tracing, data consistency challenges.
Monolith vs. SOA vs. Microservices – monolith: single deployable unit; SOA: coarse‑grained services often using an ESB; microservices: fine‑grained, independently deployable.
Challenges – network latency, data consistency, testing across services, monitoring, and deployment orchestration.
SOA vs. Microservices – SOA emphasizes enterprise‑level integration with heavyweight middleware; microservices favor lightweight communication and decentralized governance.
Domain‑Driven Design (DDD) – focuses on modeling the core domain, using ubiquitous language, and defining bounded contexts that map naturally to microservices.
Ubiquitous language – a shared vocabulary between developers and domain experts that is reflected in code and documentation.
Cohesion & coupling – high cohesion (single responsibility) and low coupling (minimal dependencies) are essential for maintainable services.
REST/RESTful – architectural style using stateless HTTP verbs (GET, POST, PUT, DELETE) and resource‑oriented URLs; enables language‑agnostic interaction.
Microservice testing types – unit tests, contract tests, integration tests, end‑to‑end tests, and chaos/ resilience tests.
Container Technology
DevOps need – bridges development and operations to deliver software rapidly, with automation, continuous integration, and continuous delivery.
Docker – platform that packages applications and their dependencies into lightweight, portable containers using OS‑level isolation.
Docker advantages – fast startup, consistent environments, resource efficiency, easy scaling.
CI purpose – automatically build, test, and package code changes to ensure quality before deployment.
Environment‑independent containers – write a Dockerfile that installs all dependencies; the resulting image runs identically on any host with Docker.
COPY vs. ADD – COPY copies files/directories from the build context; ADD also supports remote URLs and automatic extraction of tar archives.
Docker image vs. container – an image is a read‑only layered filesystem; a container is a runnable instance of an image with its own writable layer.
Docker Hub – public registry for sharing and retrieving Docker images.
Container runtime states – created, running, paused, exited, and dead.
Determine container status – docker ps -a shows the Status column; docker inspect provides detailed state information.
Common Dockerfile instructions – FROM, RUN, COPY, ADD, ENV, EXPOSE, CMD, ENTRYPOINT, WORKDIR, ONBUILD.
Stateless vs. stateful apps – stateless services are ideal for containers; stateful services require persistent storage (volumes, bind mounts, or external databases).
Basic Docker workflow – write Dockerfile → docker build → docker run → push to registry → deploy via orchestrator.
Image vs. layer – each instruction creates a new read‑only layer; layers are shared between images to save space.
Virtualization vs. containerization – VMs emulate hardware and run a full OS; containers share the host kernel, offering lower overhead.
ONBUILD instruction – defers execution of a command until the image is used as a base for another build, useful for building reusable base images.
Running Docker on Windows – Docker Desktop provides a Linux VM (WSL2) for native Linux containers; Windows containers run with a Windows kernel.
Containerization low‑level – uses Linux namespaces (pid, net, mount, uts, ipc) and cgroups for isolation and resource limiting.
Paravirtualization – technique where the guest OS is aware of the hypervisor, reducing overhead; Docker does not use paravirtualization.
Docker Swarm – native clustering and orchestration tool that manages a pool of Docker engines as a single virtual host.
Monitoring containers – tools like docker stats, Prometheus + cAdvisor, or third‑party platforms (Datadog, Grafana).
Container image handling – pull from registry, tag, push, prune unused images with docker image prune.
Docker Compose start order – services are started in dependency order defined by depends_on, but Docker does not wait for health checks unless healthcheck is used.
Redis
What is Redis – an in‑memory key‑value store supporting strings, hashes, lists, sets, sorted sets, streams, and more.
Data types – string, list, set, sorted set, hash, bitmap, hyperloglog, geospatial index, stream.
Benefits – sub‑millisecond latency, built‑in replication, persistence options, pub/sub, Lua scripting.
Redis vs. Memcached – Redis offers richer data structures, persistence, replication, and clustering; Memcached is simpler, pure cache.
Single‑process, single‑threaded – the core server runs a single thread handling all commands; I/O is multiplexed via epoll/kqueue.
Maximum string size – up to 512 MB per string value.
Persistence mechanisms – RDB snapshots (periodic) and AOF (append‑only file) logging; RDB is compact, AOF provides better durability.
Key expiration – set with EXPIRE or SET key value EX seconds; Redis uses a lazy and periodic active expiration algorithm.
Eviction policies – noeviction, allkeys-lru, allkeys-lfu, volatile-lru, volatile-ttl, etc., chosen via maxmemory-policy.
Why all data in memory – to achieve constant‑time operations; optional swapping to disk via virtual memory is not recommended for performance.
Synchronization – single thread guarantees atomicity of each command; replication is asynchronous by default.
Pipelines – batch multiple commands in a single round‑trip to reduce latency.
Cluster principles – data sharded across 16384 hash slots; each node owns a subset of slots and replicates to a slave.
Cluster unavailability – occurs when a majority of master nodes for a slot are down (loss of quorum).
Java clients – Jedis, Lettuce, Redisson; Redisson is recommended for advanced features like distributed locks.
Password authentication – configure requirepass and use AUTH password before other commands.
Hash slots – CRC16(key) mod 16384 determines the slot; clients route commands to the correct node.
Master‑slave replication – asynchronous replication; slaves receive a stream of write commands from the master.
Write loss possibility – if a master fails before its writes are replicated to a slave, those writes can be lost.
Replication flow – master sends a SYNC command; slaves perform a full sync then receive incremental updates.
Maximum nodes – limited by the number of hash slots and practical network constraints; typical clusters have up to a few hundred nodes.
Selecting a database – Redis supports 16 logical databases (0‑15) selectable with SELECT n; clusters typically use database 0.
Testing connectivity – redis-cli ping should return PONG.
Transactions – MULTI, EXEC, DISCARD, WATCH for optimistic locking.
Setting expiration vs. permanence – EXPIRE / PEXPIRE for temporary keys; omit expiration for permanent data.
Memory optimization – use appropriate data structures, enable maxmemory, configure eviction, use hash-max-ziplist-entries etc.
Eviction process – when memory limit is reached, Redis evicts keys according to the selected policy.
Reducing memory usage – use HASH with ziplist encoding, set hash-max-ziplist-value, avoid large objects, enable lazyfree‑lru‑clock‑skip.
Out‑of‑memory behavior – Redis returns an error to the client; if eviction is enabled, it removes keys according to policy.
Maximum keys per instance – limited by memory; theoretically up to 2^32‑1 keys, but practical limits depend on available RAM.
Hot data strategy – keep frequently accessed keys in memory, use LRU/LFU eviction, or tiered caching with Redis as the hot layer.
Finding keys with common prefix – use SCAN with a match pattern (e.g., SCAN 0 MATCH prefix*) to avoid blocking.
Bulk expiration considerations – setting expiration on millions of keys at once can cause spikes; spread expirations or use lazy expiration.
Redis as async queue – use LPUSH / RPOP or BRPOP for reliable FIFO queues.
Distributed lock – implement with SET key value NX PX ttl (Redlock algorithm) to acquire a lock with expiration.
MySQL
Three normal forms – 1NF (atomic columns), 2NF (no partial dependency on a subset of a candidate key), 3NF (no transitive dependency).
Permission tables – mysql.user, mysql.db, mysql.tables_priv, etc.
Binlog formats – STATEMENT, ROW, and MIXED; statement logs SQL, row logs actual row changes, mixed uses statement when safe, otherwise row.
MyISAM vs. InnoDB – MyISAM: table‑level locking, no transactions, faster reads; InnoDB: row‑level locking, ACID transactions, crash recovery, foreign keys.
Index differences – MyISAM indexes are separate files; InnoDB indexes are clustered with the primary key.
Index definition – data structure (B‑tree) that speeds lookup; created with CREATE INDEX or ALTER TABLE ... ADD INDEX.
Index pros/cons – pros: faster reads, can enforce uniqueness; cons: extra storage, slower writes, maintenance overhead.
Index types – B‑tree (default), FULLTEXT, SPATIAL, HASH (memory engine).
Lock types – table locks, row locks, metadata locks; InnoDB provides shared (read) and exclusive (write) row locks.
Transaction isolation levels – READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ (default), SERIALIZABLE; each defines visibility of other transactions' changes.
CHAR vs. VARCHAR – CHAR fixed length, padded with spaces; VARCHAR variable length with length prefix, more space‑efficient.
Primary vs. candidate keys – primary key uniquely identifies a row and cannot be NULL; candidate keys are alternate unique keys.
UNIX ↔ MySQL timestamp conversion – FROM_UNIXTIME(seconds) and UNIX_TIMESTAMP(datetime).
MyISAM file locations – .frm (table definition), .MYD (data), .MYI (index).
Monetary field type – DECIMAL(precision, scale) to avoid floating‑point rounding errors.
Index creation considerations – choose columns with high selectivity, avoid indexing low‑cardinality columns, consider covering indexes.
Index impact on performance – not always beneficial; for very small tables or queries that retrieve many rows, full table scan may be faster.
Efficient bulk delete – delete in batches (e.g., DELETE FROM tbl WHERE id < 1000 LIMIT 1000) or use TRUNCATE if whole table can be removed.
Leftmost prefix principle – MySQL can use an index only on the leftmost contiguous columns of a multi‑column index.
Clustered vs. non‑clustered index – InnoDB’s primary key is clustered (data stored with the index); secondary indexes are non‑clustered and store the primary key as a pointer.
MySQL connector – client libraries (e.g., Connector/J, Connector/ODBC) that enable applications to communicate with MySQL.
Query cache – stores result sets of SELECT statements; deprecated in MySQL 8.0 due to scalability issues.
Parser, optimizer, executor – parser builds an AST, optimizer creates an execution plan, executor runs the plan.
Temporary tables – created with CREATE TEMPORARY TABLE; automatically dropped at session end or can be dropped manually.
External vs. internal links – external links refer to tables in other databases; internal links are foreign keys within the same database.
UNION vs. UNION ALL – UNION removes duplicates (extra sorting), UNION ALL concatenates results directly, faster when duplicates are not a concern.
MyISAM characteristics – non‑transactional, table‑level locking, fast read‑only workloads.
InnoDB characteristics – transactional, row‑level locking, crash‑safe, supports foreign keys, MVCC for consistent reads.
Go Development Architecture Practice
Daily sharing of Golang-related technical articles, practical resources, language news, tutorials, real-world projects, and more. Looking forward to growing together. Let's go!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
