cswork / libraryOpen practice workspace

Interview guides · Atomic AI

Atomic AI Software Engineer Interview Guide

Atomic AI Software Engineer interview guide: reported phone screening and onsite stages, coding, design and behavioral questions, and a 7-day prep plan.

The 60-second brief

Atomic AI is described as a company building AI-based products for clients that range from startups to large enterprises. Candidates describe the Software Engineer role as covering algorithms, data structures and software architecture across a product's life, from early concept work through deployment and upkeep, alongside cross-functional teams. The responsibilities candidates list are writing and testing code, taking part in code reviews, joining architecture discussions, turning product and design needs into technical specifications, and resolving production issues. The reports name no specific product or team, so this guide stays with the engineering fundamentals that the reported questions cover.

This guide is for a candidate preparing for the Software Engineer loop at Atomic AI. It covers the two reported stages, all 25 reported questions (technical and domain, coding, system design, behavioral, and problem-solving cases), the behavioral stories that go with them, and a seven-day plan. The requirements candidates describe list Python, Java or C++, algorithms and data structures, Agile or Scrum, and cloud familiarity; machine learning frameworks, CI/CD and front-end frameworks appear only as nice-to-have items.

Candidates report two stages over roughly 2-4 weeks: a phone screening, then onsite interviews described as several conversations on problem solving and teamwork.

Focus areas

  • Python
  • Coding Interview Problem Solving
  • Debugging

Overview

The reported question set is a fundamentals set. It has five groups: technical and domain questions (synchronous versus asynchronous programming, slow SQL, design patterns, debugging, version control), coding questions (a palindrome check, two numbers that add up to a target, binary search, dynamic programming, and explaining the complexity of what you wrote), system design questions (a URL shortening service, database choices, a distributed cache, microservices, web application security), behavioral questions about leading, conflict, mentoring, motivation and prioritizing, and problem-solving cases (scaling a slow application, investigating a performance drop, improving a legacy codebase, prioritizing features, running a retrospective). Nothing in the reports points to a specialized AI question, so the preparation is breadth with clean execution.

Python is the language tag attached to the reported topics, together with coding problem solving, debugging and bug localization. The responsibilities candidates list include resolving production issues, so debugging shows up twice: as an experience question and as scenario questions about a performance drop or a slow application. Build one debugging method (reproduce, narrow, hypothesize, confirm, prevent) and reuse it for the story, the SQL tuning answer and the performance cases. Candidates also name a topic called handling difficult interview questions, so practice saying what you know, what you would check and what you don't know, rather than going quiet.

Split your time by how verifiable each answer is. Coding answers are right or wrong, so write them in Python from a blank file, run them on empty, single-element and duplicate inputs, and state time and space cost aloud. Design and case answers are judged on structure, so rehearse a fixed order: requirements, load, components, data model, failure modes, trade-offs. Behavioral answers fail when they are vague, so write four real stories and map each to several prompts. Machine learning frameworks are listed as nice-to-have, so spend your hours on the fundamentals above unless your own background includes them.

The Atomic AI interview process

Candidates describe two stages, a phone screening and onsite interviews, spread over about 2-4 weeks. The reports do not say which questions belong to which stage, so prepare all five reported groups for both.

Phone Screening

Candidates describe the phone screening as the opening stage, a first check of technical skill and general fit before the onsite. The reports give no format for it, so prepare for two possibilities: a short exchange on fundamentals or a small coding problem, and a conversation about your background. Approach it by keeping Python sharp on small functions and having a clear account of what you built and what you personally did.

What to demonstrate

  • Technical skills at a screening level, as the stage is described
  • Fit for the role, as candidates describe the stage
  • Working fluency in Python, the language tag attached to the reported topics

How to prepare

  • Write the palindrome check, the two-numbers-that-add-up-to-a-target function and binary search in Python from a blank file, then state the time and space cost of each aloud.
  • Prepare a short walk through your last two projects that names your own contribution, the stack and one decision you would change.
  • Have the debugging story and the version-control answer ready, because both are phrased as experience questions.
  • Write down why this role and AI products appeal to you, using something you have actually built or used.

Onsite Interviews

Candidates describe the onsite as multiple interviews centered on problem-solving skills and teamwork. The reports do not say how the five question groups are divided among those interviews, so treat each group as possible in any of them. Approach it by using the same structure each time: restate the problem, name assumptions, work through the solution, then say how you would verify it.

What to demonstrate

  • Problem-solving skills, as the round is described
  • Teamwork and collaboration, as the round is described
  • Consistency across several separate conversations

