cswork / libraryOpen practice workspace

Interview guides · A S S E M B L E D

A S S E M B L E D Software Engineer Interview Guide

A S S E M B L E D Software Engineer interview guide: 7 reported rounds, coding and system design questions, behavioral prompts and a 7-day prep plan.

The 60-second brief

A S S E M B L E D is described as a maker of workforce management software: tools that handle scheduling logic, operational data and real-time workflows so organisations can coordinate their teams. Candidates describe the Software Engineer role as building and maintaining features of that product, writing tested code, and owning services through to production monitoring.

This guide is for Software Engineer candidates, backend or full-stack, preparing for the loop candidates describe for A S S E M B L E D. The seniority range reported is mid-level to senior, set by the team. It covers the seven reported stages, the coding, system design and behavioral questions candidates describe, and a 7-day plan. It does not cover pay, and it does not claim to know what any single interviewer will ask.

Candidates report 7 rounds over about 4-6 weeks: a recruiter screen, a pair-programming phone screen, a virtual onsite described as four distinct rounds, and then deep-dive coding, system design, a product-minded engineering discussion and a behavioral/values interview.

Focus areas

  • HSE (Health, Safety, and Environment) Compliance
  • Structural Engineering (Design & Analysis)
  • Electrical Engineering (Systems & Supervision)

Overview

The product, as candidates describe it, is workforce management software, so the reported problems are close to that domain: real-time scheduling updates, appointment calendars with overlapping time ranges, request throttling for public API routes, and anomaly detection over a stream of logs. When you practise, wrap the standard techniques in scheduling vocabulary. Merge intervals as calendar slots, write a per-user rate limiter, and sketch an event pipeline that applies schedule changes without losing or duplicating any. The framing makes the follow-up questions easier to anticipate.

Both the phone screen and the deep-dive session involve coding. The technical phone screen is described as pair-programming on a real codebase or a realistic problem, and the deep-dive is a separate, focused coding interview. Prepare the same habits for both: pin down input constraints and edge cases first, pick the data structure from the access pattern, state the complexity, and write tests. The system design stage then asks for architecture: the reported areas are data modeling and storage, load balancing and caching, and asynchronous processing with queues. Less common topics are multi-region recovery and zero-downtime database migrations.

Two stages are about decisions rather than algorithms: a product-minded engineering discussion and a behavioral/values alignment interview. The requirements candidates describe stress unit and integration testing, relational or non-relational database experience, and distributed systems principles, with AWS or GCP, Docker, Kubernetes and event streaming as extras. One tip candidates pass on is to bring concrete stories of owning a feature end to end and watching it in production. Collect those stories early, because the same episodes can serve the product discussion and the behavioral rounds.

The A S S E M B L E D interview process

Candidates report 7 stages over about 4-6 weeks. The virtual onsite is described as four distinct rounds, and the list names four stages after it. Formats can shift with seniority and team.

Recruiter Screen

Candidates describe an opening conversation about your background, what the role involves and whether the fit works on both sides. No technical content is reported for this stage, so the preparation is about being clear on your own story. Connect your history to the themes the role lists: backend or full-stack work, distributed systems, tested code and production ownership.

What to demonstrate

  • Whether your background lines up with the role as described
  • Whether role expectations and fit make sense for both sides

How to prepare

  • Write a short career narrative built around systems you built and ran in production, ending with a specific reason this scheduling-and-operations product interests you.
  • Prepare questions about the team, the stack and how the pair-programming stage is run, for example whether you use your own editor.
  • Have location, notice period and compensation expectations ready so logistics do not take over the conversation.

Technical Phone Screen

Candidates describe practical pair-programming in which you navigate a real codebase or work a realistic problem alongside the interviewer. Because the setting can be an existing codebase, reading skill matters as much as writing: find the entry point, run the tests, make a small change and add a test. Say your assumptions aloud and confirm edge cases and input constraints before you type, which is a pitfall candidates single out for technical screens.

What to demonstrate

  • Working code produced in a live, shared setting
  • Ability to read and navigate unfamiliar code
  • How you communicate while coding with another person

How to prepare

  • Pick one language and know its test runner, debugger and standard collections without looking anything up.
  • Clone an open-source service you have never seen, locate where one behaviour lives, change it and add a test, narrating as you go.
  • Practise realistic problems such as merging overlapping calendar intervals and throttling requests per user, starting each with a spoken list of edge cases.

Virtual Onsite Stage

Candidates describe a virtual onsite stage as a comprehensive loop of four distinct rounds, and the reported list names four stages after it: a deep-dive coding session, a system design interview, a product-minded engineering discussion and a behavioral/values alignment interview. Prepare for all four areas in the virtual loop.

