Interview guides · Altana AI
Altana AI Software Engineer Interview Guide
Altana AI Software Engineer interview guide: four reported rounds, refactoring, debugging and system design questions, and a seven-day plan.
The 60-second brief
Altana AI builds a product it calls the Atlas of Global Commerce, a map of global supply chains. Candidates describe the work as data processing pipelines plus user-facing applications that let government and enterprise users visualize and analyze large trade datasets whose records are linked to one another. In this role, candidates describe writing backend services or data-heavy applications, reviewing teammates' code, joining architecture discussions about data modelling and performance, and turning product requirements into technical specifications. The reported questions fit that picture: one asks about pipeline performance, one about processing shipping events, and one about schemas for related supply chain entities.
This guide is for a candidate preparing for the Software Engineer loop at Altana AI. It covers the four reported rounds and ten reported questions: five about working with existing code (refactoring, throughput, edge cases, architecture trade-offs, pipeline debugging) and five about system design and data (a medical record database, real-time shipping events, distributed consistency, supply chain schemas, SQL versus NoSQL). Candidates do not tie specific questions to specific rounds, so the guide prepares you by question type. The role requirements list Python or Go as examples, distributed systems, SQL and/or NoSQL, and graph or relational data as a plus.
Candidates report four rounds over roughly 3-5 weeks: Initial Screening, Technical Assessments, Live Coding Scenarios and System Design Discussions. Reports also mention back-to-back sessions on one day and possible take-home components.
Focus areas
- Code Review Skills
- System Design
- Coding Challenges
Overview
The ten reported questions fall into two groups. Five are about code that already exists: identify two areas to refactor in a codebase, optimize a function for larger throughput, find the edge cases in an implementation, explain the trade-offs of a module's architectural pattern, and debug a bottleneck in a data pipeline. The wording points to code you are shown rather than a blank editor, so practise reading unfamiliar code quickly. Find the entry point, follow the data, locate the hot loop and the side effects, and check what tests exist before you change anything.
The other five are design and data questions: a large-scale medical record database, a service that maps millions of global shipping events in real time, consistency in a distributed system with high read and write volume, a schema for multi-relational queries across supply chain entities, and SQL versus NoSQL for a given use case. Only some of these concern trade data, so do not assume every design prompt is a shipping prompt. Learn one reusable frame (requirements and scale, data model, write path, read path, failure modes, trade-offs) and adapt it. The role's listed skills are distributed systems and database design, with graph or relational data as a plus, so practise modelling entities and their relationships and querying across several hops.
Candidates also report advice about how you work during the loop. Clarify requirements as soon as something is ambiguous, narrate your assumptions, and be ready to defend your choices and say what you would improve with more time if a review session or take-home appears. Reports mention several technical sessions possibly landing on the same day, so rehearse switching from a coding exercise to a design discussion without a break.
The Altana AI interview process
Candidates describe four stages. Reports do not place particular questions in particular stages, so each stage below says what it covers as reported and how to prepare by question type.
Initial Screening
The first stage is described as an Initial Screening that assesses basic qualifications and fit. No technical content is reported for it, so use it to summarise your background clearly and to learn the format of the later stages.
What to demonstrate
- Basic qualifications, as candidates describe the stage
- Fit, as candidates describe the stage
How to prepare
- Write a short summary of one production system you built: what data it processed, how it was deployed, and one failure you diagnosed and fixed.
- Decide which language you will code in (the role lists Python or Go as examples) and name a database whose schema you designed.
- Ask which stages follow and whether a take-home is included, since candidates report that the format can include one and that sessions can be scheduled close together.
Technical Assessments
Candidates describe Technical Assessments as a series of exercises testing coding ability and architectural intuition, with possible take-home components and, if a project is involved, a review session where you defend your choices. The question types that fit this scope are refactoring, edge-case analysis and architectural trade-offs on existing code. The way to prepare is to treat any code you receive as production code: protect its behaviour first, then improve it.
What to demonstrate
- Coding ability and architectural intuition, as reported for this stage
- Refactoring existing code without changing what it does, which candidates list among general evaluation areas
How to prepare
- Practise the refactoring question on code you did not write: write characterization tests that pin current outputs, then extract functions, remove duplication and rename, re-running the tests after each step.
- Keep an edge-case checklist for data code: empty input, duplicates, out-of-order records, nulls, malformed or oversized records, time zones, and a retry that repeats a write.
- For a take-home, keep the scope small, include tests, and write a short README listing your trade-offs and what you would do next, because reports mention a review session.
Live Coding Scenarios
Candidates describe Live Coding Scenarios where you demonstrate problem-solving while writing code. The coding question types in this guide, such as optimizing a function for more throughput and debugging a slow pipeline, are practised as spoken work here: you reason aloud, write working code, and test it on a small input. Candidates also report that clarifying requirements immediately and narrating your reasoning matter even when the interviewer says little.
What to demonstrate
- Problem-solving skills shown in a live coding setting
- Clear communication of assumptions and reasoning
How to prepare
- Practise optimizing for throughput by measuring first with a profiler or timer, then removing the dominant cost: repeated work inside a loop, per-item database or network calls that can be batched, or a list membership test that a set or dictionary replaces.
- Rehearse a fixed debugging sequence for a slow pipeline: confirm the symptom, time each stage, check queue depth and downstream saturation, change one thing at a time, and re-measure.
System Design Discussions
Candidates describe System Design Discussions as deeper conversations about architecture. The design question types reported for this guide are a medical record database, real-time processing of shipping events, consistency under heavy reads and writes, a schema for multi-relational supply chain queries, and SQL versus NoSQL. Prepare a reusable frame and practise saying each trade-off out loud.
What to demonstrate
- Depth of architectural discussion, as the stage is described
- Scalability, data integrity and technology trade-offs, which candidates list as architectural-design evaluation areas in general rather than for this round alone
How to prepare
- Memorise a design frame and use it every time: requirements and scale, data model, write path, read path, storage choice, failure modes, then trade-offs.
- For the shipping-event service, sketch an ingest log, partitioned consumers, idempotent upserts, a serving store for map queries, and a dead-letter path for malformed events.
- For consistency, be ready to contrast single-leader replication with quorum reads and writes, say where read-your-writes matters, and explain how idempotency keys and an outbox table prevent lost or doubled writes.
- For the schema question, draw entities and relationship tables, write a multi-hop recursive query, and say when a graph database would be worth the extra system.
Where candidates lose points
Refactoring on sight without pinning behaviour
When shown a codebase, say first how you would protect it: write characterization tests that capture current outputs, then refactor in small steps. Name two refactors, give the reason for each (duplication, mixed responsibilities, hidden state), and state the risk each one carries.
Optimizing a function by guesswork
For the throughput question, ask for the input size and a profile or timing before changing anything. Name the dominant cost, fix that one thing, and state the new complexity. A rewrite that was not measured can make the hot path no faster.
Giving a flat list of edge cases
Group edge cases by cause: empty or tiny input, duplicates, ordering and late arrival, malformed values, scale limits, and retries. For each group, say how the code handles it and which test would catch a regression.
Naming a database before stating requirements
For SQL versus NoSQL and the supply chain schema, start with access patterns, relationships, consistency needs and expected volume. Then show when each option wins, and accept that the answer can be a relational store for core records plus another store for one access pattern.
Working silently or waiting to be asked about requirements
Candidates report that clarifying a requirement immediately and narrating your reasoning both matter. Spend the first minutes of any coding or design question restating the problem, listing assumptions aloud, and checking them, then keep talking while you write.
Questions & practice
Given this codebase, identify two areas for refactoring and explain your reasoning.
Medium · Technical Coding and Refactoring
Approach
- Read before judging: find the entry points, the tests that exist and the call graph, then pick the two areas where change is both frequent and risky (high churn, several callers), not the two that merely look ugly.
- Name each problem as a concrete smell with a concrete fix. Take an ingestion or entity-matching module that mixes parsing of messy trade records with matching rules: split it into a pure, testable matching core plus thin parsing and I/O wrappers, so rules can be unit-tested on plain records without a database. Logic copied into three places becomes one helper; a module-level client becomes an injected dependency.
- Justify each choice by what it buys: the pure core can be tested without a database, a bug fix lands in one place instead of three, and an injected client can be faked in tests.
- Make the change safe: write characterisation tests that pin current behaviour first, refactor in small behaviour-preserving steps, and keep refactor commits separate from behaviour changes so review stays easy.
- Say what you would leave alone and why, for example stable code nobody touches, so the answer shows prioritisation rather than a rewrite wish list.
Follow-up
- How would you prove the refactor changed no behaviour?
- If you only had one day, which of the two would you ship first, and why?
- How would you split this into pull requests a reviewer can approve quickly?
What are the potential edge cases in this implementation, and how would you handle them?
Medium · Technical Coding and Refactoring
Approach
- Walk the input classes systematically: empty, null or missing, a single element, duplicates, unsorted input, boundary values (zero, negative, maximum size, off-by-one at range ends) and very large input.
- Cover messy-data cases explicitly: inconsistent casing and whitespace in names, Unicode and encoding issues, missing or extra fields, malformed dates and mixed time zones, and floats used where exact decimals are needed.
- Cover runtime failure modes: an external call timing out, a partial failure halfway through a batch, a retry that writes a duplicate (solve with an idempotency key or upsert), concurrent callers racing on shared state, and a generator input that is silently empty on a second pass.
- Give a handling policy per case rather than one blanket try/except: reject at the boundary with a clear error, normalise and continue, or quarantine the record (for example a dead-letter table) and count it. Never drop records silently.
- Pin each case with a test: parametrised unit tests for the listed cases, plus property-based tests (for example Hypothesis) for invariants such as output length or idempotence.
Follow-up
- Which of these cases would you reject outright, and which would you repair? Where do you draw the line?
- How would you test that this function is safe to retry?
- How would you find out which edge cases actually occur in production data?
Canonicalise a request body into a stable idempotency fingerprint
Medium · parsing · canonicalisation · hashing
idempotency_key.request_fingerprint is a SHA-256 over the method, path and canonicalised body, and a retry whose fingerprint differs must be rejected with 422 rather than served the stored response. Write the canonicaliser. Bodies are JSON up to 256 KB nested at most 32 levels; clients vary key order, whitespace and unicode escaping, and some send 64-bit ids as JSON numbers. Produce a deterministic byte string such that semantically identical bodies match and any semantic difference does not. State your complexity and name two normalisations you refuse to perform.
Approach
- Parse once into a tree, then re-serialise under fixed rules: object keys sorted, array order preserved, one escaping convention, no insignificant whitespace. Parsing is O(n) and sorting keys is O(k log k) per object, so O(n log n) overall with O(depth) stack, and the 32-level cap is enforced during parsing because hostile nesting is how a canonicaliser becomes a stack overflow.
- Sort keys by their UTF-8 bytes and say why the obvious implementation is wrong in some runtimes: a default string comparison that orders by UTF-16 code units places surrogate pairs, meaning code points from U+10000 up, below U+E000 to U+FFFF, which is not UTF-8 byte order, so two services written in different languages disagree on the same document.
- Do not re-encode numbers through a double. IEEE-754 binary64 represents integers exactly only up to 2^53, so normalising a 19-digit id through a float changes it, and 1 against 1.0 cannot be reconciled without deciding whether they are the same value. Preserve the literal token, and require ids as strings at the API boundary if you want them comparable.
- Reject duplicate keys rather than picking one. JSON permits them and parsers disagree, most keeping the last, so any choice you make ties the fingerprint to a parser detail that the code handling the request does not necessarily share.
Follow-up
- A client sends the same logical request with an extra field your API ignores. Same key, different fingerprint, so you return 422. Is that the right answer?
- Where does the fingerprint get computed relative to request decompression and the body-size limit?
How would you structure a database schema to support complex, multi-relational queries across supply chain ent
Medium · System Design
How would you structure a database schema to support complex, multi-relational queries across supply chain entities?
Approach
- Model the core tables: entity (company), facility or location, product (with HS code), shipment and shipment_line, plus a relationship table (from_entity_id, to_entity_id, type, valid_from, valid_to, source_id, confidence) so each edge carries its own time range and provenance.
- Separate raw source records from resolved entities with a mapping table (source_record_id to entity_id). Merging or splitting entities then updates the mapping and never rewrites the original data.
- Index for the joins you expect: both directions on the edge table, (from_entity_id, type) and (to_entity_id, type); (entity_id, shipment_date) for time-bounded lookups; and partition the shipment tables by date.
- Answer multi-hop questions such as tier-n suppliers with WITH RECURSIVE, using a depth limit and cycle detection (a visited path array, or the CYCLE clause in PostgreSQL 14+). If deep traversals dominate, keep the relational tables as the source of truth and project them into a graph database.
- Serve aggregate analytics from a denormalised star schema (a shipment fact table with entity, product and date dimensions) so heavy group-bys do not run against the normalised OLTP tables.
Follow-up
- Write the query for every tier-3 supplier of a given company. How do you stop it looping on cycles?
- Two source records turn out to be the same company. What changes in the data?
- At what point would you move traversal queries to a graph database?
What are the trade-offs between using a SQL versus a NoSQL database for this specific use case?
Medium · System Design
Approach
- Start from the access patterns: ad hoc queries with joins across many related entities and multi-row transactions point to SQL; very high write volume with a few known key-based lookups points to a key-value or wide-column store (DynamoDB, Cassandra); variable-shaped documents read whole point to a document store.
- SQL gives ACID transactions, joins, foreign key and unique constraints, and a cost-based planner. It scales vertically, with read replicas and partitioning, but sharding writes is harder and usually needs a layer such as Citus or Vitess.
- NoSQL stores give built-in horizontal partitioning and flexible schemas, at a cost: few or no joins, data modelled around each query (so denormalised copies must be kept in sync), limited secondary indexes and limited multi-item transactions. A badly chosen partition key creates hot partitions.
- Mention the middle ground: PostgreSQL JSONB with GIN indexes covers semi-structured attributes without a second database. If you do run both, name the operational cost and how the data stays in sync (CDC or an outbox).
- Commit to a choice for the stated use case and say what would reverse it, for example write throughput beyond what one primary can sustain.
Follow-up
- What specific access pattern would make you switch from your choice?
- If the choice turns out wrong a year in, how would you migrate without downtime?
- How do you keep denormalised copies consistent when the source record changes?
How would you optimize this specific function to handle a larger data throughput?
Medium · Technical Coding and Refactoring
Approach
- Measure before changing anything: record a baseline (records per second, p95 latency, memory) on realistic input, then profile (cProfile or py-spy in Python, pprof in Go) to see whether time goes to CPU, I/O wait or allocation.
- Fix the algorithm first: a membership check against a list inside a loop is O(n*m); building a dict or set once makes each lookup O(1) on average and the whole pass O(n+m). Hoist invariant work (regex compilation, config reads) out of the loop.
- If it is I/O-bound, batch: one bulk insert or COPY instead of a row per call, one query with IN or a join instead of N+1 queries, and a connection pool instead of a connection per call.
- Bound memory by streaming: iterate with generators or fixed-size chunks rather than loading the whole input into a list. For parallelism in CPython, threads or asyncio help I/O-bound work; CPU-bound pure-Python work needs processes or vectorised libraries because of the GIL.
- Verify the win: rerun the same benchmark, diff the outputs against the old version on the same input, and check that the downstream system can absorb the higher rate.
Follow-up
- What if the profile shows the time is spent waiting on the database rather than in the function?
- If you parallelise this, how do you keep output order and handle a failed chunk?
- At what point would you move this work out of the request path and onto a queue?
Explain the trade-offs of the current architectural pattern used in this module.
Medium · Technical Coding and Refactoring
Approach
- Name the pattern from the code itself: layered (handler, service, repository), event-driven or pub/sub, a pipeline of stages, strategy or plugin registry, or a shared module inside a monolith. Point at the files that show it.
- Give its real benefits and costs: a repository layer makes storage swappable and testable but adds indirection and leaks once queries need complex joins; event-driven code decouples producers and consumers but brings eventual consistency, duplicate or out-of-order delivery and harder tracing.
- Judge it against how this module actually changes: count how many files one typical feature touches, how hard it is to test a unit in isolation, and whether the indirection costs anything measurable on the hot path.
- End with a conditional recommendation: keep the pattern, or the smallest change that fixes the pain, and state what would make you switch (for example a second storage backend, or a team needing to deploy independently).
Follow-up
- What would make you split this module into its own service?
- How would you migrate away from this pattern incrementally without a big-bang rewrite?
- Where does this pattern make debugging a production issue harder, and how would you compensate?
Design a system to manage and query a large-scale medical record database.
Medium · System Design
Approach
- Pin down the requirements: entities (patient, encounter, observation, medication, provider), the main reads (one patient's timeline, clinician search, population analytics), write volume, retention, and regulatory constraints such as HIPAA in the US, which drive access control and auditing.
- Store the transactional record in a relational database keyed and partitioned by patient_id, with an index on (patient_id, event_time) for timelines. Make records append-only and versioned so a correction adds a version instead of overwriting. Keep large files such as imaging in object storage, referenced by metadata rows; mention FHIR as the interchange format.
- Separate the workloads: feed a search index through change data capture for free-text search, and a warehouse with de-identified data for analytics, so population queries never load the OLTP database.
- Treat security as part of the design: encryption in transit and at rest, access scoped to a care relationship, an audit log of every read as well as every write, and a logged break-glass path for emergencies.
- Use strong consistency for writes to a patient's record, read replicas for scale with read-your-writes for the clinician who just wrote, and point-in-time recovery for backups.
Follow-up
- Two records turn out to be the same patient. How do you merge them without losing history?
- How do you honour a deletion or correction request while keeping the audit trail intact?
- How would analysts query across all patients without seeing identifiers?
How would you design a service that processes and maps millions of global shipping events in real time?
Medium · System Design
Approach
- Size it first: ask for the average and peak event rate, the latency target and what mapping means here (normalising locations, resolving shipper and consignee to known entities, attaching each event to a shipment).
- Ingest into a durable, partitioned log such as Kafka or Kinesis, keyed by shipment or container id so each shipment's events stay ordered. Validate the schema at the edge and route malformed events to a dead-letter queue instead of dropping them.
- Process with stream consumers (Flink, Kafka Streams or plain consumer groups): normalise, enrich against cached reference data, and handle out-of-order arrival with event time and watermarks rather than arrival time. Shipping events come from many carriers and feeds that do not share event ids, so when source ids are unreliable or differ between sources, dedupe on a natural key or content hash, for example (container id, event type, location, event time), and write with idempotent upserts.
- Write current shipment state to a store updated by upsert, keep the full event history append-only in a data lake for replay and analytics, and serve maps and queries from read-optimised views.
- Run it on consumer lag: alert on it, scale consumers up to the partition count, apply backpressure when the sink slows, and reprocess from the log after a bug fix.
Follow-up
- An event arrives two days late. What happens to the shipment's state?
- A carrier resends its entire feed. What stops duplicates?
- How would you reprocess a month of events after fixing a bug in entity matching?
Describe how you would handle data consistency in a distributed system with high read/write volume.
Medium · System Design
Approach
- Decide consistency per operation, not per system: counts, balances and uniqueness checks need linearisable writes, while feeds and dashboards can tolerate staleness. Use CAP (consistency or availability during a partition) and PACELC (latency versus consistency otherwise) to name the trade-off.
- Explain the replication choice: with N replicas, choosing read and write quorums so that R + W > N (for example N=3, W=2, R=2) makes every read overlap the latest acknowledged write. With leader-follower replication, replicas lag, so for read-your-writes I route a session's reads to the leader or wait for a replica that has reached the write's log position.
- Prevent lost updates under concurrent writes with optimistic concurrency: a version column and a conditional update (UPDATE ... WHERE version = ?), retrying on conflict. Avoid last-writer-wins where silently losing a write matters; use merge logic or CRDTs instead.
- Across services, avoid two-phase commit: use a transactional outbox so the state change and its event commit together, idempotent consumers, and sagas with compensating actions for multi-step workflows.
- Keep caches honest: invalidate or version keys on write, set TTLs as a backstop, and monitor replication lag and conflict rates so staleness is measured rather than assumed.
Follow-up
- A user saves a change and immediately sees the old value. How do you fix that?
- One of three replicas goes down. What happens to reads and writes with your quorum settings?
- Why not just use two-phase commit across the services?
How would you debug a performance bottleneck in this specific data pipeline?
Medium · Technical Coding and Refactoring
Approach
- Quantify the symptom first: end-to-end latency, records per second and which output is late, compared with a known-good baseline. Then check what changed: input volume or shape, a deploy, a schema change or an upstream source.
- Instrument per stage: records in and out, time per stage and queue depth or consumer lag between stages. The stage that consumes from the queue that keeps growing is the bottleneck; fix nothing until you know which one it is.
- Inside that stage, classify the cost: CPU saturation, I/O wait, memory pressure (GC pauses, swapping) or lock contention. Profile it with a sampling profiler such as py-spy, and run EXPLAIN ANALYZE on its slow queries to spot sequential scans, missing indexes or N+1 patterns.
- Check for skew: if one partition or key carries most of the data, one worker lags while the rest idle. Compare per-partition timings and look for a hot key or an oversized join.
- Change one thing at a time, re-measure against the baseline, and leave an alert on the metric that exposed the problem so the next regression is caught early.
Follow-up
- Average throughput looks fine but one partition is hours behind. What do you check?
- How do you tell whether the bottleneck is the pipeline itself or the sink it writes to?
- How would you reproduce the slowdown without running experiments on production?
Suggested practice checklist
Seven days built around the four reported rounds and ten reported questions: reading and refactoring code, then performance and debugging, data modelling, streaming and consistency design, and a full dry run.
Day 1 — Map the loop and pick your language
- Write the four reported rounds as a table and mark which question types (refactoring, throughput, debugging, design, data modelling) you feel least ready for.
- Choose the language you will code in; the role lists Python or Go as examples. Write from memory a small batch function that reads records, groups them by key and writes a summary, with tests for empty input and duplicates.
Deliverable: A one-page loop map with your weakest question types marked, plus one working, tested batch function in your chosen language.
Day 2 — Refactoring and architectural trade-offs
- Take a module of 100 to 200 lines you did not write, add characterization tests, then pick two refactors and write the reason and risk for each. Answer the codebase refactoring question aloud using them.
- Pick the architectural pattern in that module (layers, callbacks, inheritance, a shared global) and answer the trade-offs question aloud: what it makes easy, what it makes hard, and when you would keep it.
Deliverable: A passing characterization test set, two written refactors with reasons and risks, and a spoken trade-off summary for one pattern.
Day 3 — Throughput, edge cases and pipeline bottlenecks
- Profile a slow data-processing script (cProfile in Python, pprof in Go), fix the dominant cost, and record before and after numbers for the function-throughput question.
- Write an edge-case table for an ingest function, grouped by cause, with the test that covers each row.
- Say the debugging sequence for a slow pipeline aloud: symptom, per-stage timing, queue depth, downstream saturation, one change at a time.
Deliverable: A before-and-after profile, an edge-case table with covering tests, and a written debugging sequence you can recite.
Day 4 — Schemas for related entities, and SQL versus NoSQL
- Write DDL for a supply chain model of your own: organisations, facilities, shipments and the relationships between them, with keys and indexes that serve your main queries.
- Write a recursive query that finds every upstream supplier of one organisation to a fixed depth, with a guard against cycles, and note where it gets slow.
- Build a table comparing a relational store, a document store and a graph database against the same three access patterns, and answer the SQL versus NoSQL question aloud from it.
Deliverable: A schema with indexes, a working recursive query with a cycle guard, and a three-way comparison table tied to named access patterns.
Day 5 — Real-time shipping events and consistency
- Sketch a pipeline for millions of shipping events: ingest log, partitioned consumers, idempotent upserts, a serving store and a dead-letter path. Mark the partition key and the query it makes expensive.
- List the failure cases (duplicate event, out-of-order event, late event, consumer crash, poison message) and write what handles each.
- Answer the consistency question aloud: where you need strong reads, where eventual is fine, and how idempotency keys or an outbox table prevent lost or doubled writes.
Deliverable: One pipeline diagram, a failure-case table with a handler for each row, and a spoken consistency answer that names where staleness is acceptable.
Day 6 — Medical record database and a coding warm-up
- Design the medical record database: list the access patterns (by patient, by visit, by time range), choose the partitioning, add an audit trail of reads and writes and an access-control layer, and state which queries are slow.
Deliverable: A design page for the medical record database with access patterns, partitioning, audit and access control, plus one timed, narrated solution to the practice exercise.
Day 7 — Dry run and stories
- Run two back-to-back sessions, because candidates report this can happen: a live coding exercise on a data function, then a design question from days 4 to 6 using the frame from round four.
Deliverable: Two completed mock sessions with notes on what you skipped, two rehearsed stories, and two written questions to ask.
Stories to prepare for the Altana AI loop
Candidates report no specific behavioral questions for this loop. What they do describe is communication and process: clarifying requirements early, narrating your reasoning, defending your choices in a review, and acting as a peer on the team. Prepare stories where you can give evidence for those habits in backend or data work.
- Tell me about a time you refactored code you did not write. What did you change, how did you know behaviour was preserved, and what would you do differently?
- Describe a performance problem in a data pipeline or service that you diagnosed. What did you measure, what was the cause, and what changed after the fix?
- Walk me through a decision where you chose between two storage options, such as SQL and NoSQL, and defend the choice you made.
- Tell me about a code review where the reviewer disagreed with your approach. How did you respond and how was it resolved?
- Describe a time you had to clarify an ambiguous requirement before building. What did you ask, and how did the answer change the design?
Argue against a design, lose, and commit anyway
Medium · disagreement · service boundaries · decision records
Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.
Approach
- State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
- Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
- Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
- Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.
Follow-up
- What threshold on that alert would have proved you right, and did anyone ever look at it?
- If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?
Estimate work you have never done and defend the range
Hard · estimation · backfills · expand-contract
You are asked to estimate a change you have never attempted: add a column to a 100-million-row table, populate it, move reads across, and drop the old shape. Give a range with the assumptions that generate it, including batch size, the signal your backfill throttles on, and wall-clock hours, and name the three unknowns that would move the number most. Then describe a real estimate you gave under comparable ignorance: how you expressed its uncertainty, what you committed to, and how wrong you turned out to be.
Approach
- Decompose into independently deployable steps before estimating anything: add the column nullable, write both shapes, backfill in batches, verify, move reads, stop writing the old shape, drop it. That is four deploys spread over days, and the calendar estimate is dominated by them rather than by the loop's runtime.
- Do the arithmetic aloud for the part that has arithmetic in it: batch size times number of batches times per-batch duration, at a write rate the primary can absorb alongside roughly 1.2k writes per second of production traffic. The loop is throttled by replication lag and lock waits, not by how fast it can issue statements.
- Price the schema step by its lock rather than its statement duration. In PostgreSQL an ALTER TABLE taking ACCESS EXCLUSIVE waits for every open transaction on that table while later queries queue behind it, so a millisecond change issued during a thirty-second analytics query stalls that table for thirty seconds. Adding a nullable column with a non-volatile default avoids a rewrite from version 11; a new index wants CREATE INDEX CONCURRENTLY, which cannot run inside a transaction block and leaves an invalid index behind if it fails.
- Express the answer as a range whose endpoints each trace to a stated assumption, then name the cheapest experiment that collapses it, which is almost always running one real batch against the real table and multiplying.
Follow-up
- How do you verify the backfill genuinely finished, given rows written by production traffic while it ran?
- Where does the backfill resume from after a worker is killed mid-batch, and what makes that resume point trustworthy?
Narrate an outage you owned from page to postmortem
Hard · incident response · blast radius · postmortems
Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you cannot name what you measured, choose a different incident.
Approach
- Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
- Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
- Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
- Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.
Follow-up
- What would you do differently in the first five minutes, given the same dashboard and no more information?
- Which follow-up action did you deliberately not take, and why was dropping it the right call?