How to prepare

  • Rehearse the debugging story and the conflict story aloud until each runs in a clear order: situation, your decisions, result, what you changed afterwards.
  • Practice the performance-drop investigation as a spoken checklist: what changed, which metric moved, where the time goes, then the fix and the guard.
  • Run two design questions end to end aloud, for example the URL shortening service and the distributed cache, finishing each with failure modes and trade-offs.
  • Prepare a mentoring example and a prioritization example that show a decision you made and who you told.

Where candidates lose points

Solving the palindrome or target-sum question without settling the spec

Ask about case, punctuation and empty input for the palindrome. For the target-sum question ask whether duplicates count, whether to return values or indices, and whether the same element may be used twice. Then test on those cases before saying you are done.

Stating wrong complexity for your own solution, for example calling a slice-and-reverse palindrome check constant extra space, or a hash-map two-sum linear without mentioning memory

After every solution, state time and space separately, and count hidden costs such as string slicing and copies. Be ready to compare your version with the brute-force one and say what you traded.

Writing binary search with an off-by-one error or an endless loop

Fix the invariant first (for example, the target lies in the closed range from low to high) and write the loop to preserve it. Test on empty, one-element, first, last, missing and duplicate inputs before you describe it as finished.

Telling a debugging story that jumps to the answer, or a design answer that lists buzzwords

For debugging, narrate reproduce, narrow, hypothesize, confirm and prevent, including a false lead. For the URL shortener, cache or microservices questions, name requirements, load, components, failure modes and one trade-off per choice, rather than naming technologies.

Giving behavioral answers in the plural, so the interviewer cannot tell what you decided

Write each story with "I" for your decisions and "the team" only for shared outcomes. Include the result, a number only if you know it, and one thing you would change.

Questions & practice

Explain the difference between synchronous and asynchronous programming.

Medium · Technical / Domain Questions

Approach

  1. Define both: a synchronous call blocks the caller until it returns; an asynchronous call starts the work and hands control back, with the result delivered later through a callback, a future/promise or an await.
  2. Separate concurrency from parallelism: Python's asyncio runs coroutines on one thread's event loop and switches only at await points, so it overlaps I/O waits but does not use more cores.
  3. Say where it pays off: many concurrent I/O waits (HTTP calls, database queries). Fetching 100 URLs one by one costs the sum of the latencies; with asyncio.gather it costs roughly the slowest one, subject to any concurrency cap.
  4. For CPU-bound work, the event loop does not help, and in standard CPython the GIL limits threads, so use processes (ProcessPoolExecutor). Name the classic bug: calling time.sleep or a blocking client inside async def stalls every coroutine. Use asyncio.sleep, an async client, or asyncio.to_thread.

Follow-up

  • What happens if a coroutine calls a blocking library function, and how would you find it?
  • How would you cap outbound requests at 10 concurrent calls and add a timeout to each?
  • For this workload, would you choose threads, asyncio or processes, and why?

Write a function that checks if a string is a palindrome.

Medium · Coding / Algorithms

Approach

  1. Clarify first: case sensitivity, whether to skip spaces and punctuation, Unicode, and what the empty string returns (True: it reads the same both ways).
  2. Two pointers from both ends: skip characters where str.isalnum() is False, compare the lowercased characters, and move inward. That is O(n) time and O(1) extra space.
  3. Mention the one-liner: cleaned == cleaned[::-1] is also O(n) time but builds copies, so it uses O(n) extra memory. Fine for short strings, worth naming the cost.
  4. Unicode: casefold() is safer than lower() (German ß), and characters built from combining marks break naive code-point reversal.
  5. Test cases: '', 'a', 'A man, a plan, a canal: Panama' (True), 'race a car' (False), and a string that is all punctuation such as ',.,' (True after cleaning).

Follow-up

  • Return True if the string can be made a palindrome by deleting at most one character.
  • Find the longest palindromic substring: expanding around each centre is O(n^2), Manacher's algorithm is O(n).
  • How would you check a palindrome too large to hold in memory?

Given a list of integers, write a function to find the two numbers that add up to a specific target.

Medium · Coding / Algorithms

Approach

  1. Clarify: return indices or values, whether exactly one answer is guaranteed, whether duplicates and negatives can appear, and that one element cannot be used twice.
  2. Brute force: check every pair, O(n^2) time, O(1) space. State it, then improve on it.
  3. One-pass hash map: for each index i with value x, look up target - x in seen; if found, return (seen[target - x], i); otherwise store seen[x] = i. O(n) time and O(n) space on average.
  4. Checking before inserting is what stops an element pairing with itself and still handles [3, 3] with target 6. Return None or an empty result when no pair exists.
  5. Alternative when memory matters: sort (value, index) pairs and walk two pointers inward, O(n log n) time. In Java or C++, note that target - x can overflow at int extremes.