What to demonstrate

  • Breadth across coding, design, product reasoning and behavioral topics
  • The skills named in the four stage descriptions: in-depth coding, system design and architecture, product impact of engineering decisions, and values alignment

How to prepare

  • Rehearse a mixed sequence in one session: a coding problem, a design question and two stories, then note which switch cost you the most.
  • Set up your remote kit in advance: camera, audio, editor, and a diagramming tool you can draw in quickly for design.
  • Keep a one-page sheet of habits to run in every round: state assumptions, name complexity, discuss trade-offs, ask about requirements.

Deep-Dive Coding Session

Candidates describe a focused coding interview meant to assess coding skill in depth. The coding questions candidates report include merging overlapping appointment intervals, implementing a rate limiter, detecting anomalous patterns in a stream of logs, optimizing an inefficient snippet, and handling edge cases with unit tests. The data structures candidates list for coding are hash maps, trees, graphs and heaps, with Big-O analysis and modular, testable functions.

What to demonstrate

  • Choice of data structures and the complexity reasoning behind them
  • Clean, bug-free code that handles edge cases
  • Code structured into testable, reusable functions

How to prepare

  • Solve interval merging until you can write it from memory, then extend it to find free slots and detect conflicts between two calendars.
  • Implement a token bucket and a sliding-window counter, and state the memory and accuracy trade-off of each.
  • For the log-stream anomaly question, build a rolling-window counter and a heap- or deque-based version, and say what each assumes about the traffic.
  • After every solution, write the time and space complexity and three tests, including empty input and ties.

System Design Interview

Candidates describe a discussion on system design and architecture. The evaluation areas candidates list are data modeling and storage (relational versus non-relational, driven by read and write patterns), load balancing and caching, and asynchronous processing with queues and event-driven designs. Scenarios they mention include a global notification service and splitting a monolithic scheduling application into microservices. Lead with requirements, then the data model, the read and write paths and the failure modes.

What to demonstrate

  • Clear service boundaries and a justified choice of data store
  • Use of caching and queues, with failure modes and trade-offs stated
  • Ability to scale a design while keeping it operable

How to prepare

  • Design the real-time scheduling-update pipeline end to end: producers, partitioning key, queue, consumers, idempotent apply, ordering per schedule and replay after a failure.
  • Write a Redis versus Memcached comparison you can say aloud: data structures, persistence, replication, eviction, and how you invalidate and avoid stampedes.
  • Practise the notification-service and monolith-to-microservices scenarios, ending each with the first thing that breaks at ten times the load.
  • Prepare a security walk-through for sensitive user data across API endpoints: authentication, authorisation per tenant, encryption in transit and at rest, and secrets handling.

Product-Minded Engineering Discussion

Candidates describe a conversation about engineering decisions with a focus on product impact. The requirements candidates quote stress explaining trade-offs to technical and non-technical stakeholders, so prepare decisions you actually made and the reasoning behind each. For every one, state the user or business problem, the options, what you chose, what it cost and how you knew it worked.

What to demonstrate

  • Engineering choices connected to product outcomes
  • Clarity when explaining trade-offs to non-engineers

How to prepare

  • Pick three decisions from your own work and write each as problem, options, choice, cost and measured result.
  • Rehearse a workforce-scheduling scenario: when a manager changes a shift, does the agent need it pushed in real time or is polling every few minutes acceptable? Name the cost of each option to users and to the system.
  • Rehearse a second one from the user's side: two managers edit the same shift at once. Decide what each of them sees, whether the later edit wins or is rejected, and how the conflict is shown.
  • Bring one example of weighing feature speed against technical debt, since that balance is part of the role as described.

Behavioral/Values Alignment Interview

Candidates describe an assessment of cultural fit and alignment with company values. The values themselves are not listed in the reports, so find the company's published material yourself and match it to real episodes. The behavioral topics candidates report are technical compromise under a deadline, code-review disagreement, a failed deployment and its recovery, backlog prioritisation and mentoring.

What to demonstrate

  • Fit with the values the company states
  • Collaboration, disagreement and ownership shown through concrete stories

How to prepare

  • Build a story bank of five episodes covering a deadline compromise, a review disagreement, a failed release, a prioritisation call and a mentoring or process improvement.
  • Write each story in first person and name your own decision, the cost, and the result before you rehearse it aloud.
  • Add production monitoring and end-to-end ownership details to at least two stories, since candidates are advised to highlight them.

Where candidates lose points

Coding the interval-merge or rate-limiter problem before settling edge cases

Before typing, list the cases aloud: touching intervals such as [1,3] and [3,5], unsorted input, an empty list, nested ranges, and a burst of requests at a window boundary. Decide the rule for each, then write tests from that list.

Presenting a rate limiter that only works inside one process

Ask whether it protects one server or a cluster. Name the algorithm (token bucket or sliding window), make the check-and-update atomic, and say where shared state lives and what happens when that store is unreachable.

