Interview guides · Cleerly
Cleerly Software Engineer Interview Guide
Cleerly Software Engineer interview guide: Python, PyTorch, Postgres and Redis, REST API design and observability prep, with a 7-day plan.
The 60-second brief
Cleerly works on heart disease diagnosis and treatment, combining medical technology with AI. Candidates describe engineering work that spans computational imaging pipelines and enterprise platforms, with AI services running in production and software that sits inside a Quality Management System. The requirements candidates describe for the Software Engineer role list Python, PyTorch, SQL, Postgres, NoSQL, Redis, RESTful web services, cloud deployment, Docker and Kubernetes. A Software Engineer there is reported to build and maintain the infrastructure that serves AI-derived results, including testing mechanisms and observability tooling, and to work daily with AI researchers, product managers and operations teams.
This guide is for experienced engineers preparing for the Cleerly Software Engineer interviews. It covers the one reported question group (five linked questions on AI deployment, distributed debugging, API design for sensitive data, Postgres and Redis behaviour, and testing under regulation), the stack the role lists, and the behavioral stories candidates are asked to bring. No round order has been reported, so the guide prepares you by topic rather than by stage.
No named rounds or round order have been reported for Cleerly. Candidates describe a series of conversations that move from technical screening toward architecture and behavioral discussion, so prepare by topic and confirm the format with your recruiter.
Focus areas
- Python
- PyTorch
- Observability
Overview
The reported question for this loop is really five questions in one: keeping AI models available and reliable in production, debugging a complex distributed-system issue, designing a REST API that carries sensitive medical data, keeping Postgres or Redis consistent and fast under heavy traffic, and testing software that has to meet medical regulatory standards. Each one rewards a concrete answer built from systems you have run, so the most useful preparation is writing out your own production stories with the trade-offs and numbers attached.
The stack matters because the role lists it. Expect to talk about Python services, PyTorch-based models behind them, SQL and Postgres, Redis, Docker, Kubernetes and a cloud platform. Candidates also report observability as a recurring theme: logs, alerts, dashboards and runbooks, and how you found out something was wrong. Prepare to describe the signal you watched, not just the tool you used.
Candidates report two evaluation areas in the written material: productionizing AI (pipeline orchestration, low-latency inference, monitoring, model versioning and reproducibility) and systems architecture (trade-offs among speed, cost and reliability). A good drill for the first is explaining how a model artifact, its code, its dependencies and its input data are pinned so that a past result can be reproduced. For the second, practise saying what you gave up and why.
The domain adds a layer most commercial backends lack. Medical imaging, HIPAA and HITRUST familiarity and computer vision are listed as nice-to-haves, not requirements. If you have no regulated-software background, prepare the nearest equivalent you have: audit trails, change control, access reviews, or release gates, and be honest about the gap. Behavioral questions are reported to be framed around the company's HEART values, so bring stories that show humility, accountability and teamwork with specifics.
Where to focus your preparation
No separate rounds have been reported, so the guide has a single preparation-focus stage. It lists what to confirm with your recruiter and what to rehearse for the reported topics.
Preparation focus
Candidates do not report named rounds or a fixed order for this loop. They describe conversations that start with technical screening and move toward deeper architecture and behavioral discussion, with people from the Computational Imaging or Enterprise Experience teams and cross-functional partners possible along the way. Treat this stage as your preparation baseline: ask the recruiter which stages you will face, whether any involves writing code live, and which team you are interviewing for, then prepare the reported question by topic. Practise answering each of its five parts in roughly the same shape: the situation, the design or diagnosis, the trade-off you accepted, and how you would know it was working.
What to demonstrate
- Depth in the listed stack: Python, PyTorch, Postgres or Redis, Docker and Kubernetes
- Ability to describe production systems you ran, including how you monitored them and handled incidents
- Reasoning about trade-offs among speed, cost, reliability, security and maintainability
- Behavioral stories framed around the HEART values and cross-functional work
How to prepare
- Ask your recruiter for the sequence of stages, whether you will write code, and which team the role sits on
- Write one story each for AI model deployment, a distributed-system debugging incident, an API you secured, and a Postgres or Redis performance or consistency problem
- Prepare a short account of how you would test and release software that has to satisfy a regulator, or the closest equivalent you have done
- Rehearse two or three stories on ambiguity and cross-functional work, each ending with a result you can quantify
Where candidates lose points
Answering the high-availability question for AI models with only "run more replicas"
Cover the whole serving path: pinned model and dependency versions, readiness checks that load the model before taking traffic, a canary or shadow rollout, a rollback path to the previous model version, capacity for compute-heavy requests (queueing or autoscaling), and what the caller sees when inference is degraded.
Telling the distributed-system debugging story as a list of things you tried
Structure it as symptom, scope, hypothesis, evidence, fix, prevention. Name the signal that first showed the problem (an alert, a latency percentile, a trace), how you narrowed it to a service or dependency, how you confirmed the cause, and the alert, test or runbook entry you added afterward.
Designing the medical-data REST API as a list of endpoints and skipping the sensitive-data parts
Address authentication and per-record authorization, opaque identifiers instead of personal data in URLs, no sensitive fields in logs or error bodies, encryption in transit and at rest, an audit trail of who read what, input validation, pagination limits and idempotent writes. Say which of these you would build first and why.
Saying "I would add a Redis cache" without a consistency story
State what the cache holds, how it is invalidated or expires, what happens on a miss stampede, and how stale a read can be before it matters. On the Postgres side, name the actual tools: indexes matched to the query, transaction isolation level, optimistic version checks or row locks for contended writes, and connection pooling.
Reducing regulated-software testing to a unit-test coverage number
Describe tests tied to requirements, integration and regression suites for the model-serving path, validation of model outputs against a fixed reference set, recorded results, and review or approval gates before release. If you have not worked under a regulator, say so and describe the closest controls you have used.
Questions & practice
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?
Diff a projection against the primary without per-row point reads
Hard · reconciliation · range hashing · throttling
The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.
Approach
- Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
- Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
- For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
- Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.
Follow-up
- The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
- Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?
Track a rolling failure rate per destination for circuit decisions
Easy · sliding window · ring buffer · circuit breaker
The egress service delivers about 1,500 webhooks per second across roughly 40,000 destinations, each call bounded by a 10 second timeout. Maintain, per destination, the failure rate over the trailing 60 seconds so a caller can ask before dispatch whether the circuit should open. Attempts arrive as (destination_id, finished_at_ms, outcome). Requirement: amortised O(1) per attempt, with total memory bounded by the destination count rather than by traffic. Give the structure, its exact memory, and the rule that stops a destination with three attempts from opening a circuit.
Approach
- Name the exact-deque version and then reject it as the default. Holding timestamps and advancing a tail pointer past anything older than now minus 60 seconds is a correct two-pointer window at amortised O(1) per attempt, but its memory tracks in-window traffic, so one destination in a retry storm holds hundreds of thousands of entries while thousands of quiet destinations hold none.
- Use a ring of 60 one-second buckets per destination, each bucket a pair of counters for attempts and failures. On an attempt, advance the ring by the elapsed whole seconds, zeroing at most min(elapsed, 60) buckets, then increment the head. That is amortised O(1) with a fixed footprint per destination.
- State the footprint: 60 buckets times two 4-byte counters is 480 bytes of payload per destination, so 40,000 destinations is roughly 20 to 25 MB with per-entry overhead, bounded by the catalogue rather than by the rate. The cost is granularity, since the oldest bucket ages out in whole seconds, which is far tighter than the decision needs.
- Require a minimum sample before the circuit may open. A destination with three attempts and three failures reads as 100 percent and is not evidence; a floor of roughly 20 attempts in the window makes the ratio meaningful, and below that floor use a run of consecutive failures as the trigger instead.
Follow-up
- The fleet is 30 instances and each sees roughly a thirtieth of a destination's traffic. Where does the rate actually live, and what does a per-instance answer get wrong?
- A destination answers in 9.5 seconds and succeeds. It is not failing but it is consuming your per-destination concurrency. What signal should open the circuit here?
Hold a per-tenant active cap against concurrent creates
Hard · write skew · isolation levels · constraints
A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.
Approach
- Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
- Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
- Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
- State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.
Follow-up
- A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
- The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?
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
- 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.
- 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.
- 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.
- 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?
These questions assess your proficiency with the core stack and your ability to apply it to medical-grade soft
Medium · Technical & Domain Expertise
These questions assess your proficiency with the core stack and your ability to apply it to medical-grade software. How do you ensure high availability and reliability when deploying AI models to production? Can you walk us through a time you had to debug a complex issue in a distributed system? What are the primary considerations when designing a RESTful API for sensitive medical data? How do you handle data consistency and performance in a high-traffic environment using PostgreSQL or Redis? How do you approach testing and validation for software that must meet medical regulatory standards?
Approach
- Treat it as five short answers and say so up front: model serving, a debugging story, API design for patient data, Postgres/Redis consistency, and regulated testing. Give each a mechanism and a trade-off, not a list of buzzwords.
- Model serving: ship model weights and code together in one versioned Docker image, run at least two replicas spread across zones, and set a Kubernetes readiness probe that only passes once the weights are loaded and a warm-up inference succeeds. The liveness probe should check something else. Roll out as a canary with automatic rollback on error rate or latency, and keep the previous model version deployable.
- Debugging story: open with the symptom and the metric that showed it. Narrow it down with a correlation or trace ID across services, state the hypothesis and how you reproduced it, then name the fix and the guard you added afterwards (an alert, a regression test or a runbook entry). Without that last step, the story is only about a fire you put out.
- API for medical data: TLS, short-lived OAuth2/OIDC tokens and an object-level authorization check on every record to block IDOR. Use opaque IDs and keep PHI out of URLs, query strings and logs. Audit-log each PHI read. Use idempotency keys on POST and version the API. Postgres/Redis: Postgres is the source of truth; use a version column for optimistic locking (zero rows updated means a conflict), cache-aside with a TTL, and delete the cache key after commit.
- Regulated testing: trace each requirement to the tests that verify it. Pin dependencies so builds are reproducible. For a model, check it against a frozen, versioned validation set with acceptance thresholds before release. Every change goes through review and leaves a record. You can name IEC 62304 as the usual lifecycle standard for medical device software, but don't claim to know the company's own quality process.
Follow-up
- A new model version passes offline validation, but its output distribution shifts once it is in production. How do you detect that, and what triggers a rollback?
- After two concurrent updates to the same record, Redis still serves the old value. Walk through the interleaving that causes this and how you would close the window.
- A client retries a POST that creates a study record after a timeout. How do you make sure the record is not created twice?
Choose what to break when replication lag reaches forty seconds
Hard · replication lag · read-after-write · consistency vs availability
Reads are served from two replicas: 14k requests/second, about 85% absorbed by cache, so roughly 2.1k reads/second reach the database. Writes go to the primary at 1.2k/second. A tenant's backfill drives replication lag from under 100 ms to 40 seconds and it is still climbing. Sessions that have just written are pinned to the primary. Decide, endpoint class by endpoint class, whether to serve stale, fail, or route to the primary, and justify each choice with the load it adds to the primary. Then state what you would have built beforehand.
Approach
- Establish blast radius before cause, because mitigation and diagnosis have different deadlines. The decisive arithmetic is what happens if the database reads move to the primary: 2.1k reads/second on top of 1.2k writes/second roughly triples its operation count, on the node already absorbing the backfill that caused this. Reads and writes are not equal in cost, so treat that as an argument against a blanket move rather than as a capacity model - but it is enough to rule out routing everything to the primary.
- Classify endpoints by what staleness costs, not by how important they feel. Reads whose staleness is invisible - listings, search, counters - stay on the replica and return the watermark so the client can tell. Reads that immediately follow that same session's write keep their primary pin, which is a small bounded slice of traffic rather than the whole 2.1k/second. Reads that feed a decision with a side effect - authorisation, quota, the read half of a read-modify-write - must not be stale at all, because a 40-second-old permission row is the stale-permission failure wearing a different costume; those go to the primary or fail.
- Shed instead of queueing. If the must-be-fresh class alone exceeds the primary's headroom, refuse its lowest-value slice with 503 and a retry-after. A request queued behind a saturated primary holds a connection for a client that has already given up, and the retry storm that follows is what turns degradation into an outage. Bound the connection pool per role so the read fallback cannot consume the write path's connections - that bulkhead is the single decision that determines whether writes survive the next ten minutes.
- Attack the cause in parallel, since it is the one thing that can be stopped. The backfill is the load generator. A backfill that reads replication lag as its throttle signal and pauses above a threshold would have made this a non-event, with batch sizes small enough that each batch's write volume is a fraction of what a replica can apply per second. That is most of the answer to what should have existed beforehand.
Follow-up
- Lag returns to normal in nine minutes. Which mitigation do you remove first, and which one stays permanently?
- A user reports their change did not save, and the write committed. Trace the path that produces that report and name the signal that would have shown it before the report arrived.
One customer endpoint stalls deliveries to every other destination
Medium · bulkhead · timeouts · saturation
The egress service delivers about 1.5k webhooks/second across 40,000 destinations, with a per-destination concurrency cap of 4 and a 10-second connect-plus-read timeout. Throughput falls to 300/second, queue depth climbs, and p99 delivery latency for unaffected destinations goes from 200 ms to minutes, while the error rate barely moves. One tenant holds 900 destination rows whose URLs share a hostname that now answers in 9.5 seconds. Explain the mechanism with the arithmetic, then give the containment in the order you would apply it.
Approach
- Look at saturation before errors. A flat error rate with collapsing throughput says nothing is failing, things are waiting, so the first signal to pull is in-flight request count or pool wait time rather than the error counter. This is the distinction that decides the whole investigation.
- Group in-flight work by resolved host, not by destination id. The cap is keyed per destination row, so 900 rows sharing one hostname buy 3,600 concurrent slots against a single host, each held for 9.5 seconds. The bulkhead was never a bulkhead for that host, and grouping by the wrong dimension is why the dashboard looked healthy.
- Do the arithmetic in both directions. Required concurrency is arrival rate times latency, so 1.5k/second at 200 ms needs about 300 in flight, which is entirely consumed by 3,600 slow slots; conversely whatever concurrency is left sustains rate equals concurrency divided by 9.5 seconds, which is the 300/second you are seeing. Matching both numbers is what promotes this from a plausible story to the mechanism.
- Explain why the circuit breaker never helped. It opens on consecutive failures, and a 9.5-second response inside a 10-second timeout is a success. Slow is not failing, so an error-rate breaker cannot see this; you need a slow-call ratio, a deadline propagated from the caller's remaining budget, or a concurrency limiter.
Follow-up
- The host recovers to 80 ms. How long does the queue take to drain, and what does the drain do to the recovered host?
- Where should the 10-second timeout number actually come from?
Suggested practice checklist
The plan follows the five parts of the reported question and the stack the role lists: REST design, Postgres and Redis, production AI, observability and debugging, then regulated testing and behavioral stories.
Day 1 — Confirm the format and map the stack
- Ask your recruiter for the stages, whether you will write code, and which team the role belongs to
- List the stack from the role requirements (Python, PyTorch, SQL, Postgres, NoSQL, Redis, REST, Docker, Kubernetes, cloud) and rate your recent hands-on depth in each
- Pick two stack areas where you can tell a production story and two where you need refreshing
Deliverable: A recruiter reply on the format and a one-page stack map showing your strong and weak areas.
Day 2 — REST API for sensitive medical data
- Outline an API for patient scan results: resources, authentication, per-record authorization, audit logging, and what never appears in URLs or logs
- Add pagination, rate limits, versioning and idempotent writes, and say which you would ship first
Deliverable: A written API outline with a security checklist, a working idempotency-fingerprint solution and a recorded spoken answer.
Day 3 — Postgres and Redis consistency and performance
- Write a cache-aside design for a read-heavy endpoint, covering invalidation, expiry, stampede protection and acceptable staleness
- Work through the replication-lag practice question and decide what to degrade first
Deliverable: A cache design note, two practice solutions with the transaction or constraint named, and a stated degradation order.
Day 4 — Productionizing AI services
- Describe how you would version a PyTorch model, its code, dependencies and input data so a past result is reproducible
- Sketch a Docker and Kubernetes deployment for an inference service: readiness checks, canary rollout, rollback, and scaling for compute-heavy requests
- Write your answer to the high-availability part of the reported question, including what callers see when inference is degraded
Deliverable: A reproducibility checklist, a deployment sketch and a spoken answer on keeping AI models available.
Day 5 — Observability and distributed debugging
- Write the set of logs, metrics, alerts, dashboards and runbook entries you would put on the inference service from day 4
- Write up one real distributed-system incident as symptom, scope, hypothesis, evidence, fix and prevention
Deliverable: An observability plan, one incident story in six steps and two practice solutions.
Day 6 — Regulated testing and behavioral stories
- Outline a test and release approach for software under medical regulation: requirement-linked tests, regression suites, a fixed reference set for model output, recorded results and approval gates
- Write three stories that show humility, accountability and teamwork, plus one on handling ambiguity across teams, each ending with a result you can quantify
Deliverable: A regulated-testing outline, four written stories with numbers and spoken runs of three behavioral practice questions.
Day 7 — Full dry run
- Answer all five parts of the reported question back to back, aloud, using your written stories and trade-offs
- Write two questions for your interviewers about how the team handles releases, monitoring and on-call
Deliverable: A recorded full run, a completed warm-up solution and two questions to ask.
Stories for the HEART-framed conversations
Candidates report behavioral questions framed around the HEART values (Humility, Excellence, Accountability, Remarkable, Teamwork), in a setting that is mostly remote and often cross-functional. Each story should show a decision you made, what it cost, and a result you can put a number on. Avoid stories that only describe a team's success: say what you personally did.
- Describe a time you debugged a complex issue in a distributed system, from the first signal to the fix and what you changed to stop it recurring.
- Tell a story about a time you were accountable for a production failure: what you owned, what you told others and what you changed.
- Describe a time you worked with AI researchers, product managers or operations teams to turn an ambiguous problem into a shipped solution.
- Give an example of a technical trade-off you made among speed, cost, security or reliability, and what you would decide differently now.
- Describe a time you showed humility: you were wrong about a technical call and changed course.
- Tell a story about raising the bar on quality or release discipline on your team, and the evidence that it worked.
Turn a code review disagreement into a decision
Easy · code review · optimistic concurrency · communication
A colleague's change updates a row with UPDATE resource SET version = version + 1 WHERE resource_id = $1 AND version = $2 and treats an affected-row count of zero as a successful no-op. You read that as a silently lost update; they think returning 200 is friendlier to clients than returning a conflict. Describe how you have handled a review disagreement of this shape: what goes in the comment, when you leave the thread, and who decides. Then write the comment you would leave here, in under 80 words.
Approach
- Sort the disagreement before writing anything. A silently discarded write is a correctness claim about data; the choice between 409 and 412 is taste. Only the first justifies blocking a merge, and saying which one you are doing is most of the value of the comment.
- Make the claim reproducible in the comment itself with an interleaving rather than a principle: A reads version 7, B reads version 7, B commits version 8, A's predicate matches zero rows, A is told it succeeded and A's edit is gone.
- Offer the alternative with its cost attached: return 409 carrying the current version and the revision that won, so the client can re-read and re-apply. Note that automatic retry is not the fix, because a retry re-reads the winner's state and reapplies an intent formed against data that no longer exists.
- Apply an escalation rule you can state: two round trips on the thread, then a call, and the service's owner decides rather than the reviewer. A reviewer who cannot be overruled is a bottleneck with extra steps.
Follow-up
- Where would you put the test that fails if someone reintroduces the swallowed zero rowcount?
- The author says clients cannot handle a 409. How do you check whether that is true?
Tell callers you do not own that their integration breaks
Medium · deprecation · compatibility · stakeholders
A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.
Approach
- Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
- Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
- Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
- Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.
Follow-up
- How would you detect a consumer that reads the field only during a monthly export?
- One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?
Unblock an engineer without taking the keyboard
Easy · mentoring · leases · at-least-once
A teammate has spent two days on a job handler that occasionally writes duplicate rows. They are certain the queue is delivering twice by mistake. You suspect a lease expiring under a slow handler, so the job is running concurrently with itself. Describe how you have unblocked someone in this position: what you asked before offering a hypothesis, what you showed them rather than told them, and what you left them owning. Then say what you would do if their theory turned out to be the right one.
Approach
- Ask before diagnosing, and ask for things answerable from data they already have: the attempt count on the job rows that produced duplicates, the handler's observed duration against its lease expiry, and whether the duplicate rows share a natural key that a unique constraint could have caught.
- Teach the shape rather than the answer. A lease cannot distinguish a dead worker from a slow one, so a handler that outruns its lease is running twice by design, and deploys deliver the other half by killing handlers mid-run on every rollout. Both of their candidate theories produce identical duplicate rows, which is why the evidence has to come from timings rather than from argument.
- Hand over a checklist they execute: a natural key on every write the handler performs so the second copy collides rather than appends, the record of intent written before any external effect, a lease heartbeat while running, and the metric that shows it working.
- Keep ownership with them deliberately. Pair on the first write, then step back; if you finish it yourself you have closed one ticket and left the same person stuck on the next redelivery.
Follow-up
- How would you distinguish a genuine double-delivery from a lease expiry using only the data already stored?
- Their handler calls an external endpoint before recording that it did. What do you tell them to change first?