Interview guides · Deuna
Deuna Software Engineer Interview Guide
Deuna Software Engineer interview guide: reported Golang, coding, system design and behavioral questions across 4 rounds, with a 7-day prep plan.
The 60-second brief
Candidate reports describe Deuna's Software Engineer role in general terms: designing, building and maintaining software for the company's platform, adding features, and tuning existing functionality so applications keep up with user demand. The requirements candidates describe name Golang, RESTful APIs and cloud services (AWS, GCP or Azure), with JavaScript, React and DevOps practice listed as extras. Day to day, the role is described as working with product managers and designers, taking part in code reviews, and covering the lifecycle from requirements to deployment. Reports say little about the product itself, so this guide prepares you for the engineering skills the loop covers and does not guess at business details.
This guide is for engineers with a couple of years of professional experience who are preparing for the Deuna Software Engineer loop. It covers the four reported rounds, the 23 reported questions across Go knowledge, system design, behavioral, problem-solving and coding, the losing points on those question types, and a seven-day plan that sequences them.
Candidates report four rounds spread over roughly three to five weeks: HR Screening, Technical Assessments, a System Design Interview and Final Interviews. The reported questions fall into five groups: Go knowledge, design, behavioral, problem-solving and coding.
Focus areas
- Golang (Go)
- Live Coding
- System Design
Overview
The reported questions show where to spend your time. Five are about Go itself: its key features, dependency management, goroutines compared with threads, error handling and unit testing. Four are short coding tasks: reverse a string in Golang, implement binary search, check for a palindrome, and sort an array of integers. Five are design and architecture questions: a URL shortening service, distributed-system considerations, the CAP theorem, a real-time messaging application and useful design patterns. Four are problem-solving cases: debugging a slow web application, estimating time and resources, a data-structures challenge, and an unexpected technical problem. Five are behavioral. That balance suggests Go fluency and clear spoken reasoning matter at least as much as raw algorithm speed.
The requirements candidates describe put Golang first, then RESTful APIs and cloud services, with front-end work, DevOps practice and CI/CD as extras. Prepare to answer the Go questions with the cost as well as the feature. Goroutines are cheap, and you should also be able to say what shared memory and unbuffered channels do to correctness. Error values are explicit, and you should be able to say how you wrap and inspect them. A candidate who can write a table-driven test, explain why a module needs go.sum, and run the race detector will sound more credible than one who recites a feature list.
The design questions are classic and broad, so structure matters more than any single technology. Each one needs a repeatable frame: requirements and scale, API, data model, the read and write paths, the failure modes, and the trade-offs you accepted. Practise that frame on the URL shortener and the messaging application, then reuse it for the distributed-systems and CAP questions, where the answer is a clear statement of what your system does during a partition.
Behavioral preparation deserves real time. Candidates describe the final interviews as possibly covering collaboration and problem-solving, and the five reported behavioral prompts cover challenges, deadlines, influence, motivation and conflict. Build a small set of stories from your own backend work, each with a decision you made, the alternatives you rejected and a result you can verify.
The Deuna interview process
Candidates describe four stages, from an HR screen through technical, design and concluding interviews, and say individual loops can vary by team. Treat the order below as reported, not guaranteed.
HR Screening
Candidates describe this first stage as a screen of your background and cultural fit. No technical questions are reported for it, so treat it as the place to connect your history to what the role asks: Go, RESTful APIs, cloud services, and two or more years of professional work. The behavioral prompts candidates report are also the natural material for the fit conversation.
What to demonstrate
- How your background matches the requirements candidates describe for the role
- How clearly you explain your experience and your reasons for applying
- Whether your answers on teamwork and motivation are concrete
How to prepare
- Write a spoken walk through your CV that names where you used Go, REST APIs and a cloud provider, with one outcome for each
- Prepare short answers to "What motivates you as a software engineer?" and "How do you handle conflicts within a team?"
- Ask the recruiter which stages follow and whether the technical stage is a live session or a take-home, then write down the answer
Technical Assessments
Candidates describe this stage as an evaluation of technical skills through coding challenges or tests, and reports mention live coding on algorithms and data structures. The coding questions reported for this guide are small: reversing a string in Golang, implementing binary search, checking for a palindrome and sorting an array of integers. Expect to write working code, so practise typing complete solutions in Go and running them yourself.
What to demonstrate
- Working code for small algorithm and data-structure problems
- Fluency in Go, the language the requirements list first
- How you explain your approach and complexity while you code
How to prepare
- Write the string reversal over runes rather than bytes, then the palindrome check, and run both against empty, single-character and non-ASCII input
- Implement binary search with an iterative loop and the lo + (hi-lo)/2 midpoint, then test duplicates, a missing target and a one-element slice
- Solve a sorting task with sort.Ints or slices.Sort, then be ready to write a simple version by hand and state its complexity
- Rehearse the reported Go questions on goroutines versus threads, error handling and table-driven tests, since a technical stage can include them
System Design Interview
This is the system design and architecture stage. The design questions candidates report are a URL shortening service, general distributed-system considerations, the CAP theorem and its implications, a real-time messaging application, and useful design patterns. They differ in scope: two are concrete systems, two are conceptual. Practise both: a full design with numbers, and a short principled explanation.
What to demonstrate
- Ability to design a system from requirements through components and trade-offs
- Understanding of architectural ideas such as consistency, availability and partition tolerance
- Ability to explain design choices clearly while drawing the system
How to prepare
- Write a one-page URL shortener design covering ID generation, storage, redirect path, caching and expiry, plus what breaks first under load
- Prepare a CAP answer built on a concrete case: what your messaging service does for reads and writes during a network partition
- Sketch the real-time messaging application with connections, delivery, ordering and offline users, and note which choices you would revisit at larger scale
- List four design patterns you have used in services, such as a repository, a circuit breaker, a worker pool and an outbox, each with a problem it solved
Final Interviews
Candidates describe the final interviews as concluding conversations that may include collaboration and problem-solving. Reports do not say which questions arrive here, so bring both your behavioral stories and your reasoning for technical cases such as the slow web application and the unexpected technical challenge. Prepare to speak about decisions you owned rather than events you observed.
What to demonstrate
- How you describe working with teammates, product managers and designers
- How you reason through a problem you have not seen before
- Whether your examples show your own decisions and results
How to prepare
- Write five stories matching the reported prompts on challenge, deadlines, influence, motivation and conflict, each with your decision and a result
- Rehearse the debugging sequence for a slow web application: reproduce, measure, find the bottleneck, change one thing, re-measure
- Prepare one story about code review feedback you gave and one you received, since reviews are part of the described role
- Have two thoughtful questions ready about how the team ships, tests and reviews code
Where candidates lose points
Answering the Go questions with a list of features and no cost, such as saying goroutines are lightweight without saying what that means for stacks, scheduling or shared-state bugs
Pair every Go claim with a trade-off or a failure case: goroutines against OS threads, channels against mutexes, wrapped errors against sentinel errors. Practise saying one sentence of cost per feature.
Writing the string reversal or palindrome check over bytes, or leaving out empty input, mixed case, spaces and non-ASCII text, then submitting binary search with an off-by-one loop bound
Convert to a []rune for reversal, say what the palindrome check should ignore before you code, and test empty, one-element, duplicate and missing-target inputs by hand before you say the solution is done.
Jumping to boxes and databases in the URL shortener or the messaging application without asking about scale, read and write ratio, expiry, or what happens when a node fails
Open every design with requirements and rough numbers, then draw the read path and write path separately, and close with the bottleneck and the failure case. Rehearse this frame on all five reported design questions.
Giving the CAP theorem as 'pick two of three' without saying that partitions force a choice between consistency and availability, or naming no real system behaviour
State that partition tolerance is not optional in a distributed system, then give one example of what your design does during a partition: reject writes, or accept them and reconcile later. Be ready to place a messaging service on that spectrum.
Telling behavioral stories in the plural, where 'we decided' and 'we shipped' hide what you personally did, or answering the motivation question with generalities
For each story write down the decision that was yours, the alternative you rejected and the result you verified. Say 'I' where it applies, and give team credit separately.
Questions & practice
Can you explain the difference between goroutines and threads?
Medium · Technical / Domain Questions
Approach
- Goroutines are user-space tasks scheduled by the Go runtime onto OS threads (M:N, the G-M-P model). GOMAXPROCS sets how many Ps, and so how many threads, run Go code at once.
- Cost: a goroutine starts with a stack of a few kilobytes that grows by copying, while an OS thread usually reserves a fixed stack of megabytes. That is why tens of thousands of goroutines are practical.
- Scheduling: switching goroutines happens in user space and is cheap. Network I/O parks on the netpoller; a blocking syscall makes the runtime hand the P to another thread; preemption has been asynchronous since Go 1.14.
- No goroutine IDs or thread-local storage: pass state explicitly, share it through channels or sync primitives, and cancel work with context.
- Shared memory still races: run tests with -race, and watch for leaks such as a goroutine blocked forever on a channel nobody reads. Use runtime.LockOSThread only for thread-affine C libraries.
Follow-up
- What happens to the other goroutines when one makes a blocking system call?
- How would you detect and fix a goroutine leak in a running service?
- What does GOMAXPROCS control, and what should it be inside a CPU-limited container?
Write a function to reverse a string in Golang.
Medium · Coding / Algorithms
Approach
- Point out that Go strings are immutable UTF-8 bytes, so reversing bytes corrupts multi-byte characters. Convert to []rune and reverse that.
- Two pointers: r := []rune(s); for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] }; return string(r). O(n) time and O(n) extra space.
- If the input is guaranteed ASCII, reversing a []byte is enough and cheaper. Say that trade-off.
- Edge cases: empty string, one character, invalid UTF-8 (converting to []rune turns bad bytes into U+FFFD), and combining marks or emoji sequences, which rune reversal splits. Fixing those needs grapheme-cluster segmentation.
- Write a table-driven test with "", "a", "héllo" and a multi-rune emoji.
Follow-up
- Why does len("héllo") return 6?
- How would you reverse the order of words in a sentence?
- Can you reverse a []byte in place with no allocation?
How would you implement a binary search algorithm?
Medium · Coding / Algorithms
Approach
- Precondition: the data is sorted, or more generally the predicate is monotonic. Iterative version: lo, hi := 0, len(a)-1; for lo <= hi { mid := lo + (hi-lo)/2; compare; move lo or hi }. O(log n) time, O(1) space.
- Compute mid as lo + (hi-lo)/2 to avoid overflow. It matters with 32-bit ints and costs nothing elsewhere.
- Lower-bound variant on a half-open range [lo, hi): for lo < hi, set hi = mid when a[mid] >= target, else lo = mid+1. It returns the first index not less than the target, which handles duplicates and first/last occurrence.
- Mention the standard library: sort.Search(n, f) returns the smallest i in [0, n) where f(i) is true (n if none), and slices.BinarySearch (Go 1.21+) returns (index, found).
- Edge cases: empty slice, target below or above every element, one element, all duplicates, and an infinite loop if lo = mid is ever possible.
Follow-up
- Search for a target in a rotated sorted array.
- Find the first and last position of a target among duplicates.
- Binary search on the answer: the minimum capacity to ship packages within D days.
Can you create a function to check if a string is a palindrome?
Medium · Coding / Algorithms
Approach
- Clarify the rules first: case-sensitive or not, ignore punctuation and spaces or not, ASCII or Unicode. "A man, a plan, a canal: Panama" is the usual test.
- Two pointers from both ends moving inward. Skip characters that are not letters or digits (unicode.IsLetter, unicode.IsDigit) and compare with unicode.ToLower.
- Complexity: O(n) time. O(1) extra space on bytes for ASCII input; O(n) if you convert to []rune for Unicode.
- Edge cases: empty string and one character are palindromes, a string of only punctuation is one too, and both even and odd lengths must work.
- Know the follow-up techniques: allowing one deletion (on the first mismatch try skipping either side, still O(n)), and the longest palindromic substring by expanding around centers in O(n^2).
Follow-up
- Return true if the string can become a palindrome after deleting at most one character.
- Find the longest palindromic substring.
- Check whether a singly linked list is a palindrome in O(1) extra space.
Solve a problem involving sorting an array of integers.
Medium · Coding / Algorithms
Approach
- Ask what the sort serves: plain ordering, or a problem built on it (merge intervals, kth largest, removing duplicates, two pointers on sorted data).
- In Go, slices.Sort or sort.Ints (pattern-defeating quicksort since Go 1.19): O(n log n), not stable. Use sort.SliceStable or slices.SortStableFunc when equal elements must keep their order, and cmp.Compare instead of a-b in comparators to avoid overflow.
- Be able to write one yourself and compare: merge sort O(n log n), stable, O(n) extra; quicksort average O(n log n), worst O(n^2) with bad pivots, in place; heapsort O(n log n) worst case, O(1) extra, unstable.
- Special cases: insertion sort for tiny or nearly sorted input, counting sort O(n + k) when the value range k is small (shift by the minimum for negatives), radix sort for fixed-width integers.
- For kth largest, quickselect is O(n) average and a size-k min-heap is O(n log k). Test with empty, duplicates, negatives and already sorted input.
Follow-up
- Sort an array of 0s, 1s and 2s in one pass.
- How would you sort integers that do not fit in memory?
- Why does stability matter when sorting records by a second key?
Replace offset paging on the resource feed with keyset
Easy · keyset pagination · composite index · sort order
resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.
Approach
- Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
- Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
- Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
- Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.
Follow-up
- The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
- Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?
Find version gaps and relay lag with window functions
Medium · window functions · event tables · frame semantics
outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.
Approach
- Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
- Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
- Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
- Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.
Follow-up
- Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
- Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?
What are the key features and benefits of Golang?
Medium · Technical / Domain Questions
Approach
- Lead with deployment: static typing, fast compiles and a single binary (statically linked by default for pure-Go code; cgo can add dynamic links), which makes container images small and simple.
- Cover concurrency: goroutines, channels and select, multiplexed onto OS threads by the runtime scheduler, with context.Context for cancellation and deadlines.
- Explain the type system: composition through struct embedding, interfaces satisfied implicitly (so the consumer defines the small interface it needs), and generics since Go 1.18.
- Name the tooling a team actually uses: gofmt, go vet, go test with -race, pprof, and modules. The standard library (net/http, encoding/json) is enough for most REST services.
- Close with the costs: verbose error checks, a garbage collector you cannot tune finely, and fewer abstractions than languages like Java or Rust. Say when you would pick something else.
Follow-up
- When would you not choose Go for a service?
- Why can an interface value holding a nil pointer compare unequal to nil?
- How does implicit interface satisfaction change how you lay out packages?
How do you manage dependencies in a Golang project?
Medium · Technical / Domain Questions
Approach
- Go modules: go.mod declares the module path, the go directive, require, replace and exclude. go.sum records hashes of module content and go.mod files, checked against the checksum database (GOSUMDB) on first download.
- Explain minimal version selection: for each module the build uses the highest of the minimum versions any requirement asks for, never the newest release, so builds are reproducible without a separate lock file.
- Daily commands: go get module@version, go mod tidy, go list -m all, go mod why, and govulncheck for known vulnerabilities. Major versions v2+ change the import path (/v2 suffix).
- Private and local code: GOPRIVATE (and GOPROXY/GONOSUMDB) for internal repos, replace directives or go.work workspaces for multi-module development, vendoring when builds must work offline.
- In CI: run go mod verify, fail the build if go mod tidy produces a diff, and let a bot (Dependabot or Renovate) open upgrade PRs that the test suite gates.
Follow-up
- Two dependencies need different minor versions of the same module; which one gets built and why?
- How do you move a codebase to a v2 major version of a library?
- How do you fetch a private repository in CI without leaking credentials?
Describe how you would handle error management in Go.
Medium · Technical / Domain Questions
Approach
- Errors are values: return error as the last result and check it at the call site. Keep panic for programmer bugs or impossible states, and recover only at boundaries such as HTTP middleware or a worker goroutine.
- Add context as the error travels up: fmt.Errorf("load order %s: %w", id, err). Inspect with errors.Is for sentinels (io.EOF, sql.ErrNoRows) and errors.As for typed errors. Never compare error strings.
- Handle each error once: either log it or return it, not both, so one failure does not produce five log lines. errors.Join (Go 1.20) combines several errors.
- Map domain errors to HTTP status codes in one place, keep internal detail out of responses, and log with a request ID.
- Edge cases: a function returning a typed nil pointer as error gives a non-nil interface; errors from deferred Close calls are easy to drop; for concurrent work, errgroup returns the first error and cancels the rest.
Follow-up
- When is panic the right choice in Go code?
- Why does returning a nil *MyError from a func that returns error make err != nil true?
- How do you collect errors from many goroutines without losing any?
What is your approach to writing unit tests in Golang?
Medium · Technical / Domain Questions
Approach
- Default to table-driven tests: a slice of cases with name, input and expected output, each run with t.Run so failures name the case. Use t.Helper in assertion helpers and t.Cleanup for teardown.
- Put dependencies behind small interfaces defined by the consumer and pass hand-written fakes. Test HTTP handlers with httptest.NewRecorder and clients with httptest.NewServer.
- Run go test -race and -coverprofile in CI. Add fuzz tests (func FuzzX(f *testing.F), Go 1.18+) for parsers and benchmarks (func BenchmarkX(b *testing.B)) for hot paths.
- Keep slow integration tests separate with a build tag or testing.Short(), and run them against a real database in a container instead of mocking SQL.
- Remove flakiness: inject a clock instead of calling time.Now, synchronise goroutines with channels or WaitGroups instead of sleeps, and know that Go 1.22 made loop variables per-iteration, which removed the old capture bug in parallel subtests.
Follow-up
- How would you test code that calls an external HTTP API?
- What does a high coverage number fail to tell you?
- How do you keep a suite using t.Parallel free of shared-state failures?
How would you design a URL shortening service?
Medium · System Design / Architecture
Approach
- Scope it: create a short link, redirect, optional custom alias and expiry, click analytics. It is read-heavy, so size the redirect path first. Seven base62 characters give 62^7, about 3.5 trillion codes.
- Code generation: a unique counter encoded in base62 (each server leases an ID range from a central allocator) never collides but is guessable; a truncated hash or random code needs a uniqueness check via a conditional insert and retry.
- Storage: a key-value mapping code -> long URL with the code as primary key, sharded by code hash. Lookups are point reads, so any KV store or a relational table with a unique index works.
- Redirect path: cache hot codes in Redis and at a CDN. A 301 lets browsers cache the redirect and cuts load but hides repeat clicks; a 302 keeps every click visible for analytics.
- Write click events asynchronously to a queue, rate-limit link creation, scan targets for malicious URLs, and expire links with a TTL plus lazy checks on read.
Follow-up
- How do you make sure two app servers never issue the same code?
- Should the same long URL submitted twice get the same short code?
- One link goes viral; what breaks first and how do you protect it?
What considerations would you take into account when designing a distributed system?
Medium · System Design / Architecture
Approach
- Start from partial failure: networks drop, nodes pause, and timeouts are ambiguous. Every remote call needs a timeout, retries with exponential backoff and jitter, and a circuit breaker.
- Make retries safe: idempotency keys or natural keys so a duplicate request does not double-apply. Delivery is realistically at-least-once plus deduplication, not exactly-once.
- Choose consistency per piece of data: strong where a wrong read costs money or uniqueness, eventual for feeds and counters. Replication choices follow (leader-follower, quorums with R + W > N).
- Partitioning: pick a shard key that spreads load and keeps common queries on one shard, plan for hot keys, and use consistent hashing or range splits for rebalancing.
- Avoid distributed transactions: use the transactional outbox for DB-plus-event writes and sagas with compensations. Don't order events by wall clocks; use versions or sequence numbers. Add tracing, latency percentiles and structured logs.
Follow-up
- How do you update a database row and publish an event without one of them getting lost?
- How would you make a retry safe for an operation that is not naturally idempotent?
- What goes wrong if you order events by server timestamps?
Can you explain the CAP theorem and its implications?
Medium · System Design / Architecture
Approach
- State it precisely: when a network partition happens, a replicated system must give up either consistency (linearizable reads) or availability (every non-failed node answers without error). Without a partition it can have both.
- CP examples: consensus-based stores such as etcd or ZooKeeper refuse writes on the minority side. AP examples: Dynamo-style stores such as Cassandra at low consistency levels accept writes on both sides and reconcile later.
- Reconciliation in AP systems: last-write-wins (loses updates), vector clocks or CRDTs. Say which data can tolerate that.
- Extend with PACELC: even without a partition you trade latency against consistency, which is the choice you make most days.
- Implication for design: decide per operation, not per system. Uniqueness, balances and inventory need the CP side; likes, view counts and presence can be AP.
Follow-up
- Is a single-node Postgres database CA, and does the label mean anything?
- Does R + W > N guarantee linearizability?
- Which side would you choose for a shopping cart versus an account balance, and why?
Describe how you would approach designing a real-time messaging application.
Medium · System Design / Architecture
Approach
- Scope: one-to-one and group chat, online delivery, offline storage, per-conversation ordering, read receipts and presence. Give a rough user and message rate before choosing components.
- Connections: clients hold WebSockets to gateway servers. A registry such as Redis maps user to gateway, kept fresh by heartbeats.
- Send path: the client sends a message with a client-generated ID; the chat service assigns a per-conversation sequence number, persists it, acks the sender, then fans out through pub/sub to the recipients' gateways.
- Storage and offline: partition by conversation ID, ordered by sequence (a wide-column store fits). On reconnect the client asks for everything after its last seen sequence; offline devices get push notifications.
- Trade-offs: write-time fan-out suits small groups, read-time suits very large ones. Delivery is at-least-once, and clients drop duplicates by message ID.
Follow-up
- How do you keep ordering correct when several members send at once?
- A gateway server dies; what happens to its connections and undelivered messages?
- How do you sync read state across a user's phone and laptop?
What are some design patterns you find useful in system architecture?
Medium · System Design / Architecture
Approach
- Pick four or five patterns and give each one's problem and cost: cache-aside (fast reads, stale data and invalidation), circuit breaker with retry and backoff (stops cascades, needs tuned thresholds), transactional outbox (no dual writes, needs a relay).
- Architecture-level: API gateway or backend-for-frontend, CQRS (separate read model, eventual consistency between models), saga with compensating actions, strangler fig for incremental migration, bulkhead to isolate resource pools.
- Show you can apply them in Go: functional options for constructors, middleware wrapping http.Handler (decorator), small interfaces for dependency injection, worker pools and fan-out/fan-in pipelines with channels.
- Anchor one pattern in a real story: what I introduced, what it fixed, and what it cost later.
- Name an anti-pattern: microservices sharing one database (a distributed monolith), or event sourcing adopted without a need for replay.
Follow-up
- When did a pattern add more complexity than it removed?
- How does the outbox pattern avoid the dual-write problem?
- What are a circuit breaker's states and what moves it between them?
Given a set of requirements, how would you estimate the time and resources needed for development?
Medium · Problem-Solving / Case Studies
Approach
- Break the requirements into tasks small enough to size with confidence, and include the work people forget: tests, code review, migrations, CI/CD changes, observability, documentation and rollout.
- Estimate in ranges, not single numbers: a three-point estimate ((optimistic + 4 x likely + pessimistic) / 6), or compare against similar past work. Use story points and velocity if the team already does.
- Find the unknowns and time-box a spike on the riskiest one before committing. Map dependencies and the critical path.
- Turn effort into calendar time using real capacity (meetings, on-call, reviews), add an explicit risk buffer, and state your confidence.
- Re-estimate as you learn, and when the date is fixed, negotiate scope: say which features move to a later release.
Follow-up
- You are halfway through and behind schedule; what do you do?
- How do you estimate work nobody on the team has done before?
- How do you push back when the deadline is already set?
Can you solve a coding challenge related to data structures?
Medium · Problem-Solving / Case Studies
Approach
- Clarify input size and constraints, state a brute-force solution and its complexity, then choose the structure that makes the dominant operation cheap.
- Know the defaults: hash map for O(1) average lookups and counts; stack for matching brackets and monotonic-stack problems; queue for BFS; heap for top-k in O(n log k); a balanced tree or sorted slice when order matters.
- In Go: map[K]V (iteration order is randomized, and writing to a nil map panics), slices as stacks (append, then s = s[:len(s)-1]), container/heap (implement Len, Less, Swap, Push, Pop), container/list for a doubly linked list.
- Classic composite: an LRU cache is a map from key to list node plus a doubly linked list, giving O(1) get and put.
- Test out loud on edge cases: empty input, one element, duplicates, negative numbers, and the largest input the constraints allow.
Follow-up
- Implement an LRU cache with O(1) get and put.
- What is the complexity of top-k frequent elements with a heap versus bucket sort?
- How would you make this structure safe for concurrent use: sync.Mutex, sync.RWMutex or sync.Map?
Describe your thought process when faced with an unexpected technical challenge.
Medium · Problem-Solving / Case Studies
Approach
- Contain impact first if users are affected: roll back, turn off a feature flag, or shed load before trying to understand anything.
- Gather facts before theories: what changed, logs, metrics, traces, and a minimal reproduction. Write down what is known and what is assumed.
- Rank hypotheses by likelihood and cost to test, then bisect: git bisect, toggling config, or comparing a working and a failing environment.
- Time-box the investigation, say early when you need someone who knows the system, and keep stakeholders updated on status and next check.
- Close the loop: fix the root cause, add a test or alert, and write a short blameless review. Tell one real example in first person ("I rolled back first, then...").
Follow-up
- How do you decide between rolling back and fixing forward?
- When do you stop investigating alone and ask for help?
- What did you change in the system or process after that incident?
How would you approach debugging a performance issue in a web application?
Medium · Problem-Solving / Case Studies
Approach
- Define the symptom first: which endpoint or page, which percentile (p50 versus p99), since when, and for whom. Line that up against deploys, traffic and data growth.
- Split where the time goes: browser (DevTools waterfall, bundle size, Core Web Vitals), network, or server. A distributed trace shows which span dominates.
- On the server: profile with pprof (CPU, heap, goroutine, block, mutex), then check the database with slow-query logs and EXPLAIN ANALYZE for missing indexes, N+1 queries, lock waits and connection-pool exhaustion. Check outbound calls without timeouts.
- Reproduce under load, change one variable at a time, and compare the same percentile before and after on comparable traffic.
- Typical fixes: add an index, batch queries, paginate, cache, move work off the request path. Then add an alert or a benchmark so the regression is caught next time.
Follow-up
- p50 is fine but p99 is bad; what kinds of causes do you look for?
- CPU is low but latency is high; where do you look next?
- How do you prove the fix worked and was not a traffic dip?
Suggested practice checklist
The plan follows the four reported rounds and the five question groups, starting with Go fundamentals and coding, moving to design and problem-solving, and ending with behavioral stories and a full mock run.
Day 1 — Map the loop and refresh Go fundamentals
- Write the four reported rounds on one page, with the question groups that can be practised for each stage, and mark your weakest one.
- Write a half-page answer on the key features and benefits of Go, pairing each feature (static typing, compiled binaries, goroutines, garbage collection, fast builds) with one cost.
- Create a throwaway Go module and run go mod init, go get, go mod tidy and go mod vendor, then explain what go.mod and go.sum each record.
Deliverable: A one-page loop map, a half-page Go features answer with costs, and a written note on how a Go module records and pins its dependencies.
Day 2 — Concurrency, errors and tests in Go
- Write a short comparison of goroutines and OS threads covering stack size, scheduling by the Go runtime, and what still needs a mutex or channel.
- Write a function that returns errors wrapped with fmt.Errorf and %w, then handle them with errors.Is and errors.As in the caller.
- Write a table-driven test with t.Run for one function and one HTTP handler test using httptest, then run go test -race.
Deliverable: A goroutine-versus-thread answer, a small error-handling example with Is and As, and a passing table-driven and handler test suite run with the race detector.
Day 3 — Live coding in Go
- Implement string reversal over runes and a palindrome check, and run both on empty, single-character, mixed-case and non-ASCII input.
- Implement iterative binary search with a safe midpoint and test duplicates, a missing target and a single-element slice.
- Solve one sorting task with the standard library, then write a hand-made merge sort and state both complexities, and do one more data-structures problem of your choice.
Deliverable: Four working Go solutions with test cases, plus a written complexity note for each, produced by typing and running the code yourself.
Day 4 — Design the URL shortener and answer CAP
- Design a URL shortening service on one page: requirements, short-code generation, storage, redirect path, caching and expiry.
- Name the bottleneck at ten times the traffic and the failure case when a node is down, and write your fix for each.
- Write a CAP answer around a concrete case, stating what the service does during a network partition and which side you choose.
Deliverable: A one-page URL shortener design with a bottleneck and failure note, and a written CAP answer tied to a specific system.
Day 5 — Distributed systems, messaging and patterns
- Write a checklist of distributed-system considerations (partitioning, replication, retries and idempotency, timeouts, ordering, observability) with one sentence of use for each.
- Sketch a real-time messaging application covering connection handling, message delivery, ordering and offline users.
- List four design patterns you have used in services and, for each, name the problem it solved and when it would be the wrong choice.
Deliverable: A distributed-systems checklist, a messaging architecture sketch with its main trade-offs, and a four-pattern list with a use and a misuse for each.
Day 6 — Debugging, estimating and unfamiliar problems
- Write the debugging sequence for a slow web application: reproduce, measure with profiling or tracing, find the bottleneck, change one thing, and re-measure.
- Break a feature you have built into tasks and give each an estimate with its main risk, then add the assumptions you would confirm first.
- Pick a technical problem you had not met before and write how you approached it, from clarifying the goal to the first experiment.
Deliverable: A written debugging checklist, a task-level estimate with assumptions for a real feature, and a short account of how you handle an unfamiliar technical problem.
Day 7 — Behavioral stories and a full mock run
- Write five stories matching the reported prompts on challenge, deadlines, influence, motivation and conflict, each with your decision, the rejected option and the result.
- Rehearse a spoken CV walk through that connects your work to Go, REST APIs and cloud services.
- Run a mock loop aloud: one behavioral story, one coding problem in Go, one design question, then review where your explanation lost clarity.
Deliverable: Five written behavioral stories, a rehearsed CV walk through, and a list of the weak spots you found in the mock run.
Behavioral and collaboration stories
Candidates describe the first stage as a check of background and fit, and the final interviews as possibly covering collaboration and problem-solving. The requirements candidates describe list communication, teamwork and adaptability, with mentoring as an extra. The first five prompts below are reported; the last two are extra practice drawn from the code-review and cross-team work the role is described as involving. Build each answer around a decision you made.
- Tell me about feedback you gave or received in a code review and what changed afterwards.
Describe a time when you faced a significant challenge in a project. How did you overcome it?
Medium · Behavioral / Leadership
Approach
- Choose a project challenge where you made the key decision, such as a latency problem, a risky migration or an unclear requirement. A challenge that was only a heavy workload shows effort but no judgment.
- Set the scene in two sentences, then spend most of your time on the options you weighed and why you chose one. Name at least one alternative you rejected.
- Say how you reduced risk and checked the outcome: a staged rollout, a benchmark before and after, a test that reproduced the bug, or a rollback plan. Use only numbers you actually measured.
- Close with the result and one lesson you applied later. The story fails if it ends with the team succeeding and your own part unclear.
Follow-up
- What alternative did you reject, and what would have made you choose it?
- How did you know the fix worked and the improvement was not coincidence?
- What would you do differently if you faced it again?
How do you prioritize tasks when working under tight deadlines?
Medium · Behavioral / Leadership
Approach
- Show a repeatable method before the story: list the work, mark what is required to ship, what depends on other people, and what can move without user impact. Then rank by user impact and risk.
- Tell one concrete case where you cut or deferred something. Say what you dropped, who agreed, and how you protected quality, for example by shipping behind a flag or deferring a non-essential endpoint.
- Show early communication with the product manager or designer: you raised the trade-off as soon as you saw it and offered options, rather than discovering at the deadline that the plan did not fit.
- Avoid the answer that says you worked longer hours. Prioritisation means choosing what not to do, and the answer should name that choice.
Follow-up
- Everything was labelled top priority. How did you decide?
- What happened to the work you deferred?
- What would you do if the deadline were impossible even after cutting scope?
Can you give an example of how you have influenced a team decision?
Medium · Behavioral / Leadership
Approach
- Pick a decision where you had no authority to dictate the outcome, such as a library choice, an API shape, a testing approach or a rollout plan.
- Show how you built the case: a small prototype, a benchmark, a short design write-up, or examples from past incidents. Evidence moves a team, while insistence does not.
- Describe how you handled objections. Name the strongest counterargument, say what you did with it, and whether you changed your own proposal.
- State the outcome honestly, including any part where the team overruled you and you committed to the chosen direction. Presenting the result as proof you were right makes the story weaker.
Follow-up
- Who disagreed most, and what did you change after hearing them?
- What if the team had picked the other option anyway?
- Did the decision hold up, and how did you find out?
What motivates you as a software engineer?
Medium · Behavioral / Leadership
Approach
- Answer with specifics. Name one or two things that drive you, such as owning a service end to end, making a slow path fast, or writing code others can change safely, and attach a real moment to each.
- Link the motivation to work you did, for example the satisfaction of cutting a flaky test suite or of seeing a feature used. A motivation with no behaviour behind it sounds rehearsed.
- Connect it to what the role is described as involving: shipping through the full lifecycle, working with product managers and designers, and reviewing code. Relate it to the work without flattering the company.
- Do not make it only about money or title, and do not answer with a generic love of problem solving. Have an example ready of a time the motivation changed a decision you made.
Follow-up
- When did that motivation last change something you did at work?
- What kind of work drains you, and how do you handle it?
- How would you stay engaged on a long maintenance project?
How do you handle conflicts within a team?
Medium · Behavioral / Leadership
Approach
- Use a real disagreement with a colleague, ideally technical, such as a code review dispute, an architecture choice or competing priorities. A story with no real tension is unconvincing, and claiming you never have conflict is worse.
- Show the sequence: you spoke to the person directly first, asked questions to understand their reasoning, found the shared goal, and moved the argument to evidence such as a test, a measurement or the user impact.
- Say when you involved a lead or manager and why. Escalation is reasonable after direct conversation has failed, and a story that starts there looks like avoidance.
- Finish with the working relationship afterwards. Avoid casting the other person as the villain, and say what you would now do earlier, such as agreeing on decision criteria before debating options.
Follow-up
- What if the other person had refused to change their mind?
- When did you decide to involve someone else?
- How do you handle disagreement in a code review specifically?