Follow-up

  • Return all unique pairs that sum to the target, without duplicates.
  • Extend it to three numbers (3Sum): sort plus two pointers gives O(n^2).
  • What changes if the input is already sorted, or arrives as a stream?

Explain the time complexity of your solution.

Medium · Coding / Algorithms

Approach

  1. Express cost in named input sizes (n, m): nested loops multiply, sequential steps add, keep the dominant term, and always state extra space as well as time.
  2. Call out hidden costs in Python: 'x in list' is O(n) while set and dict membership is O(1) on average; list.pop(0) is O(n) (use collections.deque); slicing copies O(k); sorted() is O(n log n); repeated string += in a loop can go quadratic, so use ''.join.
  3. For recursion, write the recurrence: binary search T(n) = T(n/2) + O(1) gives O(log n); merge sort T(n) = 2T(n/2) + O(n) gives O(n log n); naive Fibonacci is exponential and memoised Fibonacci is O(n). Recursion depth counts toward space.
  4. Distinguish the kinds of bound: list.append is amortised O(1) because the array grows geometrically, hash lookups are O(1) average but O(n) worst case, and quicksort is O(n log n) on average but O(n^2) in the worst case.

Follow-up

  • What does amortised O(1) mean, and why is list.append amortised O(1)?
  • What is the space complexity of your recursive solution, including the call stack?
  • Can you do better than this bound, and what lower bound applies?

Implement a binary search algorithm.

Medium · Coding / Algorithms

Approach

  1. State the precondition: the input is sorted. Iterative version: lo = 0, hi = len(a) - 1; while lo <= hi, mid = lo + (hi - lo) // 2; return mid on a match, otherwise set lo = mid + 1 or hi = mid - 1; return -1. O(log n) time, O(1) space.
  2. Name the invariant: if the target exists, it lies in [lo, hi]. Every iteration shrinks the range, so the loop ends. In Java or C++, (lo + hi) / 2 can overflow int; Python ints do not overflow.
  3. Know the boundary variant: a half-open [lo, hi) with while lo < hi, setting lo = mid + 1 when a[mid] < target and hi = mid otherwise, returns the first position >= target. That matches bisect.bisect_left.
  4. Test cases: an empty array, one element, the target at either end, a target smaller or larger than every element, and duplicates.
  5. Generalise: binary search on the answer works for any monotonic predicate, for example the smallest ship capacity that delivers all packages within D days.

Follow-up

  • Find the first and last positions of a target that appears several times.
  • Search a sorted array that has been rotated at an unknown pivot.
  • How would you search a sorted stream of unknown length (exponential search)?

How would you approach a problem involving dynamic programming?

Medium · Coding / Algorithms

Approach

  1. Recognise the shape: overlapping subproblems plus optimal substructure. Typical signals are counting ways, minimum or maximum cost, or feasibility over prefixes, intervals or subsets.
  2. Work through a concrete example such as coin change: the state in words (dp[i] = fewest coins that make amount i), the recurrence (dp[i] = 1 + min(dp[i - c]) over coins c <= i), the base case (dp[0] = 0, unreachable = infinity), the fill order and where the answer sits.
  3. Choose top-down or bottom-up: memoisation with functools.cache is quick to write, but Python's default recursion limit is about 1000, so a bottom-up table is safer for large inputs.
  4. Complexity is the number of states times the work per state: coin change is O(amount x number of coins), and LCS is O(m x n) time, reducible to O(min(m, n)) space with rolling rows.
  5. Explain why greedy fails: with coins {1, 3, 4} and amount 6, greedy takes 4 + 1 + 1 (three coins) while the optimum is 3 + 3 (two). To return the choices as well as the count, store them and backtrack.

Follow-up

  • Reduce your table's memory from O(m x n) to one or two rows.
  • Return the actual coins or path, not just the optimal value.
  • How would you prove a greedy algorithm is good enough here instead of DP?

How would you optimize a slow-running SQL query?

Medium · Technical / Domain Questions