Answering the caching question with 'use Redis' and nothing else

State what is cached, its time-to-live, how entries are invalidated and how you prevent a stampede. Then compare Redis (richer data structures, persistence, replication) with Memcached (simple, multi-threaded key-value cache) for the workload.

Naming microservices, queues and databases in the system design round without requirements or failure modes

Start with scope and load, then the data model and the read and write paths. Add the asynchronous path with idempotent consumers, say what happens on a retry, and finish with the first bottleneck at ten times the traffic.

Telling behavioral stories in 'we' with no personal decision and no cost

For each story, state the decision you made, the trade-off you accepted, what it cost, and what you would change. Include production monitoring or end-to-end ownership in at least two stories.

Questions & practice

This category assesses your proficiency in data structures, algorithmic thinking, and writing clean, maintaina

Medium · Coding and Problem Solving

This category assesses your proficiency in data structures, algorithmic thinking, and writing clean, maintainable code under time constraints.

Approach

  1. Before coding, restate the problem, fix input size and constraints, and walk one small example by hand; the constraints tell you the target complexity (around 10^5 elements rules out O(n^2) in most settings).
  2. State the brute-force solution and its complexity, then pick the structure from the reported problems: sort by start and sweep once for calendar conflicts and interval merging (O(n log n)); a min-heap of end times for how many rooms or shifts overlap at once; a hash map of per-user buckets or counters for the rate limiter (average O(1) per request); a deque holding the current time window for the log stream, popping expired entries from the front.
  3. Write readable code: descriptive names, small helper functions for distinct steps, and no clever one-liners that hide the logic; narrate what each block does as you write it.
  4. Test before declaring done: run the example by hand, then edge cases such as empty input, a single element, duplicates, touching intervals and the largest allowed size; finish by stating time and space complexity.

Follow-up

  • The input arrives as a stream and no longer fits in memory. What changes?
  • How would you convince me this solution is correct for every input, not only the examples?
  • Which unit tests would you write for this function, and why those?

Write a function to efficiently merge overlapping time intervals in an appointment scheduling calendar.

Medium · Coding and Problem Solving

