cswork / libraryOpen practice workspace

Interview guides · Bugcrowd

Bugcrowd Software Engineer Interview Guide

Bugcrowd Software Engineer interview guide: the four reported rounds, coding, design and behavioral questions with answer approaches, and a 7-day prep plan.

The 60-second brief

Bugcrowd builds cybersecurity products. As candidates describe the role, engineers work on systems that connect security researchers with organizations so those organizations can find and manage security vulnerabilities. The reported responsibilities are building and maintaining software applications, designing and implementing features with product managers, designers and other engineers, reviewing code, troubleshooting software issues and contributing to the architecture of the systems. For you, that means the loop is about general engineering ability (algorithms, data structures, design, debugging, teamwork) applied to a product where reliability and security matter.

This guide is for candidates interviewing for a Software Engineer position at Bugcrowd. It covers the four reported rounds, the reported technical, system design, problem-solving, behavioral and coding questions, and a seven-day plan. Behavioral and case-study answers are written as structures you fill with your own experience. Reports say questions vary by team, so treat the question list as a set of patterns to rehearse, not a fixed script.

Candidates report four rounds spread over roughly 3-5 weeks: Phone Screen, Technical Coding Challenges, Onsite Interview and Team Discussions. The flow can differ by team and role.

Focus areas

  • Pair Programming
  • Coding Challenges
  • Problem Solving

Overview

The reported questions fall into five groups. Technical and domain questions cover synchronous versus asynchronous programming, optimizing a slow database query, a challenging problem you solved, the principles of object-oriented programming and memory management. System design questions include a URL shortening service, a real-time chat application, choosing a database, microservices versus a monolith and data consistency in a distributed system. Case-study questions ask about a critical bug found just before launch, improving an existing feature, adding features to a legacy codebase, a quick decision with limited information and tight deadlines. Coding questions are classic: reverse a linked list, maximum sum of a contiguous subarray, binary search, hash tables and a sorting or searching problem. The fifth group is behavioral and leadership.

The requirements candidates describe list Java, Python or JavaScript, plus algorithms, data structures and system design principles. Cloud services (AWS, Azure), CI/CD pipelines, security knowledge and agile experience are described as nice to have. Pick one of the three languages and be fluent in it: you should be able to write a linked-list reversal, a binary search and a small hash table in it without an IDE. For the memory management question, prepare two languages so you can contrast them (for example reference counting or garbage collection against manual allocation).

The topics candidates associate with this loop are pair programming, coding challenges, problem solving, explaining technical choices and collaborative engineering. All of these reward the same habit: say what you are doing and why while you do it. Practice narrating a solution aloud, stating the trade-off behind each choice, and testing your code on small inputs before you declare it finished.

Because the reported responsibilities include code review, troubleshooting and working with product and design colleagues, expect the case-study and behavioral questions to be about how you handle real engineering situations. Reports also include a tip to keep technical answers clear and avoid over-complicating them. A short answer with the right structure and one concrete example beats a long tour of every concept you know.

The Bugcrowd interview process

These are the four stages candidates describe, in the order they report them. The exact flow varies by team, so confirm the format of each stage with your recruiter.

Phone Screen

Candidates describe the first stage as a call that checks whether you fit the role and gives you a chance to learn about it. Reports do not describe technical content, so plan for a conversation about your background and motivation, and keep a few core concepts fresh in case the call goes technical. Your job here is to be concise: a clear summary of what you have built, in which languages, and why this role.

What to demonstrate

  • Fit for the Software Engineer role as discussed on the call
  • How clearly you summarize your background and the languages you work in
  • Whether your experience lines up with the requirements candidates describe (Java, Python or JavaScript, algorithms, data structures, system design)

How to prepare

  • Write a two-minute walk through your experience that ends with the kind of work you want next, and rehearse it aloud.
  • Read the reported requirements and map each one (language, frameworks, system design, cloud, CI/CD) to a project of yours with one concrete detail.
  • Prepare questions for the call: which rounds are coding, which are design, and what the format of the coding exercises is.
  • Be ready to say what draws you to security software and be honest about any security background you do or do not have.

Technical Coding Challenges

Candidates report that you may be given coding exercises to show technical skill, and the reported topics include coding challenges and pair programming. Reports do not say whether the exercises are live or done on your own, so prepare for both: write working code in your chosen language and be able to explain each decision. This is the stage where the reported coding questions are practiced: reversing a linked list, the maximum sum of a contiguous subarray, binary search, hash tables, and sorting or searching problems.

What to demonstrate

  • Working code in a language you choose, including edge cases
  • How well you explain your reasoning while you code
  • Command of the basics behind the reported coding problems: pointers, array scans, binary search, hashing, sorting

How to prepare

  • Implement the five reported coding problems from scratch in your language, and for each one state the time and space complexity aloud.
  • For each solution, run it by hand on an empty input, a single element and a duplicate-heavy input before you call it done.
  • Practice talking through a problem while typing, as in pair programming: restate the problem, outline the approach, then code.
  • Write a small hash table with chaining or open addressing, including resize, so the hash table question is not your first time seeing the details.

Onsite Interview