Approach

  1. Measure before changing anything: get the actual plan with EXPLAIN ANALYZE (PostgreSQL) and look for sequential scans on large tables, estimated rows far from actual rows, sorts spilling to disk and nested loops over large inputs.
  2. Index for the predicate: a composite index with the equality columns first, then the range or ORDER BY column; a covering index (INCLUDE in PostgreSQL) can allow an index-only scan.
  3. Say what makes an index unusable: a function on the column (LOWER(email) = ..., unless you add an expression index), a leading-wildcard LIKE '%x', implicit type casts, or a filter that matches most of the table.
  4. Rewrite the query shape: select only the needed columns, filter before joining, turn a correlated subquery into a join or EXISTS, use keyset pagination instead of a large OFFSET, and drop a DISTINCT that only hides a fan-out join.
  5. Then check the environment: stale statistics (run ANALYZE), lock waits, and for very large tables partitioning or a summary table. Every new index makes writes slower and uses storage.

Follow-up

  • An index exists on the filtered column but the planner ignores it. What are the possible reasons?
  • Why does column order matter in a composite index, and which queries can use (a, b) but not (b, a)?
  • The table is write-heavy. How do you decide whether another index is worth it?

Explain why the owner filter ignores the listing index

Medium · composite index · query plans · sort elimination

The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.

Approach

  1. Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
  2. For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
  3. Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
  4. Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.

Follow-up

  • 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
  • A dashboard runs this for 40 owners in one page load. What changes about the design?

What are some design patterns you have used in your projects?

Medium · Technical / Domain Questions

Approach

  1. Pick two or three patterns you actually shipped, and for each one give the problem, the pattern and what it cost you. A list of definitions is not an answer.
  2. Strategy: swap an algorithm (pricing, ranking, retry policy) behind one interface. In Python this is often just passing a function; in Java it is an interface with implementations.
  3. Adapter: wrap a third-party SDK behind your own interface so it can be mocked in tests and replaced later. Decorator: Python decorators for retries, timing or caching (functools.lru_cache).
  4. Factory plus dependency injection: build clients from config and pass them in rather than constructing them inside business logic, which keeps units testable. Link these to SOLID: open/closed and dependency inversion.
  5. Name the downside: extra indirection and abstraction nobody needed. A Singleton is global mutable state that makes tests order-dependent, which is why module-level injection is usually better.

Follow-up

  • Tell me about a time a pattern made the code worse. What did you change?
  • Why is Singleton often called an anti-pattern, and what would you use instead?
  • How does Strategy look in Python compared with Java?

How would you design a URL shortening service?

Medium · System Design / Architecture

Approach

  1. Scope it: create a short link, redirect, and optionally custom aliases, expiry and click analytics. Redirects far outnumber creations, so design the read path first. Ask for traffic numbers instead of assuming them.
  2. Key generation: base62-encode a unique ID from a counter, with ID ranges handed out in blocks to each app server. Seven base62 characters give 62^7, about 3.5 trillion codes. Alternatives: a truncated hash with collision checks, or random codes behind a unique constraint. Sequential codes can be enumerated, so shuffle them if that matters.
  3. Storage: a key-value lookup by code, either a KV store or a relational table with the code as primary key. Put a cache in front for hot links and replicate for reads.
  4. Redirect status: 301 is cached by browsers, which cuts load but means you lose later clicks; 302 sends every hit to the server, so you can count it.
  5. Keep analytics off the redirect path: write click events to a queue or log and aggregate them asynchronously. Add rate limits on creation, malicious-URL checks and a job that cleans up expired links.

Follow-up

  • How do you handle a custom alias that collides with a generated code?
  • How would you count billions of clicks without slowing redirects?
  • What changes if the service has to run in several regions?

Discuss the trade-offs between different database choices for a web application.

Medium · System Design / Architecture

Approach

  1. Relational (PostgreSQL, MySQL): ACID transactions, joins, constraints and a fixed schema. Reads scale with replicas; writes scale vertically, and sharding is manual work. This is the default for most web apps.
  2. Document stores (MongoDB): flexible schema, and data that is read together is stored together. Joins are weaker, and denormalised copies have to be kept in sync on update.
  3. Key-value (Redis, DynamoDB): fast access by key and easy horizontal scale, but only the queries the key design allows. Wide-column (Cassandra): high write throughput and tunable consistency, but queries must follow the partition key. Search engines (Elasticsearch) handle full-text search, usually as a secondary index rather than the system of record.
  4. Decide from access patterns, consistency needs (money needs transactions), expected scale, operating cost and what the team can run. During a network partition a replicated store has to choose between consistency and availability.
  5. Name the cost of using several stores: keeping them in sync, typically through change data capture or a transactional outbox.

Follow-up

  • When would you move part of the data off PostgreSQL, and what would you watch to decide?
  • A reporting query is slowing down the transactional database. What do you do?
  • With read replicas, how do you guarantee that users see their own writes?

Explain how you would design a distributed caching system.