Approach

  1. Clarify the representation (start and end times, possibly as datetimes) and whether touching appointments such as 9:00-10:00 and 10:00-11:00 should merge; that decides between start <= lastEnd and start < lastEnd. Normalise times to one time zone (e.g. UTC) before comparing.
  2. Sort the intervals by start time, then scan once: if the current interval starts before the last merged interval ends, set the last end to max(lastEnd, currentEnd); otherwise append the current interval as a new merged block.
  3. Explain why max matters: an interval fully contained in the previous one (9:00-12:00 then 10:00-11:00) must not shrink the merged end.
  4. Complexity is O(n log n) for the sort plus O(n) for the scan. A separate output list takes O(n) space; merging in place in the sorted array needs O(1) extra beyond the sort, and the sort itself may use O(log n) to O(n) auxiliary space depending on the language (Python's Timsort up to O(n), C++ std::sort O(log n)).
  5. Edge cases: empty list, a single interval, unsorted input, identical intervals, and invalid intervals where start > end, which you reject or report rather than silently merge.

Follow-up

  • Insert one new appointment into an already merged, sorted list. Can you do better than re-sorting?
  • Given everyone's appointments, what is the minimum number of rooms needed at once? (Sweep line or a min-heap of end times, O(n log n).)
  • Find the free slots common to several people's calendars within working hours.

Given a stream of incoming logs, write an algorithm to detect anomalous traffic patterns in real time.

Medium · Coding and Problem Solving

Approach

  1. Define the anomaly first: a request rate per key (IP, endpoint, account) or an error rate that departs sharply from that key's recent baseline. Parse each log line into (timestamp, key, status) and keep per-key counts in time buckets, such as a ring buffer of per-second counts, so updating the sliding window is O(1) per event.
  2. Maintain a baseline per key with an exponentially weighted moving average and variance, and flag a bucket whose z-score exceeds a threshold such as 3; mention a median and MAD baseline as a more robust option when spikes would contaminate the average.
  3. Handle stream realities: out-of-order events (accept them within a lateness window and drop or count later ones), cold start for new keys (require a minimum volume before alerting), and flapping (require several consecutive anomalous buckets, or separate trigger and clear thresholds).
  4. When the key space is huge, bound memory: keep exact state only for heavy hitters found with a Space-Saving or count-min sketch (count-min overestimates and never underestimates counts for non-negative updates), and evict idle keys.
  5. State the cost: O(1) amortised time per event and memory proportional to tracked keys times window buckets; compare against seasonal baselines (same hour last week) so normal daily peaks do not alert.

Follow-up

  • Traffic doubles every weekday morning. How do you keep that from triggering alerts?
  • The stream is split across ten machines. How do you detect an attacker whose requests are spread across all of them?
  • How would you measure whether the detector is any good, given you have few labelled incidents?

Optimize a provided snippet of inefficient code that suffers from high time and space complexity.

Medium · Coding and Problem Solving

Approach

  1. Read the snippet and state its current time and space complexity before changing anything; confirm the expected input size so you know which term actually hurts. If tests do not exist, write a few first so the rewrite can be checked against the original behaviour.
  2. Look for the usual culprits: a membership check on a list inside a loop (O(n) each, O(n^2) overall; a hash set makes it average O(1)), nested loops matching two collections (index one side in a hash map), repeated string concatenation in a loop (quadratic with immutable strings; use a builder or join), and the same value recomputed (cache it, memoise, or use prefix sums).
  3. For space, avoid materialising intermediate lists when a single pass or generator will do, update in place with two pointers where order allows, and replace deep recursion with iteration to avoid stack overflow.
  4. Say the trade-off explicitly: a hash set lowers time but costs O(n) extra memory, and memoisation trades memory for speed; pick based on the stated constraint.
  5. Finish by restating the new complexity and, in a real codebase, measuring with a profiler or benchmark on realistic input rather than assuming the change helped.

Follow-up

  • Memory is now the tight constraint and you cannot use the hash set. What do you do instead?
  • How would you prove your optimised version behaves exactly like the original?
  • The function is still slow in production after your change. How do you find out why?

What strategies do you use to optimize database query performance for large, relational datasets under heavy l

Medium · Technical and System Architecture

What strategies do you use to optimize database query performance for large, relational datasets under heavy load?

Approach

  1. Start from evidence, not guesses: find the slow statements from the slow-query log or pg_stat_statements, then run EXPLAIN (ANALYZE, BUFFERS) to see sequential scans, nested loops over large row counts, and estimated versus actual row mismatches that point to stale statistics (fix with ANALYZE).
  2. Index for the query shape: composite indexes with equality columns first and the range or sort column last, covering indexes (INCLUDE in Postgres) to allow index-only scans, and expression indexes when the query filters on a function such as lower(email). Note that every index slows writes and costs storage.
  3. Rewrite the queries that scale badly: replace OFFSET pagination with keyset pagination (WHERE (created_at, id) > (?, ?)), remove ORM N+1 patterns with a join or a batched IN query, select only needed columns, and avoid wrapping indexed columns in functions or implicit casts.
  4. Under heavy load, reduce work reaching the primary: connection pooling (e.g. PgBouncer) so connections do not exhaust memory, read replicas for read traffic (with replication lag in mind for read-your-writes), a cache for hot reads, and precomputed summary tables or materialized views for expensive aggregates.
  5. For very large tables, partition by time or tenant so queries prune partitions and old data can be dropped cheaply; also check lock contention and long-running transactions, which block vacuum and inflate table bloat.

Follow-up

  • A query is fast in staging and slow in production with the same index. What could explain that?
  • You added an index and the planner still does a sequential scan. Why might it, and what do you check?
  • The table is write-heavy. How do you decide which indexes are worth their write cost?

Denormalise tenant onto revisions and backfill it live

Hard · denormalisation · online migration · backfill

resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.

Approach

  1. Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
  2. Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
  3. Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
  4. Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.

Follow-up

  • The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
  • How do you verify the backfill actually finished, given rows are still being inserted while it runs?

This category tests your ability to design scalable systems, make appropriate trade-offs, and explain complex

Medium · Technical and System Architecture

This category tests your ability to design scalable systems, make appropriate trade-offs, and explain complex technical concepts clearly.

Approach

  1. Carry one scheduling example through the whole answer. Requirements: managers build and publish weekly schedules of shifts per tenant, and agents read their own week. Non-functional: reads of this week's schedule far outnumber writes, publishes arrive in bursts before a new week, and agents should see a published change within seconds. Do a quick estimate of tenants, agents and reads per second.
  2. Outline the API and data model first: tenant, schedule (tenant_id, week, status), shift (schedule_id, agent_id, start, end, version). The dominant queries are an agent's shifts for a week and a team's shifts for a week, so index (tenant_id, agent_id, start). A relational store fits because conflict checks and publish need transactions.
  3. Grow the design from the example: stateless API servers behind a load balancer; a cache of each tenant's published week keyed by tenant and week, invalidated on publish; the publish action writes once and puts notification fan-out on a queue so a burst of publishes does not slow the request. Partition by tenant_id only when one primary cannot hold the write volume.
  4. Say each trade-off in terms of the example: the cached week can be stale for a moment after publish; async notifications mean an agent may see the new schedule before the alert arrives; partitioning by tenant makes cross-tenant reporting expensive and a very large tenant becomes a hot shard.
  5. Close with failure modes and operations: primary failover during a publish, a cold cache when everyone opens the new week at once, a growing notification backlog. Alert on p99 read latency, publish error rate and queue depth.

Follow-up

  • Which part of this design becomes the bottleneck first if traffic grows tenfold, and what do you change?
  • Where did you accept eventual consistency, and what does a user see during that window?
  • How would you explain this design and its main trade-off to a product manager in two minutes?

How would you design a real-time event processing pipeline that handles high-concurrency scheduling updates?

Medium · Technical and System Architecture

Approach

  1. Write path: clients send schedule changes to an API that validates them and appends an event (event id, schedule or team id, version, payload) to a durable log such as Kafka. Use a transactional outbox if the change is also written to a database, so the row and the event cannot diverge.
  2. Partition the log by schedule or team id so all updates to one schedule are ordered within a partition; say the cost: a very busy team becomes a hot partition, and ordering across different schedules is not guaranteed.
  3. Handle concurrent edits with optimistic concurrency: each schedule row carries a version, updates are compare-and-set (UPDATE ... WHERE id = ? AND version = ?), and a losing writer gets a conflict to retry or surface to the user rather than silently overwriting.
  4. Assume at-least-once delivery: consumers are idempotent by recording processed event ids or applying version-checked upserts, so a redelivered event is a no-op. Commit consumer offsets only after the side effect succeeds.
  5. Fan results out to connected clients over WebSockets or server-sent events through a pub/sub layer, and monitor consumer lag per partition; a dead-letter topic keeps one malformed event from blocking its partition.

Follow-up

  • Two managers edit the same shift within the same second. Walk through exactly what each of them sees.
  • A bug corrupted derived state for a day. How do you replay events to rebuild it safely?
  • Consumer lag on one partition keeps growing while others are fine. What do you check?

Can you explain how you would architect a fault-tolerant microservice architecture with minimal latency?

Medium · Technical and System Architecture

Approach

  1. Put a timeout on every network call and propagate a request deadline down the chain, so a slow dependency cannot hold threads indefinitely; without timeouts, one stuck service exhausts the pools of every caller upstream.
  2. Retry only idempotent operations (or ones carrying an idempotency key), with exponential backoff plus jitter and a retry budget; unbounded retries turn a partial outage into a retry storm. Add circuit breakers so callers fail fast while a dependency is unhealthy, and bulkheads (separate pools per dependency) to contain the damage.
  3. Keep the synchronous path short for latency: tail latency compounds with fan-out, since if each of N parallel calls is slow with probability p, the request is slow with probability 1 - (1 - p)^N. Move non-essential work (notifications, analytics) to a queue, and consider hedged requests for idempotent reads.
  4. Run multiple instances across availability zones behind a load balancer with health checks, give each service ownership of its own data, and handle cross-service writes with a saga (local transactions plus compensating actions) rather than distributed locks or two-phase commit.
  5. Design graceful degradation (serve cached or partial data when a non-core dependency fails) and make it observable: distributed tracing to find which hop adds p99 latency, plus per-dependency error and latency metrics.

Follow-up

  • A downstream service slows from 50 ms to 5 seconds. Trace what happens to your service with and without timeouts and circuit breakers.
  • How do you keep data consistent when one business action updates three services and the second update fails?
  • How would you find which service is responsible for a p99 latency regression?

How do you approach caching strategies, and when would you choose Redis versus Memcached for a given workload?

Medium · Technical and System Architecture

Approach

  1. Name the patterns and when each fits: cache-aside (application reads cache, falls back to the database and fills the cache) for most read-heavy cases; write-through when reads must see recent writes; write-behind when write throughput matters and some loss risk is acceptable.
  2. Invalidation: combine a TTL as a safety net with deleting the key after the database commit; deleting rather than updating avoids racing writers leaving an old value. Say the residual race: a slow reader can still refill a stale value, which the TTL bounds.
  3. Protect the database from stampedes: request coalescing or a short lock so only one caller rebuilds a hot key, jittered TTLs so keys do not expire together, and stale-while-revalidate for keys where slightly old data is acceptable.
  4. Redis versus Memcached: Memcached is a multithreaded, in-memory key-value store for opaque values with no persistence or built-in replication, sharded by the client; it suits simple, large-volume object caching. Redis adds data structures (hashes, sorted sets, lists, streams), atomic operations and Lua scripts, optional persistence (RDB snapshots, AOF), replication and Redis Cluster; it suits counters, rate limits, leaderboards and anything needing atomic updates or survival across restarts.
  5. Close with sizing and eviction: estimate the working set, pick an eviction policy (Redis maxmemory-policy such as allkeys-lru or allkeys-lfu; Memcached evicts LRU within slab classes), and track hit ratio, since a low hit ratio means the cache adds latency and cost without relieving the database.

Follow-up

  • The cache cluster restarts empty during peak traffic. What happens to the database, and how do you prevent it?
  • One key receives a large share of all reads. How do you handle the hot key?
  • A user updates their profile and immediately sees the old value. Walk through the race that causes it.

Walk through how you would secure sensitive user data across distributed API endpoints.

Medium · Technical and System Architecture

Approach

  1. Encrypt in transit everywhere, including service-to-service traffic: TLS at the edge and mutual TLS (or a service mesh) internally, so a compromised network segment does not expose data and services can authenticate each other.
  2. Authenticate callers with short-lived tokens (OAuth 2.0 / OIDC); when using JWTs, verify the signature, exp and aud claims and pin the accepted algorithm. Then authorise every request at object level: check that the caller may access this specific record, taking tenant id from the verified token, never from the request body, which prevents insecure direct object references.
  3. Encrypt at rest with envelope encryption: each record or object is encrypted with its own data key (DEK), and the DEKs are wrapped by a master key (KEK) held in a KMS. Rotating the master key only means re-wrapping the DEKs; the data is not re-encrypted. Rotating a DEK does require re-encrypting the data it protects, which can be done lazily on the next read or write, with a key version stored per record so old records stay readable until migrated.
  4. Minimise exposure: field-level encryption or tokenisation for the most sensitive fields so most services never see plaintext, return only the fields a client needs, redact PII from logs and error messages, keep secrets in a secrets manager with rotation, and use parameterised queries and input validation against injection.
  5. Make it auditable and resilient to abuse: audit logs of who accessed sensitive records, rate limiting on authentication and data-export endpoints, and alerts on unusual access volumes.

Follow-up

  • A JWT is stolen and is valid for another hour. How do you limit the damage or revoke it?
  • How do you rotate an encryption key without downtime or unreadable old records?
  • How does one internal service prove its identity to another, and how do you limit what it can call?

How would you implement a rate limiter from scratch to protect public-facing API routes?

Medium · Coding and Problem Solving

Approach

  1. Clarify the limit's key (API key, user or IP), the limits per tier, and whether short bursts are allowed; then choose the algorithm. Token bucket (capacity b, refill rate r) allows bursts up to b while enforcing the average rate; fixed window counters are simplest but allow up to twice the limit across a window boundary; a sliding window log is exact but stores every timestamp; a sliding window counter approximates it with two counters.
  2. Implement token bucket lazily: store tokens and lastRefill per key, and on each request add (now - lastRefill) * r tokens capped at b, then admit and decrement if tokens >= 1. In one process, guard the per-key update with a lock or atomic operation so concurrent requests cannot both spend the last token.
  3. For several API servers, keep state in a shared store such as Redis and make the read-modify-write atomic with a Lua script (or for a fixed window, INCR plus an expiry set atomically); a separate INCR then EXPIRE can leave a key that never expires if the process dies in between. Use one clock source, such as Redis server time, to avoid skew between hosts.
  4. Rejected requests get HTTP 429 with a Retry-After header, and many APIs also return remaining-quota headers so clients can back off.
  5. Decide the failure mode when the store is unreachable: fail open (availability, no protection) or fail closed (protection, outage); a common middle ground is a coarse local in-memory limit as a fallback.

Follow-up

  • How do you support different limits for free and paid customers without a code change?
  • The Redis round trip adds latency to every request. How would you reduce it, and what accuracy do you give up?
  • How would you enforce one global limit for a client whose traffic hits several regions?

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

Days 1-5 follow the reported technical stages: the phone screen, deep-dive coding and system design. Days 6-7 cover the product discussion and the behavioral rounds.

Day 1 — Map the loop and set up your stack

  • List the seven reported stages and write down the skill each calls for, marking the two where you are weakest.
  • Choose the one language you will code in for the whole loop, and set up its test runner and a scratch project.
  • From the reported requirements (databases, distributed systems, AWS or GCP, Docker, Kubernetes, event streaming), mark the ones you can speak to from real experience.

Deliverable: A one-page stage map with the weak spots marked, plus a working scratch project with an empty passing test.

Day 2 — Calendar intervals and code optimisation

  • Write interval merging from scratch, then extend it to find free slots and detect conflicts between two calendars.
  • Test touching intervals, nested ranges, unsorted input, empty input and a single interval, and write down the complexity.
  • Take a slow nested-loop snippet and rewrite it with a hash map or sorting, stating time and space before and after.

Deliverable: A merge function with extensions, its test list, and one optimised snippet with before and after complexity.

Day 3 — Rate limiter and log-stream detection

  • Implement a thread-safe token bucket and a sliding-window counter, and write down the memory and accuracy trade-off between them.
  • Outline a Redis-backed per-user limiter that stays correct across a cluster, naming the atomic operation you rely on.
  • Build a rolling-window detector over a stream of log lines that flags a request-rate spike, and test it with bursts and quiet periods.

Deliverable: Two limiter implementations, a one-page distributed-limiter outline, and a streaming detector with tests.

Day 4 — Database performance and caching

  • Create a schedules table with a few million rows, write the slow query, read its plan and fix it with a composite index.
  • Write out when you would choose Redis and when Memcached, including eviction, persistence, invalidation and stampede protection.

Deliverable: A before and after query plan, a Redis versus Memcached comparison page, and the drill attempted end to end.

Day 5 — System design answers out loud

  • Design the real-time scheduling-update pipeline, covering partition key, ordering, idempotent consumers and replay.
  • Design a fault-tolerant microservice setup with timeouts, retries with backoff, circuit breaking and a latency budget.

Deliverable: Three spoken design answers with diagrams, the security checklist, and your notes on the partition drill.

Day 6 — Product-minded decisions and testing

  • Write three engineering decisions from your work as problem, options, choice, cost and measured result.
  • Practise two scheduling product calls aloud: push versus polling for shift changes, and two managers editing the same shift.
  • Write a test plan for a scheduling rule of your choice, covering boundaries, time zones and a fake clock.

Deliverable: Three decision write-ups, two rehearsed product scenarios and one test plan.

Day 7 — Behavioral story bank and full rehearsal

  • Write five first-person stories: deadline compromise, review disagreement, failed deployment and recovery, backlog prioritisation, mentoring or process improvement.
  • Add to each story your decision, its cost and the result, and mark which two include production monitoring.
  • Run a spoken rehearsal that mixes one coding problem, one design question and two stories, and note what to tighten.

Deliverable: A five-story bank in first person and a rehearsal list of fixes.

Ownership, trade-off and recovery stories

The last stage is described as a behavioral/values alignment interview, and candidates also report prompts on technical compromise, code review, a failed deployment, backlog prioritisation and mentoring. Tell each as a first-person story with your decision, its cost and the outcome. One tip candidates pass on is to bring examples of owning a feature end to end and watching it in production.

How do you handle edge cases and write comprehensive unit tests for complex business logic?

Medium · Coding and Problem Solving

Approach

  1. Answer with a method, then one real example. Method: list boundaries from the domain, test the pure rule first, then the integration around it. For a scheduling rule the boundaries are touching intervals, zero-length slots, an empty calendar, duplicates, unsorted input, events spanning midnight, time zones and daylight-saving changes.
  2. Keep business logic in pure functions that take data and return data, and push I/O and the clock to the edges. Then unit tests need no mocks beyond a fake clock, and a table-driven test is one row per rule plus one per boundary.
  3. Add property-style checks where they fit. For interval merging, assert the output is sorted, non-overlapping and covers exactly the same points as the input. These catch cases you did not think to list.
  4. Turn every bug found in review or production into a regression test before fixing it. Say what coverage cannot prove: a high percentage shows lines ran, not that the rule is right, so review the cases against the business rules with the product owner.
  5. Close with the one test you added after a real miss. A story where a bug escaped and your test suite changed afterwards is more convincing than a list of tools.

Follow-up

  • How do you test code that depends on the current time or on time zones?
  • How do you decide you have enough tests for a rule that changes often?
  • What do you do with a flaky test that guards business logic?

This category explores how you navigate team dynamics, manage competing priorities, and handle technical disag

Medium · Behavioral and Collaboration

This category explores how you navigate team dynamics, manage competing priorities, and handle technical disagreements.

Approach

  1. This is a topic area, not one prompt, so prepare three separate stories: one on team dynamics, one on competing priorities, one on a technical disagreement. Do not stretch a single story across all three.
  2. For the disagreement, state both positions fairly, name the evidence that moved the decision (a benchmark, a prototype, incident data), say who made the final call and show that you committed to it afterwards.
  3. For competing priorities, show how you ranked work (user impact, cost of delay, dependencies), who you told about what was dropped, and how the outcome compared with your expectation.
  4. For team dynamics, pick a specific friction such as unclear ownership or a stalled review queue, say what you did first and how the working relationship looked afterwards.
  5. The mistake that sinks these answers is a story in which the other person was simply wrong. Include something you changed your own mind about.

Follow-up

  • What would the other person say about how that was resolved?
  • If you had the same information again, would you decide differently?
  • How did you behave after the decision went against you?

Tell me about a time when you had to make a difficult technical compromise under a tight product deadline.

Medium · Behavioral and Collaboration

Approach

  1. Name the compromise precisely: what was cut or deferred, such as a single region, polling instead of events, a skipped migration, or a feature flag in place of a full rollout. Vague compromises sound like hidden risk.
  2. Describe the constraint and at least two alternatives you weighed, with the cost of each in the terms the deadline cared about, such as a launch date a customer or product manager was depending on.
  3. Show the safety net you put around the shortcut: a feature flag, limited rollout, alerting on the specific failure, a rollback path, and a clear statement to the product manager of what risk they were accepting.
  4. Record the debt: a ticket with an owner and a date, and whether it was repaid. A story that ends at launch is incomplete.
  5. Say what you would refuse to compromise on, such as data correctness or security, to show the limits of your judgement.

Follow-up

  • Did the shortcut get fixed, and how did you make sure it did?
  • How did you explain the risk to the product manager or stakeholder?
  • What compromise would you refuse to make under any deadline?

How do you handle feedback during code reviews, especially when you disagree with a peer's suggestion?

Medium · Behavioral and Collaboration

Approach

  1. Choose one actual review where you disagreed, and separate the three kinds of disagreement: correctness, risk and taste. Correctness and risk get evidence (a failing test, a benchmark, an incident link); taste defers to the team's conventions or an automated linter.
  2. Show how you responded as the author as well as the reviewer: you restated the comment to check you understood it, asked about intent, and replied with data rather than opinion.
  3. Describe the resolution mechanism: a short call, a change that satisfied both concerns, or deferring to the module owner, and that you recorded the decision in the pull request for later readers.
  4. Include a time the peer was right and you changed your code, which proves the answer is about the code and not about winning.
  5. Add how you give feedback: small pull requests, comments that explain why, and a clear split between blocking and optional remarks.

Follow-up

  • What do you do when a senior reviewer asks for a change you believe is wrong?
  • Describe a review comment that changed your mind.
  • How do you review a pull request that is too large to review well?

Describe a situation where a production deployment failed unexpectedly and how you coordinated the recovery.

Medium · Behavioral and Collaboration

Approach

  1. Tell it as a timeline: how the failure was detected (alert, customer report, dashboard), how you scoped the impact, and the explicit decision between rolling back and fixing forward, with the reason for your choice.
  2. Make your coordination role visible: who investigated, who communicated to stakeholders and support, who approved the rollback, and how you kept everyone on one channel. Restoring service came before diagnosing the root cause.
  3. Handle the hard case honestly: if the release included a schema migration, a rollback may not be possible, which is why expand-and-contract migrations and feature flags matter. Say what you did where rollback was not clean.
  4. End with the blameless review: root cause, contributing factors, and concrete changes such as a canary stage, a pre-deploy migration check, or an automated rollback trigger, plus evidence that the change worked later.
  5. Use only real figures for impact (users affected, minutes of degradation). The mistake that sinks the story is a solo hero narrative with no coordination, or one that blames a person.

Follow-up

  • How did you decide between rolling back and fixing forward?
  • What guardrail did you add, and did it ever catch a later problem?
  • What would you do if the change could not be rolled back?

How do you prioritize your engineering backlog when multiple stakeholders request urgent features?

Medium · Behavioral and Collaboration

Approach

  1. Describe a repeatable method before the example: gather each request as a problem rather than a solution, estimate impact, effort and cost of delay, note dependencies, and keep one visible ranked list that stakeholders can read.
  2. Walk through a real case with two urgent, conflicting requests: what you asked each stakeholder, who made the final call (usually the product owner, with your input on cost and risk), and what was deferred.
  3. Show how you said no or not yet: with an alternative, a smaller version, or a date, and that the deferred stakeholder heard it from you directly rather than finding out later.
  4. Mention reserving capacity for reliability and technical debt so that urgent feature requests do not push them out indefinitely.
  5. Avoid answers like 'I do whatever the loudest person asks' or 'I go by gut'; both show no method.

Follow-up

  • What do you do when two senior stakeholders disagree about the order?
  • How do you protect time for technical debt when everything is urgent?
  • How did you tell a stakeholder their request had moved down the list?

Give an example of how you mentored a junior engineer or improved team processes.

Medium · Behavioral and Collaboration

Approach

  1. Pick one clear example, either mentoring one person or improving one process, and be specific about the starting point: what the junior engineer could not yet do, or what was slowing the team down.
  2. List your concrete actions: pairing on a real task, sizing a starter task to their level, writing review comments that explain why, a short written guide for a recurring problem. For a process change: a pull request template, a CI check, or an on-call runbook.
  3. Show a result you can support: the engineer shipping a feature independently, fewer review rounds, or the new process being used without reminders. Use real figures only.
  4. Say how you calibrated when to step in and when to let them struggle, and what you learned about teaching.
  5. The mistake to avoid is doing the person's work for them, or describing a process the team never adopted.

Follow-up

  • What did you do when the person was not improving as expected?
  • How did you get the team to adopt the process change?
  • How did you tell whether it had worked?