Candidates describe in-person interviews that include both technical and behavioral assessments. Expect a mix: system design or architecture discussion, technical and domain questions, case studies from real engineering work, and behavioral stories. Reports also say to be ready to discuss past projects and the technologies you used, so pick two projects you can explain in depth, including what you would change.

What to demonstrate

  • Technical depth across the reported design, database and architecture topics
  • Behavioral examples that show how you work
  • Clarity of explanation when you reason aloud or sketch a design

How to prepare

  • Practice the design questions from the guide: a URL shortening service, a real-time chat application, choosing a database, microservices versus a monolith and data consistency. For each, write requirements, the read and write path, storage choice and one failure mode.
  • Rehearse the case studies: a critical bug before launch, improving an existing feature and adding features to a legacy codebase, each as a short ordered plan.
  • Choose two past projects and write down architecture, your contribution, one trade-off and one mistake for each.
  • Sketch designs by hand on paper, since you may need to draw components and data flow while talking.

Team Discussions

Candidates describe this stage as conversations with team members focused on collaboration and cultural fit. Reports mention teamwork, transparency, innovation and ethical practice in cybersecurity as themes. Treat it as the stage for your behavioral stories: how you handle disagreement, feedback, prioritization and leading without a title. Bring questions of your own about how the team works day to day.

What to demonstrate

  • Collaboration with colleagues in engineering, product and design
  • Fit with the team as described by the people you speak to
  • How you talk about feedback, conflict and ownership

How to prepare

  • Build a bank of five stories (a difficult teammate, leading a project, giving and receiving feedback, a quick decision, juggling priorities) and give each a one-line headline.
  • Have a considered view on ethics in security work, such as how you would treat sensitive vulnerability information, and be able to say it plainly.
  • Prepare questions about code review practice, how releases are decided and how engineers work with product managers and designers.
  • Rehearse each story at about two minutes, with the result stated in measurable or observable terms.

Where candidates lose points

Coding silently, then finding off-by-one or null-pointer bugs at the end of the linked-list or binary-search problem

Narrate as you write. State the loop invariant (for binary search, which half can still hold the target), then trace an empty input, a single element and two elements by hand before calling the code done.

Answering a design question such as the URL shortener or the real-time chat application by listing technologies

Open with requirements and scale, separate the read path from the write path, name the storage and the key you partition on, and finish with one failure mode and one consistency trade-off.

Answering "How would you optimize a slow database query?" with "add an index" and nothing else

Describe the order: capture the slow query, read its execution plan, check whether the filter or join columns are indexed and usable, look at row counts and joins, then change and re-measure.

Reciting definitions for object-oriented principles, memory management or synchronous versus asynchronous programming

Give each concept a small example in your own language, such as a blocking call versus an async call that overlaps I/O, or an object's lifetime under garbage collection compared with manual allocation.

Treating the critical-bug-before-launch and legacy-codebase questions as purely technical

Answer in sequence: reproduce and assess severity, decide on fix, mitigation or delaying the launch, tell stakeholders, and for legacy code, add tests around existing behavior before changing it.

Questions & practice

Explain the difference between synchronous and asynchronous programming.

Medium · Technical / Domain Questions

Approach

  1. Define it by what the caller does: a synchronous call blocks the calling thread until the result is ready; an asynchronous call starts the work and returns at once, and the result arrives later through a callback, a promise/future, or an await.
  2. Separate asynchrony from parallelism: a single-threaded event loop (Node.js, Python asyncio) interleaves tasks at await points, so it gives concurrency for I/O but no extra CPU, and one CPU-bound or blocking call stalls every task on that loop.
  3. Say where it pays off: I/O-bound services with many slow connections, because one loop can hold thousands of pending sockets while a thread-per-request model pays a stack and a context switch per waiting request.
  4. Show the latency effect with a concrete case: awaiting three independent HTTP calls one after another costs the sum of their latencies; Promise.all or asyncio.gather costs roughly the slowest one.
  5. Name the costs: async spreads up the call chain (callers must await), errors can vanish as unhandled rejections, stack traces are harder to read, and unbounded fan-out needs a limit such as a semaphore.

Follow-up

  • What happens if you call time.sleep or a synchronous database driver inside an asyncio coroutine, and how would you fix it?
  • How would you run CPU-heavy work from an async service without stalling the event loop?
  • You need to send 10,000 requests asynchronously without overwhelming the downstream service. How do you cap concurrency?

Discuss memory management in programming languages you are familiar with.

Medium · Technical / Domain Questions