Medium · System Design / Architecture

Approach

  1. Requirements: get/set/delete with TTL, a latency target, total size and an expected hit ratio. Choose a caching pattern: cache-aside, read-through, write-through or write-behind, and know what each risks.
  2. Partitioning: consistent hashing with virtual nodes, so adding or removing a node remaps about 1/N of keys, whereas hash mod N remaps almost all of them. Replicate each shard; with asynchronous replication a failover can lose recent writes.
  3. Per node: a hash map plus a doubly linked list gives O(1) LRU get and put. Compare LRU, LFU and TTL-only eviction under a memory limit.
  4. Failure modes: a stampede when a hot key expires (fix with single-flight request coalescing, jittered TTLs, early refresh), hot keys (replicate them or add a small in-process cache), and lookups for missing keys (negative caching or a Bloom filter).
  5. Consistency: delete the key after the database commit rather than updating it in place, watch for the race where a stale read repopulates the cache, and keep TTLs as a backstop.

Follow-up

  • A cache node dies. What happens to its keys and to load on the database?
  • Implement the per-node LRU cache with O(1) operations.
  • How would you keep the cache and the database consistent under concurrent writes?

What considerations would you take into account when designing a microservices architecture?

Medium · System Design / Architecture

Approach

  1. Draw boundaries around business capabilities and give each service its own data, with no shared database. For a small team, start with a modular monolith and split only when independent deploys or scaling pay for the overhead.
  2. Synchronous calls (REST or gRPC) need timeouts, retries with exponential backoff and jitter, idempotency keys and circuit breakers. Asynchronous events through a broker decouple services, but delivery is at least once, so consumers must be idempotent.
  3. There are no ACID transactions across services: use a saga with compensating actions, and a transactional outbox so the database write and the event publish cannot diverge.
  4. Operability: service discovery, an API gateway, centralised logs, distributed tracing (OpenTelemetry), per-service CI/CD, versioned backward-compatible APIs and contract tests.
  5. Name the costs: network latency, partial failures, harder debugging and more infrastructure for every new service.

Follow-up

  • How would you split an existing monolith, for example with the strangler fig pattern?
  • An order touches payment and inventory services. How do you keep them consistent when payment fails?
  • A request crossing five services became slow. How do you find the culprit?

Describe how you would ensure the security of a web application.

Medium · System Design / Architecture

Approach

  1. Start with a threat model (assets, entry points, trust boundaries) and use the OWASP Top 10 as a checklist instead of listing tools.
  2. Injection and browser attacks: parameterised queries with bind parameters, never string concatenation; context-aware output encoding and template auto-escaping plus a Content-Security-Policy against XSS; SameSite cookies and anti-CSRF tokens when auth is cookie-based.
  3. Authentication and authorisation: hash passwords with bcrypt, scrypt or Argon2; offer MFA; set cookies HttpOnly and Secure; check on the server, on every request, that the caller may access that object (broken access control or IDOR); apply least privilege.
  4. Transport and data: TLS everywhere plus HSTS, encryption at rest for sensitive fields, secrets in a secrets manager rather than the repo, and rate limiting on login and expensive endpoints.
  5. Supply chain and detection: dependency vulnerability scanning and patching, static analysis in CI, audit logs without secrets or tokens, and alerts on unusual auth activity.

Follow-up

  • JWTs or server-side sessions: how do you revoke access with each?
  • How would you secure a file-upload endpoint?
  • Where exactly should authorisation checks live in the code, and how do you test them?

How would you approach scaling an existing application that is experiencing performance issues?

Medium · Problem-Solving / Case Studies

Approach

  1. Define the symptom in numbers (p95/p99 latency, throughput, error rate), then find the bottleneck with APM traces, a profiler and the database slow-query log before changing any architecture.
  2. Fix the cheap causes first: N+1 queries, missing indexes, unbounded queries, slow synchronous calls in the request path, missing connection pooling. Then cache the hot reads.
  3. Then scale out: a stateless app tier behind a load balancer with autoscaling and sessions in a shared store; read replicas, then partitioning, for the database; slow or bursty work moved onto a task queue.
  4. Verify with a load test shaped like production traffic and compare the same percentile before and after each change.
  5. State the trade-offs: caching adds staleness, replicas add replication lag, and vertical scaling is fast but has a ceiling. Scaling the app tier can simply move the bottleneck onto the database.

Follow-up

  • The database sits at 90% CPU after you fixed the obvious queries. What next?
  • How do you know adding app servers will not just overload the database?
  • How would you run a realistic load test without affecting production users?

Given a set of user requirements, how would you prioritize features for a new product?

Medium · Problem-Solving / Case Studies

Approach

  1. Tie every requirement to a user problem and a measurable goal, then separate what a first usable release needs from what can wait (MoSCoW: must, should, could, won't).
  2. Score the rest with a stated framework: RICE = (Reach x Impact x Confidence) / Effort, or a value-versus-effort grid. Say openly that the inputs are estimates.
  3. Account for dependencies and risk: build the foundations other features rely on first, and test the riskiest assumption early with a spike or prototype. Flag decisions that are hard to reverse, such as the data model.
  4. Validate and iterate: ship the smallest version, instrument the success metric, gather user feedback, and record the trade-offs agreed with product, design and stakeholders.

Follow-up

  • Two stakeholders insist on conflicting features for the same release. What do you do?
  • The deadline moves two weeks earlier. What do you cut, and how do you decide?
  • After launch, how do you tell whether the prioritisation was right?

Discuss how you would investigate a significant drop in application performance.

Medium · Problem-Solving / Case Studies

Approach

  1. Scope it: which metric (latency percentile, throughput, errors), since when, and for which endpoints, regions or users. A sudden drop points to a change; a gradual one points to growth or a leak.
  2. Correlate with changes: deploys, config or feature flags, traffic spikes, dependency incidents, migrations. If a deploy lines up and impact is high, mitigate first (roll back) and diagnose afterwards.
  3. Follow a slow request through distributed traces to the slow span, then check saturation: CPU, memory and GC pauses, exhausted connection or thread pools, disk I/O, lock waits and slow queries, and a falling cache hit ratio.
  4. Confirm the root cause: reproduce it in staging, profile (py-spy and flame graphs for Python), bisect commits, and check that the fix brings the metric back.
  5. Close the loop with a blameless postmortem, an alert on the signal that would have caught it earlier, and a benchmark or regression test.

Follow-up

  • The p50 is unchanged but the p99 doubled. What does that suggest?
  • Memory climbs steadily until the process restarts. How do you find the leak?
  • Nothing was deployed and traffic is flat. Where do you look next?

What strategies would you employ to improve code quality in a legacy codebase?

Medium · Problem-Solving / Case Studies

Approach

  1. Before touching risky code, add characterization tests that pin its current behaviour, so refactors can be checked against them.
  2. Add guardrails gradually: formatting and linting (for Python, ruff or black) and type checking (mypy) enforced on changed files first, so you avoid one huge diff and a frozen codebase.
  3. Refactor in small behaviour-preserving steps tied to feature work. Replace modules behind an interface (strangler fig) rather than attempting a big-bang rewrite.
  4. Prioritise hotspots, the files that change most often and are most complex, and track progress with coverage on touched code, defect rate and review turnaround.
  5. Make it stick: shared review standards, short decision records, and agreed team capacity for the work.

Follow-up

  • How would you convince a manager to give you time for this?
  • A module with no tests and no owner needs a change tomorrow. What do you do?
  • When is a rewrite actually justified?

Describe your approach to conducting a project retrospective.

Medium · Problem-Solving / Case Studies

Approach

  1. Prepare before the meeting: a timeline of the project, data such as planned versus delivered scope, incidents and cycle time, and anonymous input so quieter people are heard.
  2. Run it blameless, focused on systems and process rather than individuals, with a clear structure: what went well, what did not, what to change (or Start-Stop-Continue).
  3. Go from symptoms to causes with 5 Whys, group recurring themes, and vote to pick the few that matter most.
  4. Leave with a small number of action items, each with an owner and a due date, tracked in the backlog. Open the next retrospective by reviewing them.
  5. Back it with a real example: a retrospective you ran and the concrete process change it produced.

Follow-up

  • One person dominates the discussion. How do you handle it?
  • The same problems come up every retrospective. What do you change?
  • How do you run a retrospective on a failed launch with senior stakeholders in the room?

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

Seven days built around the two reported stages and the reported question groups: Python coding fundamentals first, then debugging and data, then design, then behavioral stories.

Day 1 — Map the loop and warm up Python

  • Write the two reported stages and the five reported question groups on one page, and mark your weakest group.
  • In a blank Python file, write the palindrome check and the target-sum function from memory, with tests for empty input, duplicates and the same element used twice.
  • State the time and space cost of both functions aloud.

Deliverable: A one-page loop map with the weakest group marked, plus two tested Python functions with their complexity written next to them.

Day 2 — Binary search and complexity talk

  • Implement iterative binary search, then variants: first occurrence of a duplicate and insertion point for a missing value.
  • Test each on empty, one-element, first, last and missing targets, and write the loop invariant as a comment.
  • Revisit the palindrome and target-sum functions and give a complexity explanation for each that includes hidden costs such as string slicing.

Deliverable: Three tested Python functions (binary search with two variants) with a written complexity note for every solution from days 1 and 2.

Day 3 — Dynamic programming and async versus sync

  • Solve one counting problem and one optimization problem with dynamic programming, first as top-down memoization and then bottom-up, writing the recurrence and base cases before the code.
  • Write a small asyncio example in Python in which a blocking call stalls the event loop, then fix it, and note when threads or processes fit better.
  • Say aloud a short explanation of synchronous versus asynchronous programming that uses your example.

Deliverable: Two dynamic programming solutions with recurrences written out, one asyncio example showing the blocking problem and its fix, and a rehearsed spoken explanation.

Day 4 — Debugging method and the performance drop

  • Write your own debugging story in a fixed order: situation, method, false lead, root cause, fix, guard.
  • Write a checklist for investigating a significant drop in application performance: what changed, which metric moved, where the time goes, rollback options.

Deliverable: A written debugging story and a one-page performance-drop checklist.

Day 5 — SQL tuning, database choices and caching

  • Write a comparison of relational, document and key-value stores for a web application, with one workload where each is the better fit.
  • Sketch a distributed cache: partitioning, eviction, expiry, invalidation, and what happens when a hot key is requested at once.

Deliverable: A slow-query checklist, a database comparison table and a one-page cache design.

Day 6 — System design, security and scaling cases

  • Speak a full URL shortening design aloud: requirements, key generation and collisions, data model, read and write paths, caching, failure modes.
  • Write a list of the considerations for a microservices architecture, naming where a single service would be simpler.
  • Write a web security checklist covering authentication, authorization, input validation, parameterized queries, session handling, secrets and transport.
  • Write a plan for scaling a slow application (measure first, then fix the biggest bottleneck) and for improving a legacy codebase (tests first, then small changes).

Deliverable: A spoken URL shortener design, a security checklist, and one-page plans for scaling a slow application and improving legacy code.

Day 7 — Behavioral story bank and a mixed rehearsal

  • Write four real stories (a led project, a conflict, a mentoring example, a prioritization decision) and map each to the prompts it can answer.
  • Rehearse the version-control answer, the motivation answer, and your approach to a project retrospective aloud.
  • Answer the feature-prioritization question aloud, then finish with one coding problem, one design question and one story back to back.

Deliverable: Four written stories tagged to prompts, plus recorded spoken answers for version control, motivation, retrospectives and feature prioritization.

Stories for debugging, version control and teamwork

Candidates report that the loop includes behavioral and leadership questions, a teamwork emphasis in the onsite, and technical topics phrased as experience questions, namely debugging and version control. Example scenarios candidates report include working with a difficult team member, handling feedback from peers, and conflicting requirements from stakeholders. Prepare a small set of real stories, each with your own decisions and a result, that can be reused across these prompts.

  1. Can you share an experience where you had to work with a difficult team member?

Describe a time when you had to debug a complex issue. What steps did you take?

Medium · Technical / Domain Questions

Approach

  1. Choose a bug whose cause was not obvious: intermittent, data-dependent or spread across services. Give the symptom and its impact in two sentences, then spend the rest on the method.
  2. Narrate the sequence in order: reproduce it, or capture evidence if you cannot; narrow the scope with logs, metrics, tracebacks, a debugger such as pdb, or git bisect to find the commit; form one hypothesis; run the smallest experiment that would disprove it.
  3. Name a false lead you ruled out and how you ruled it out. That shows method, and it is more convincing than a straight line to the answer.
  4. Separate the root cause from the symptom you first saw, then close with the fix, a regression test, how you confirmed in production that it was gone, and the guard you added such as an alert, an assertion or a runbook note.

Follow-up

  • How did you know it was the root cause and not a symptom?
  • What would you have done if you could not reproduce it?
  • What did you add so the next person finds this class of bug faster?

Can you discuss your experience with version control systems?

Medium · Technical / Domain Questions

Approach

  1. Answer with a concrete workflow rather than a list of commands: feature branches, small commits with clear messages, pull requests reviewed before merge, and how your team protected the main branch.
  2. Show judgment on history: when you rebase a private branch, when you merge, when you squash, and why you never rewrite history that others have pulled.
  3. Include one real incident: resolving a hard merge conflict by reading both sides' intent, recovering lost work with reflog, or backing out a bad change on a shared branch with git revert instead of reset.
  4. If you have used another system such as Mercurial or SVN, say so and map the concepts. If your experience is mostly pull requests, say that plainly and cover how CI runs on each push.
  5. Avoid the common failure of naming ten commands with no story; one specific situation shows more than breadth.

Follow-up

  • When would you choose merge, rebase or squash for a pull request, and why?
  • You pushed a broken commit to the shared main branch. What do you do, step by step?
  • How would you find which commit introduced a regression across a hundred commits?

Describe a challenging project you led and how you approached it.

Medium · Behavioral / Leadership

Approach

  1. Pick a project where you set direction and made decisions. Leading by influence counts, but say clearly what you owned; if the answer keeps saying "we", the role you played disappears.
  2. Open with the goal and what made it hard (unclear requirements, a deadline, a risky dependency, an unfamiliar technology) in a few sentences.
  3. Spend most of the time on your decisions: how you scoped it, split it into milestones, handled the main risk, divided work among people, and what you cut and why.
  4. Give the outcome with only numbers you actually know, then one thing you would do differently. Without the result and the lesson, the story stops being a leadership answer.

Follow-up

  • Where did the project come closest to failing, and what did you do?
  • How did you divide the work, and how did you check on it without taking it over?
  • What did you decide not to build, and who disagreed?

How do you handle conflicts with team members?

Medium · Behavioral / Leadership

Approach

  1. Choose a real disagreement about a technical approach, a priority or a review comment, not a clash of personalities with a villain.
  2. Show you understood the other person's position first and could state it fairly, then moved the discussion from opinion to evidence: a small prototype, a benchmark, a written comparison of options.
  3. Describe the conversation order: talk to the person directly first, bring in a third party only if that fails, and keep the focus on the decision, not on the person.
  4. Give the outcome honestly. A good story can end with you losing the argument and committing fully to the other approach, or with a compromise, and it should say how the working relationship looked afterwards.
  5. Avoid claiming you never have conflicts; that answer ends the discussion with nothing to assess.

Follow-up

  • What if the two of you still disagreed after the evidence?
  • Tell me about a conflict where the other person was more senior than you.
  • Would you handle it differently now?

Can you provide an example of how you have mentored others in your team?

Medium · Behavioral / Leadership

Approach

  1. Name one person and the specific gap they had, such as writing testable code, reading a profiler, or scoping tasks, rather than saying you help juniors in general.
  2. Explain how you worked out where they were, then the methods you used: pairing on a real task, code review comments that explain the reason, a sequence of tasks that grew in difficulty, and a regular short check-in.
  3. Show growth you can point to: the mentee shipped a change alone, caught their own bug in review, or took over an area. Say what you stopped doing for them as they improved.
  4. Mention one thing that did not work and how you adjusted. Avoid the common mistake of taking over the keyboard and presenting your own output as their progress.

Follow-up

  • What did you do when the person was not improving?
  • How did you balance mentoring with your own deadlines?
  • How do you give hard feedback in a code review?

What motivates you to perform well in your job?

Medium · Behavioral / Leadership

Approach

  1. Answer with evidence rather than slogans. Name the kind of work that energizes you and back it with two short, real examples, such as tracing a slow system to its cause or seeing a feature used after release.
  2. Connect it to the work candidates list for this role (writing and reviewing code, architecture discussions, resolving production problems) only where it is true for you; do not guess what the company values.
  3. Show that your motivation produces behavior: you volunteer for the hard bug, write the missing test, or ask for review early.
  4. Avoid answers that are only pay, only titles, or a vague wish to learn. Add one honest note on what drains you and how you manage it.

Follow-up

  • Tell me about a time your motivation dropped. What did you do?
  • How do you stay engaged on maintenance or legacy work?
  • What kind of project would you not want?

How do you prioritize tasks when working on multiple projects?

Medium · Behavioral / Leadership

Approach

  1. State a method before the story: list everything in flight, rank by impact, deadline and cost of delay, check dependencies, and decide what to defer or cut.
  2. Use a concrete collision, such as two features due the same week or a production incident landing mid-sprint. Say what you put first and why, and what you deliberately dropped.
  3. Show the communication: you told the people affected, offered options and a new date, and agreed on the trade-off rather than silently working longer hours.
  4. Mention how you protect focus, such as written priorities, time blocks and a clear rule that production problems come before feature work. The mistake to avoid is saying "I work harder", which is not a prioritization method.

Follow-up

  • Two stakeholders each say their project is the top priority. What do you do?
  • A production issue arrives while you are mid-feature. Walk me through your next hour.
  • How did you know afterwards that you chose correctly?