Approach

  1. Start with stack versus heap: stack frames hold locals and are freed automatically on return; heap objects outlive the call and need either manual freeing or a collector.
  2. Manual and scope-based: in C, malloc/free, with the risks of leaks, double free and use-after-free; in C++, RAII ties a resource to an object's lifetime, unique_ptr gives single ownership, and shared_ptr uses reference counting, which leaks on cycles unless one link is a weak_ptr.
  3. Garbage collection: the JVM traces reachability from roots and is generational (most objects die young, so frequent minor collections cover the young generation); tracing collects cycles. CPython uses reference counting plus a cycle collector; V8 (JavaScript) uses a generational collector with mark-sweep and mark-compact for the old generation.
  4. Explain how a garbage-collected program still leaks: anything still reachable is kept, such as unbounded caches, static collections, listeners that are never removed and closures holding large objects. Name the tools: heap dumps and VisualVM for Java, tracemalloc for Python, Chrome DevTools heap snapshots for JavaScript.
  5. Give the trade-off: GC removes whole classes of bugs but costs CPU, memory headroom and pause time; manual or ownership-based models (Rust's borrow checker) give predictable latency at the price of more work for the programmer.

Follow-up

  • How can a Java or Python service run out of memory even though it has a garbage collector?
  • What do GC pauses do to tail latency, and what would you change to reduce them?
  • Why does a cycle of shared_ptr objects never get freed, and how do you break it?

Write a function to reverse a linked list.

Medium · Coding / Algorithms

Approach

  1. Iterative solution with three pointers: prev = null, curr = head; while curr is not null: next = curr.next, curr.next = prev, prev = curr, curr = next; return prev as the new head. O(n) time, O(1) extra space.
  2. Recursive alternative: reverse the rest of the list from head.next, then set head.next.next = head and head.next = null. Still O(n) time, but O(n) call-stack depth, which can overflow on very long lists, so prefer the iterative version in production.
  3. Edge cases: an empty list and a single node return unchanged; with two nodes, check that the old head's next becomes null, otherwise you leave a cycle.
  4. Test by walking the result from the new head and comparing it with the reversed input; for a doubly linked list, swap each node's prev and next pointers instead.

Follow-up

  • Reverse only the nodes between positions m and n, in one pass.
  • Reverse the list in groups of k nodes.
  • Check whether a linked list is a palindrome using O(1) extra space.

How would you find the maximum sum of a contiguous subarray?

Medium · Coding / Algorithms

Approach

  1. Clarify first: can values be negative, and is an empty subarray allowed (if it is, the answer is never below 0)?
  2. Brute force: try every start and extend the end with a running sum, O(n^2) time; mention it, then improve on it.
  3. Kadane's algorithm: cur = max(x, cur + x) and best = max(best, cur) for each element, O(n) time and O(1) space. Initialise both with the first element, so an all-negative array returns its largest element rather than 0.
  4. To return the subarray itself, record a tentative start whenever cur restarts at x, and save start and end whenever best improves.
  5. Mention the O(n log n) divide-and-conquer version (best of left, right and crossing sums), and integer overflow on large values in fixed-width languages.

Follow-up

  • Return the start and end indices of the best subarray.
  • Find the maximum product subarray. What changes when negative numbers are involved?
  • Solve it for a circular array.

Implement a binary search algorithm.

Medium · Coding / Algorithms

Approach

  1. State the precondition (sorted input) and write the closed-interval version: lo = 0, hi = n - 1; while lo <= hi: mid = lo + (hi - lo) / 2; return mid on a match, otherwise move lo = mid + 1 or hi = mid - 1; return -1. O(log n) time, O(1) space iteratively.
  2. Explain the midpoint: (lo + hi) / 2 can overflow 32-bit integers in Java or C++, so use lo + (hi - lo) / 2.
  3. Choose one invariant and stick to it: closed [lo, hi] with lo <= hi, or half-open [lo, hi) with lo < hi and hi = mid. Mixing them causes off-by-one errors or an infinite loop when lo = mid.
  4. Cover the variants: lower bound (first index with a[i] >= target), first and last occurrence with duplicates, and binary search on the answer over any monotonic predicate.
  5. Test an empty array, a single element, targets below the minimum and above the maximum, and runs of duplicates.

Follow-up

  • Find the first and last position of a target in a sorted array with duplicates.
  • Search for a target in a rotated sorted array.
  • Find the smallest ship capacity that delivers all packages within D days.

Explain how hash tables work and provide an example implementation.

Medium · Coding / Algorithms

Approach

  1. Core idea: a hash function maps a key to a bucket index (hash(key) mod capacity), giving average O(1) insert, lookup and delete; the worst case is O(n) when many keys land in the same bucket.
  2. Collision handling: separate chaining keeps a list per bucket (Java's HashMap turns a bucket into a balanced tree once it holds more than 8 entries and the table has at least 64 buckets); open addressing probes for the next free slot (linear, quadratic or double hashing) and needs tombstones on delete. CPython's dict uses open addressing.
  3. Resizing: when size / capacity passes the load-factor threshold (0.75 by default in Java's HashMap), allocate a larger array, usually double, and rehash every entry. One resize is O(n), but doubling keeps inserts amortised O(1).
  4. Implementation sketch: an array of bucket lists, with put (update the value if the key exists, else append, then check the load factor), get and remove. Keys need consistent equals and hashCode, and a key mutated after insertion becomes unreachable.
  5. Security note: adversarial keys that all collide (hash flooding) push operations to O(n); seeded hash functions such as Python's randomised string hashing defend against it.

Follow-up

  • Why must equal objects have equal hash codes, and what breaks if you override equals but not hashCode?
  • How would you make your hash table safe for concurrent use?
  • Design an LRU cache with O(1) get and put.

Solve a problem involving sorting or searching algorithms.

Medium · Coding / Algorithms

Approach

  1. Clarify input size, value range, duplicates, whether the input is already sorted, memory limits and whether stability matters. Often sorting first turns an O(n^2) pairwise check into O(n log n) sorting plus a two-pointer pass.
  2. Know the main sorts: merge sort is O(n log n) worst case and stable but needs O(n) extra space; quicksort is O(n log n) on average and in place but O(n^2) in the worst case and not stable (a random pivot avoids the sorted-input worst case); heapsort is O(n log n) worst case with O(1) extra space and is not stable; insertion sort is fast on small or nearly sorted input; counting or radix sort runs in O(n + k) for bounded integer keys.
  3. Know your library: Python's sort is Timsort (stable); Java's Arrays.sort uses dual-pivot quicksort for primitives and a stable merge sort (Timsort) for objects.
  4. Pick the search structure from the query: binary search on sorted data, a hash set for O(1) average membership, a heap for top-k in O(n log k), and quickselect for the k-th element in O(n) average time.
  5. Test an empty input, a single element, all-equal values, already sorted and reverse-sorted input.

Follow-up

  • Find the k-th largest element without fully sorting the array.
  • How would you sort a file too large to fit in memory?
  • Merge k sorted lists efficiently and give the complexity.

How would you optimize a slow database query?

Medium · Technical / Domain Questions

Approach

  1. Measure before changing anything: capture the exact query with real parameters, run EXPLAIN ANALYZE (PostgreSQL) or EXPLAIN (MySQL), and find where the time goes: sequential scans on large tables, estimated rows far from actual rows, nested loops over many rows, or sorts spilling to disk.
  2. Match an index to the predicate: in a composite index put equality columns first and the range or ORDER BY column last; a covering index (INCLUDE columns in PostgreSQL) can allow an index-only scan; an index on a low-selectivity column rarely helps.
  3. Rewrite patterns that defeat indexes: a function on the column (WHERE lower(email) = ... needs an expression index), a leading-wildcard LIKE '%x', implicit type casts, SELECT *, and deep OFFSET pagination, which keyset pagination (WHERE id > last_seen ORDER BY id LIMIT n) replaces.
  4. Check the causes outside the SQL text: stale planner statistics (run ANALYZE), N+1 queries issued by an ORM, and lock waits; if the query is still too slow, consider a materialized view, a cache or a read replica.
  5. State the trade-off: every index adds write cost and storage, so confirm the gain with before-and-after plans on production-sized data rather than a small local table.

Follow-up

  • You added an index but the planner still does a sequential scan. What are the possible reasons?
  • How would you add an index to a large, busy table without blocking writes?
  • The query is fast when you run it by hand but slow in production. What do you check next?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.

Approach

  1. Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
  2. Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
  3. Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
  4. State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.

Follow-up

  • A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
  • The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?

Describe a challenging technical problem you've solved.

Medium · Technical / Domain Questions

Approach

  1. Pick a problem with a technical root cause that you personally diagnosed and fixed, such as a memory leak, a race condition, a slow query or a scaling limit. Avoid a story where your part was coordination.
  2. Structure it as symptom, constraints, hypotheses, evidence, fix and measured result, and name the real tools you used (profiler, query plan, logs, heap dump, a reproduction script).
  3. Show the reasoning: which hypotheses you ruled out and how, and why the fix addresses the cause rather than hiding the symptom.
  4. Close with numbers and the trade-off you accepted (latency before and after, error rate, cost), plus the test or alert you added so it would not come back.
  5. The mistake that sinks it: a story told entirely in the collective voice, so the listener cannot tell what you did. Say "I" for your own actions.

Follow-up

  • How did you confirm that was the root cause and not a correlated symptom?
  • What would you do differently if you hit the same problem again?
  • What did you change so the same class of problem would be caught earlier?

What are the principles of object-oriented programming?

Medium · Technical / Domain Questions

Approach

  1. Name the four principles, each with a short code example: encapsulation (private state behind methods that keep invariants), abstraction (callers depend on an interface, not the implementation), inheritance (a subclass reuses and specialises a base class), polymorphism (one call site, behaviour chosen at runtime by dynamic dispatch).
  2. Show polymorphism concretely: a Shape interface with area() implemented by Circle and Rectangle, and a loop that sums area() over a List<Shape> without type checks.
  3. Add judgement: prefer composition over inheritance when the relationship is "has-a" or behaviour must vary independently, because deep hierarchies couple subclasses to base-class internals (the fragile base class problem).
  4. Mention the Liskov substitution principle with the classic violation: Square extending a mutable Rectangle breaks callers that set width and height independently. Mention SOLID briefly as the design rules built on these principles.
  5. Tie it to a language you know well: in Java a class extends one class but implements many interfaces, and since Java 8 interfaces can carry default methods; in Python, duck typing gives polymorphism without a shared base class.

Follow-up

  • When would you choose composition over inheritance? Give an example from your own code.
  • What is the difference between method overloading and overriding?
  • In Java, when would you use an abstract class rather than an interface?

Design a URL shortening service. What considerations would you take into account?

Medium · System Design / Architecture

Approach

  1. Fix requirements and scale: create a short link, redirect, optional custom alias and expiry, click analytics; reads far outnumber writes, so the redirect path is the one to optimise. Estimate storage from links per day times bytes per row times retention.
  2. Key generation: base62-encode a unique integer from a counter or pre-allocated ID ranges per server (7 base62 characters give 62^7, about 3.5 trillion codes), or hash the long URL and truncate, which needs collision checks. Sequential codes are guessable and allow enumeration, so shuffle or encrypt the ID if that matters.
  3. Storage: a key-value mapping from short code to long URL (a KV store, or a relational table with a unique index on the code), partitioned by code since every lookup is by code.
  4. Redirect path: serve hot codes from a cache (Redis or a CDN). 301 lets browsers cache the redirect and cuts load but hides repeat clicks; 302 keeps every click visible for analytics. Write click events to a queue and aggregate asynchronously so analytics never slows the redirect.
  5. Abuse and safety: validate destination URLs, check them against malicious-URL lists, rate-limit link creation per user or IP, and support taking a link down.

Follow-up

  • How do several application servers generate unique codes without a single counter becoming the bottleneck?
  • If the same long URL is submitted twice, should it get the same code? What does each choice cost?
  • How would you expire links and reclaim their codes?

How would you architect a real-time chat application?

Medium · System Design / Architecture

Approach

  1. Scope it: one-to-one and group chat, delivery and read receipts, presence, message history, and delivery to users who are offline.
  2. Connections: clients hold WebSocket connections to a tier of gateway servers; a registry (for example Redis) maps user to gateway, and gateways exchange messages through pub/sub or a message broker.
  3. Send path: the client attaches a client-generated message ID for deduplication; the server persists the message, assigns a per-conversation sequence number, acks the sender, then fans out to recipients' gateways. Offline recipients get a push notification and fetch everything after their last-seen sequence on reconnect.
  4. Storage: partition messages by conversation ID and order them by the server-assigned sequence rather than client clocks; a wide-column store such as Cassandra or sharded SQL fits this append-heavy, range-read pattern.
  5. Trade-offs: fan-out on write is simple for small groups but expensive for very large ones; presence via heartbeats with a TTL is eventually consistent, and broadcasting every status change to large contact lists is costly, so push presence only to active viewers.

Follow-up

  • A client reconnects and resends messages it is unsure were delivered. How do you avoid duplicates and keep ordering?
  • A gateway server holding many open connections crashes. What happens to those users and to messages in flight?
  • How would you add end-to-end encryption, and what does the server lose the ability to do?

What factors influence your choice of database for a new project?

Medium · System Design / Architecture

Approach

  1. Start from access patterns, not product names: the queries the application runs, the read/write ratio, the data's shape and relationships, expected volume and growth, and latency targets.
  2. Transactions and integrity: if operations span several rows or tables and need ACID guarantees, foreign keys and ad hoc joins, a relational database (PostgreSQL, MySQL) is the default.
  3. Scale and access shape: key-based access at very high write volume suits DynamoDB or Cassandra, where tables are modelled per query and ad hoc joins are lost; document stores suit nested, varying records; search engines (Elasticsearch) handle full-text search; Redis handles caching and counters; time-series and graph databases cover their own niches.
  4. Consistency and availability: say which operations need strong consistency and where eventual consistency is acceptable, and what the database does during a replica failure or network partition.
  5. Operations: team experience, managed-service options, backup and restore, migrations and cost. Running several stores means keeping them in sync, so start with one relational store until a measured requirement rules it out.

Follow-up

  • At what point would PostgreSQL stop being enough, and what would you try first (read replicas, partitioning, caching) before switching?
  • How would you keep a search index consistent with the primary database?
  • What queries become hard or impossible once you move a dataset into DynamoDB?

Explain the trade-offs between microservices and monolithic architecture.

Medium · System Design / Architecture

Approach

  1. Monolith strengths: one deployable, in-process calls, single-database transactions, simple local development and debugging, and one place to trace a request. Costs as it grows: everything deploys together, the scaling unit is the whole app, build and test times rise, and module boundaries erode.
  2. Microservice strengths: independent deploys and scaling per service, ownership by one team, failures isolated to one service, freedom to pick technology per service.
  3. Microservice costs: every call crosses the network (latency, partial failure, timeouts, retries); transactions across services become sagas with eventual consistency; you need distributed tracing, centralised logging, versioned APIs and more infrastructure.
  4. Decision criteria: team count and structure (Conway's law), parts of the system with very different scaling needs, and how stable the domain boundaries are. A modular monolith with enforced boundaries is often the right step first, and the strangler fig pattern extracts services one at a time.
  5. Name the failure mode: a distributed monolith, where services share one database or must deploy in lockstep, pays both sets of costs and gets neither set of benefits.

Follow-up

  • How would you pick and extract the first service from an existing monolith?
  • How do you handle an operation that must update data owned by two different services?
  • What observability would you require before splitting a monolith?

Describe how you would ensure data consistency in a distributed system.

Medium · System Design / Architecture

Approach

  1. Say which consistency each operation needs: linearizable (reads see the latest write), read-your-writes, or eventual. During a network partition a system must choose between consistency and availability (CAP), so make that choice explicitly per operation.
  2. Within one data store: single-leader replication with synchronous replicas for strong reads, or quorums where R + W > N so read and write sets overlap; consensus protocols such as Raft or Paxos for leader election and critical metadata.
  3. Across services: avoid two-phase commit where possible, since a failed coordinator blocks participants; use a saga with compensating actions, and a transactional outbox so the state change and its event are written in one local transaction.
  4. Assume at-least-once delivery: make consumers idempotent with deduplication keys or idempotency keys, and prevent lost updates with optimistic concurrency (a version column with compare-and-set) or unique constraints; protect lock holders with fencing tokens.
  5. Verify continuously: monitor replication lag, and run reconciliation jobs that compare sources of truth and report drift.

Follow-up

  • Why is exactly-once delivery hard, and how do you get effectively-once processing?
  • Two users update the same record at the same moment. How do you prevent a lost update?
  • What happens to a saga when a compensating step itself fails?

How would you handle a situation where a critical bug was found just before a product launch?

Medium · Problem-Solving / Case Studies

Approach

  1. Triage before deciding: reproduce the bug, then size it: what breaks (data loss, a security hole, wrong billing, or a cosmetic issue), which users hit it, how often, and whether a workaround exists.
  2. Lay out the options with their risks: fix and retest, launch with the affected path switched off behind a feature flag, cut that part from the launch scope, or delay. Make the call together with the people who own the launch, not alone.
  3. If you fix it: make the smallest targeted change, add a regression test that fails without the fix, run the full suite, release through a canary or staged rollout, and have monitoring and a rollback plan ready.
  4. Communicate early and concretely: what the bug is, the decision, the remaining risk and the next update time. If the bug is a security vulnerability, treat it as blocking the release unless the affected path can be fully disabled.
  5. Afterwards, run a blameless review of why tests or review missed it and add the check that would have caught it.

Follow-up

  • The product owner insists on launching on the planned date anyway. What do you do?
  • How do you decide whether a bug is severe enough to block a launch?
  • What would you add to the pipeline so this class of bug is caught earlier?

Imagine you are tasked with improving an existing feature. What steps would you take?

Medium · Problem-Solving / Case Studies

Approach

  1. Define the problem and the success measure first: talk to users and the product owner, read support tickets, error logs and usage data, and record a baseline for the metric you intend to move.
  2. Understand the current implementation before changing it: read the code and its tests, find other callers and dependent features, and note the constraints that explain why it works the way it does.
  3. List candidate changes, estimate impact against effort, and pick the smallest change that moves the metric; keep existing behaviour backward-compatible where users rely on it.
  4. Ship incrementally behind a feature flag or staged rollout, ideally as an A/B test, and watch the target metric alongside errors and latency.
  5. Decide from the data: iterate, expand the rollout, or roll back, and write down what you learned.

Follow-up

  • Several stakeholders want different improvements. How do you choose what to do first?
  • The change ships and the metric does not move. What do you do next?
  • How do you make sure the improvement does not break the workflows of existing users?

Given a legacy codebase, how would you approach adding new features?

Medium · Problem-Solving / Case Studies

Approach

  1. Read before changing: get it running locally, trace the code path the new feature touches, use git log and blame to learn why the code looks the way it does, and find anyone who knows the history.
  2. Write characterization tests around the area first, tests that pin down what the code does today (even if it looks wrong), so you can tell whether your change altered existing behaviour.
  3. Find a seam: add the feature behind an interface, adapter or new module rather than threading it through tangled code; grow new code around the old (strangler fig) instead of a big-bang rewrite.
  4. Keep refactoring and behaviour changes in separate, small, reversible commits or pull requests, and release the new behaviour behind a feature flag.
  5. Improve only what the change touches (naming, a missing test, a comment explaining a quirk), and record what you learned for the next person.

Follow-up

  • The code has no tests and is hard to test in isolation. Where do you start?
  • When would you argue for rewriting a component instead of extending it?
  • You find behaviour that looks like a bug, but some users may depend on it. What do you do?

Describe how you would manage a high-pressure situation involving tight deadlines.

Medium · Problem-Solving / Case Studies

Approach

  1. Ground it in a real deadline you faced, and first say which constraint was fixed: the date, the scope or the quality bar, because that decides what you were able to trade.
  2. Show the plan: break the work into tasks, find the critical path, and agree with stakeholders on what had to ship and what could wait.
  3. Show how you protected quality under pressure: tests and review kept on the risky core path, simpler designs chosen where they were good enough, and any shortcuts written down as follow-up work.
  4. Show the communication: raise schedule risk as soon as you saw it, with a concrete status and options, and remove blockers for others instead of working alone.
  5. Give the result with evidence, and what you would do differently. The answer that sinks it is "I just worked longer hours" with no prioritisation or trade-off.

Follow-up

  • What did you cut, and how did you decide?
  • At what point do you tell stakeholders a date is at risk?
  • How did you handle the shortcuts taken to hit the deadline afterwards?

One log partition stops advancing while the others drain

Easy · poison message · head-of-line blocking · consumer lag

Search results for a subset of tenants are hours stale; the rest are current. The projection consumer reports lag of zero on 15 of 16 partitions and 400,000 on one. Its error rate is flat and its CPU is idle. outbox_event has no pending rows older than a second, so the relay has published everything it holds. Identify the mechanism, give the ordered checks, and state what you do in the first ten minutes versus what you change permanently.

Approach

  1. Read the lag distribution first. A slow consumer lags everywhere; zero on fifteen partitions and 400,000 on one is not throughput. Idle CPU on the stuck partition means the consumer is not advancing its offset at all, which points at one message it cannot get past rather than at a rate problem.
  2. Exonerate the producer before touching the consumer. No pending outbox rows older than a second means the relay published, so the event exists in the log. This separates never sent from sent and never applied, which are different code paths and usually different owners.
  3. Read the message at the stuck offset and the handler's log lines for its event_id. A flat error rate with no progress has two explanations and you must distinguish them: the handler is throwing and the retry loop is swallowing it, or the handler is blocking on something and never returning. Idle CPU with no error lines favours the second.
  4. Mitigate before diagnosing further. Move the offending event to a dead-letter store and commit the offset past it. Adding consumers does nothing here, because a partition is consumed by exactly one member of the group, and the blast radius is every aggregate hashed to that partition, not only the aggregate that produced the bad event.

Follow-up

  • The dead-lettered event carried aggregate_version 7 and the projection had applied 6. What must the replay do differently if 8 and 9 landed in the meantime?
  • How do you show staleness to the user while the partition is behind, given the API already returns the projection's watermark?

Suggested practice checklist

The plan goes from the four reported rounds to the coding problems, technical and design questions, case studies and finally the behavioral stories.

Day 1 — Map the loop and fix your language

  • Write the four reported rounds (Phone Screen, Technical Coding Challenges, Onsite Interview, Team Discussions) and what each says it covers, then mark which you are least ready for.
  • Pick Java, Python or JavaScript as your interview language and write a short list of its core features you will need (collections, string handling, recursion limits).
  • Write a two-minute walk through your experience and map each reported requirement to one project of yours.

Deliverable: A one-page round map with your weakest stage marked, a chosen language, and a written two-minute career walk-through with a requirement-to-project table.

Day 2 — Core coding problems

  • Implement the linked-list reversal both iteratively and recursively, and note the space cost of each.
  • Solve the maximum contiguous subarray sum with a brute force first, then a single pass, and test an all-negative array.
  • Write binary search iteratively, then a variant that returns the first matching index among duplicates.

Deliverable: Three working solutions in your language with complexity noted and a test list for each (empty, single element, duplicates, all negative).

Day 3 — Hash tables, sorting and searching

  • Implement a hash table with chaining, including collision handling and resizing, and state its average and worst-case costs.
  • Pick two sorting algorithms (for example merge sort and quicksort) and write when you would choose each: stability, extra memory, worst case.
  • Solve one problem that combines sorting with searching, such as finding a pair with a given sum, and say what each approach costs.

Deliverable: A working hash table, a half-page comparing two sorts, and one solved sorting-and-searching problem with complexities.

Day 4 — Technical and domain questions

  • Write a one-minute answer, with an example in your language, for synchronous versus asynchronous programming and for the principles of object-oriented programming.
  • Write a comparison of memory management in two languages you know, covering allocation, freeing and what causes leaks.

Deliverable: Spoken-answer notes for the four technical questions, a two-language memory comparison, and a solved practice SQL problem.

Day 5 — System design

  • Design a URL shortening service: requirements, key generation, storage, read-heavy caching, redirect path, and what happens on a collision or a deleted link.
  • Design a real-time chat application: connection handling, message storage, delivery ordering, and what happens when a client goes offline.
  • Write a decision table for choosing a database and a second table for microservices versus a monolith, then list three tools for keeping data consistent across services.

Deliverable: Two design sketches with a failure mode each, a database decision table, a microservices-versus-monolith comparison and a consistency cheat sheet.

Day 6 — Problem-solving cases and debugging

  • Write an ordered plan for a critical bug found before a launch: reproduce, severity, fix or mitigate or delay, who to tell.
  • Write plans for improving an existing feature and for adding features to a legacy codebase, including the tests you would add first.

Deliverable: Three written case-study plans, a recorded narration of the practice debugging problem, and a one-page outline of your technical story.

Day 7 — Behavioral stories and full run-through

  • Write headlines and the key facts for stories covering a difficult teammate, leading a project, feedback, prioritization and a quick decision.
  • Prepare a short honest answer to what motivates you, tied to two real episodes.
  • Answer every story aloud at about two minutes and tighten each one until the result is stated in the last sentence.

Deliverable: Five story outlines, a motivation answer and one recorded run-through of all the behavioral prompts.

Behavioral and team-fit preparation

Candidates report behavioral assessments in the onsite stage and a team-discussion stage focused on collaboration and cultural fit. The prompts below cover conflict, motivation, prioritization, leading, feedback and acting with limited information. Prepare each as a specific story with your own decision and a result you can state, and keep one example of working with product or design colleagues ready.

Describe a time you had to work with a difficult team member. How did you handle it?

Medium · Behavioral / Leadership

Approach

  1. Choose a conflict about work, such as missed handoffs, harsh code review comments or shifting requirements, and describe the other person's behavior and its effect on delivery instead of passing judgment on their character.
  2. Show you went to them first: a private one-to-one where you asked about their constraints and priorities, then a concrete change such as a written agreement on who owns which interface, or moving the argument into a design document or a small benchmark.
  3. Say when and why you involved a manager, if you did. It should come after the direct conversation, and be about unblocking the work.
  4. Close with the result (review turnaround, the release that shipped, a working relationship that recovered) and one thing you changed in your own behavior.
  5. The mistake that sinks it is a story where you were simply right and the other person was simply wrong, or one that ends with a complaint about them.

Follow-up

  • What would you have done if they had not changed?
  • Is there anything you contributed to the friction?
  • How did you keep the rest of the team unblocked while this was unresolved?

What motivates you to perform well in your role?

Medium · Behavioral / Leadership

Approach

  1. Name two specific drivers and tie each to a real episode, for example closing a reliability gap that kept paging the team, or seeing users adopt a feature you built. A claim with no episode behind it sounds rehearsed.
  2. Show the motivation changes your behavior when the work is dull: describe fixing a flaky test suite, writing the migration notes or taking the unglamorous on-call fix.
  3. Connect it to this role honestly. The reported work is building and maintaining software that links security researchers with organizations, so say what appeals to you about reliable, security-conscious software, and only claim security interest if it is true.
  4. Avoid answers that are only about pay, title or vague love of challenges, and avoid reciting the company's mission back to the interviewer.

Follow-up

  • What drains your motivation, and what did you do the last time that happened?
  • Tell me about a task you found boring and how you handled it.
  • How do you stay motivated when a project is cancelled or reprioritized?

How do you prioritize tasks when working on multiple projects?

Medium · Behavioral / Leadership

Approach

  1. State a concrete method: list every item, then rank by deadline, impact, who is blocked and the cost of delay, and keep one visible queue (a ticket board) so priorities are not only in your head.
  2. Back it with one real episode of two or three competing items. Say what you did first, what you delayed or dropped, and how you told the affected people in writing.
  3. When two stakeholders each call their item top priority, take it to the person who owns the ranking, usually the product manager, with the trade-off laid out, instead of choosing silently.
  4. Reserve capacity for interruptions such as a production bug, and re-plan out loud when something changes.
  5. Do not answer with long hours or a tool name. The point is the decision rule and the communication.

Follow-up

  • Two stakeholders both insist their item is the top priority. What do you do?
  • What did you say no to recently, and how did you say it?
  • An urgent bug arrives in the middle of a sprint. How does your plan change?

Tell me about a time you took the lead on a project.

Medium · Behavioral / Leadership

Approach

  1. Pick a project where you owned the outcome without a formal title. Give the goal and the constraint in two sentences, then spend your time on what you did.
  2. Cover how you broke the work into pieces, how you matched pieces to people, and how you kept everyone aligned (a short written plan, regular check-ins, shared review standards).
  3. Include one decision you made that others disagreed with, how you handled the disagreement and what happened.
  4. Include a setback and how you adjusted scope, timeline or approach, then give the outcome and what teammates gained.
  5. Use "I" for your actions and name teammates for theirs. The failure modes are a story that is only coordination with no decisions, or taking sole credit for team work.

Follow-up

  • How did you handle a teammate who was not delivering their part?
  • What would you change about how you led it?
  • How did you decide when the team was split between two approaches?

How do you approach giving and receiving feedback?

Medium · Behavioral / Leadership

Approach

  1. Answer both halves with one example each, because the question asks about giving and receiving.
  2. For giving, use a code review example. Say what you wrote, for instance: "I pointed out that this function mixed parsing and persistence, suggested splitting it so each part could be tested, and offered to pair on it." Good feedback is specific, about the work, timely and comes with a suggestion.
  3. For receiving, use real critical feedback, such as PRs that were too large or missing tests. Say what you changed (smaller PRs, tests first) and how you checked it worked, for instance by later review comments.
  4. Avoid disguised strengths as weaknesses, and avoid claiming you take feedback well without an instance. A defensive tone in the retelling is the usual way this answer fails.

Follow-up

  • Tell me about feedback you disagreed with and what you did.
  • How do you give feedback to someone more senior than you?
  • What do you do when the same issue keeps recurring after feedback?

Discuss a time when you had to make a quick decision with limited information.

Medium · Problem-Solving / Case Studies

Approach

  1. Pick a moment with real time pressure, such as a production incident or a release blocker, where waiting would have cost something.
  2. Lay out your reasoning: what you knew, what you did not, which facts you could get in minutes (logs, a quick check with the owner) and whether the decision was reversible. Prefer the option that kept a way back, such as a feature flag or a rollback.
  3. Say who you informed and what you wrote down afterward so the decision was visible.
  4. Give an honest outcome, including anything that turned out wrong, and what you changed afterward (an alert, a runbook step, a pre-release check).
  5. A story where the information was available and you did not look, or one with no downside acknowledged, undermines the answer.

Follow-up

  • What information would have changed your decision?
  • Was the decision reversible, and how did you keep it that way?
  • How did you check afterward whether you were right?