Interview guides · celebrate
celebrate Software Engineer Interview Guide
celebrate Software Engineer interview guide: reported frontend and CSS questions, Figma-to-code assignment, GitHub code review, and a 7-day prep plan.
The 60-second brief
Candidate reports do not spell out celebrate's product line, so this guide does not guess at it. They describe the Software Engineer role as building user-facing digital product features and owning them from a design concept through to production. The work is reported to start from Figma designs, which engineers turn into working, maintainable applications alongside cross-functional colleagues, and to pass through peer review on GitHub. The requirements candidates describe list modern frontend frameworks, strong CSS and styling knowledge, and full-stack awareness, so the logic behind the interface matters as well as the interface itself.
This guide is for engineers interviewing for the celebrate Software Engineer role, with the heaviest weight on frontend work. It covers the five questions candidates report (frontend technology choices, a complex past feature, debugging under an ambiguous requirement, pull request quality, and design versus performance), the coding assignment and code review that candidates describe, and a seven-day plan that practises each of them.
Candidates do not report named rounds or a fixed order. They describe a remote process that includes a coding assignment, interaction through GitHub and a review of how clearly you explain your decisions.
Focus areas
- Frontend development
- Code reviews
- CSS
Overview
The reported emphasis is narrow and practical: frontend development, CSS, building UI from a Figma design, and code review. Candidates say the CSS you write is read for accuracy against the design and for maintainability, which means the file structure, naming, and reuse of spacing and colour values matter as much as the rendered result. Prepare by rebuilding one real design at several viewport widths, with hover, focus, empty and error states, and by being able to say why each layout decision (flexbox or grid, fixed or fluid sizing) was made.
Code review shows up twice in what candidates describe: as a topic in its own right and as the medium for much of the process, because work is exchanged on GitHub. Written clarity is therefore part of the preparation. Practise writing pull request descriptions that state the goal, the assumptions you made where the requirement was unclear, and what you did not do. Practise answering review comments with either a change or a reasoned reply, never silence.
The five reported questions are conversation prompts rather than algorithm problems: which frontend technologies you know best and why, a complex feature you delivered, how you debug when a requirement is ambiguous, how you keep code quality high during a pull request review, and how you balance a designer's aesthetic with performance limits. Each rewards a specific past project with concrete numbers and trade-offs, so build your answers from your own work.
Candidates also report a coding assignment that takes roughly 4-5 hours of focused work and is meant to be treated as a professional project. No round sequence is reported beyond that, so ask your recruiter what the stages are, what the assignment contains, and which tools you will use, then block out the time in one or two uninterrupted sessions rather than squeezing it in.
Where to focus your preparation
Candidates do not name individual rounds, so this guide treats the loop as one preparation focus covering what they describe. Confirm the actual stages and tools with your recruiter.
Preparation focus
No round names or ordering are reported for celebrate. Candidates describe a remote process that moves from an introductory conversation to a deeper technical assessment and then a conversation about fit, with a coding assignment and GitHub-based review as the hands-on part. They say the CSS you submit is checked against the Figma design and that explaining your reasoning in review matters. Approach it as three skills to rehearse: building UI from a design, discussing your engineering experience, and handling feedback in writing. Ask the recruiter for the stage list, the assignment brief format and the review mechanics before you start.
What to demonstrate
- Translating a Figma design into accurate, maintainable CSS and frontend code
- Clear technical communication in pull request comments and spoken answers
- Judgement on code quality, ambiguity and design versus performance trade-offs
- Ownership of features from concept to production, as shown by your past work
How to prepare
- Rebuild one design from a Figma file at mobile, tablet and desktop widths, then compare it with the original and list every difference you find
- Write a pull request description and review your own diff as a reviewer would, leaving at least five comments you would accept from a colleague
- Prepare spoken answers for the five reported questions, each anchored to one specific project with a trade-off and a result
- Ask the recruiter about stages, assignment format, expected time and tooling, and plan one uninterrupted block of several hours for the assignment
Where candidates lose points
Matching the Figma design at one screen width only, with hard-coded pixel values and no interactive states
Since candidates report that CSS is reviewed for accuracy and maintainability, define spacing, colour and type values once (custom properties or a theme file) and reuse them. Check your build at several widths and build hover, focus, disabled, empty and error states even when the design shows only the default. Compare against the design file one last time before opening the pull request.
Guessing at an unclear requirement and building on the guess without telling anyone
Ask early, as candidates are advised to do, and write the question so it can be answered in one line. When no answer arrives, state your assumption at the top of the pull request description and keep the code easy to change if the assumption is wrong. This also gives you a concrete story for the ambiguous-requirement question.
Treating review feedback as an argument, or replying to comments with a bare 'done' or nothing at all
Review is reported to be a main channel of the process, so reply to each comment with the change you made or the reason you kept the original. Name the trade-off in a sentence, thank the reviewer only when it adds something, and never resolve a thread silently. Rehearse with a friend commenting on your own pull request.
Answering the frontend technologies question with a list of tools and no depth
Pick two or three you know best, tie each to a shipped project, and say what it did better than the alternative and where it hurt. Be ready to explain how you handled state and styling in that project, because those are the areas candidates are told to expect questions about.
Squeezing the coding assignment into a gap and submitting without a self-review or README
Candidates report that it takes roughly 4-5 hours and should be handled like professional work. Schedule it in blocks, leave time at the end to read your full diff, remove dead code, check the layout against the design, and write a short note covering how to run it, what you assumed and what you would improve next.
Questions & practice
Archive a resource graph without breaking live references or recursing
Medium · graph traversal · topological order · tenant isolation
Resources reference other resources within a tenant; for the largest tenant the reference table holds up to 2,000,000 nodes and 8,000,000 edges. Archiving a resource must archive everything reachable from it that nothing outside the set still references, refuse when a live external referrer exists, and terminate when references form cycles, which they legitimately do. Produce the archive order and the refusal list, targeting O(V+E). Say what stops the traversal crossing a tenant boundary, and why recursion is the wrong control structure at this size.
Approach
- Load the subgraph with the tenant predicate on both endpoints of the edge, not only on the side you started from. Scoping the left table alone is the classic cross-tenant leak: one mis-entered edge then pulls another tenant's resources into the traversal and, worse, into the archive.
- Traverse iteratively with an explicit stack. A 2,000,000-node graph can hold a chain deep enough to exhaust a native stack in the low tens of thousands of frames, and that failure is a process crash rather than an error you can return.
- Treat cycles as data rather than corruption: compute strongly connected components with Tarjan in O(V+E) using its own explicit stack, then condense. The condensation is a DAG, so a topological order over it gives the archive order, and every member of a component archives in one transaction because no order within a cycle is valid.
- Decide refusals with reverse edges. A candidate is archivable only if every in-edge originates inside the candidate set, so build the transpose or count in-degrees restricted to the visited set, and emit each blocked resource with the id of the external referrer, which is the only part of the answer an operator can act on.
Follow-up
- The graph is read in one query and the archive writes a minute later. What can change in between, and how do you make the write safe?
- The candidate set is 400,000 resources. Is that one transaction, and if not, what does a half-finished archive look like to a reader?
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?
Identify the heaviest tenants in a five-minute window under memory pressure
Medium · top-k · heavy hitters · streaming
The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.
Approach
- Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
- Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
- State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
- Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.
Follow-up
- The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
- Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?
Write the update path that detects a concurrent edit
Medium · optimistic concurrency · transactional outbox · audit history
resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.
Approach
- One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
- Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
- State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
- Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.
Follow-up
- A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
- Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?
Replace offset paging on the resource feed with keyset
Easy · keyset pagination · composite index · sort order
resource holds resource_id, tenant_id, owner_user_id, title, body_ref, version, status ('draft','active','archived','deleted'), created_at, updated_at, deleted_at, with an index on (tenant_id, status, updated_at DESC, resource_id DESC). The listing endpoint returns active resources for one tenant, newest update first, 50 per page, today with LIMIT 50 OFFSET n. Tenants reach page 400 and rows are created while they read. Write the keyset query, define what the cursor carries and how it is encoded, and say which part of the index each predicate uses. Assume PostgreSQL 16.
Approach
- Name the two failures separately. OFFSET 20000 makes the server produce and discard 20,000 rows, so page cost grows with depth rather than with page size. Independently, any write that changes how many rows sort above the offset moves the window between two fetches, and the direction decides which anomaly you get: an insert lands at the head of updated_at DESC and pushes already-returned rows down past the boundary, so they are returned a second time; a delete above the offset, or a row whose updated_at is bumped above the cursor, pulls rows up and one is never returned at all. Nothing in the response reveals either.
- Write the seek: WHERE tenant_id = $1 AND status = 'active' AND (updated_at, resource_id) < ($2, $3) ORDER BY updated_at DESC, resource_id DESC LIMIT 50. The row-value comparison is one index range rather than a disjunction, and both columns are NOT NULL, which is what makes that comparison well defined.
- Map each predicate onto the index: tenant_id and status are equality on the leading columns, (updated_at, resource_id) is the range, and the ORDER BY matches the index order so no Sort node appears and the scan stops after 50 rows. The DESC in the definition only matters for mixed directions — a plain ascending btree on the same columns is read backwards for this query.
- Put both sort columns in the cursor and nothing the client can tamper with into another tenant: base64 of (updated_at, resource_id), validated server-side, with tenant_id taken from the principal.
Follow-up
- The client asks for 'jump to page 400'. What do you offer instead, and what does the honest version cost?
- Sort order becomes user-selectable across four columns. How many indexes is that, and which would you refuse to add?
Which frontend technologies are you most proficient in and why?
Medium · Technical & Domain Expertise
Approach
- Name one primary stack and defend it with specifics, not a list: for example React with TypeScript, and say why. Cover the component model, typed props that catch contract errors at compile time, and the ecosystem you have shipped with (routing, testing with React Testing Library, data fetching).
- Separate the layers you are proficient in. Cover rendering and framework (React, Vue or Svelte), state (local state, context, a server-cache library such as TanStack Query, a global store only when shared client state demands it) and styling (CSS Modules, Tailwind or CSS-in-JS).
- Show depth in CSS, since candidates report CSS and design implementation as tested topics. Explain Flexbox for one-dimensional layout and Grid for two-dimensional layout, custom properties for design tokens, the cascade and specificity, and container or media queries for responsive breakpoints.
- Give a trade-off for each choice. Runtime CSS-in-JS adds JavaScript work during render, while CSS Modules and utility classes compile to static CSS. A global store adds boilerplate and re-render risk that server-cache libraries avoid for fetched data.
- Close with evidence: one feature where the technology choice mattered, and one limitation you hit and how you worked around it.
Follow-up
- When would you not reach for React, or for a global state library, on a new screen?
- How do you map a Figma design system (colours, spacing, type scale) onto your styling approach so it stays consistent?
- What causes unnecessary re-renders in your framework of choice, and how do you find and fix them?
Can you walk us through a complex feature you delivered in a previous role?
Medium · Technical & Domain Expertise
Approach
- Pick one feature you owned end to end, ideally one that went from a design file to production, and state the problem and its constraints in two sentences before any implementation detail.
- Explain the architecture: the component breakdown, where state lives and why, the data flow between client and API, and the one decision that made the feature complex (async data, optimistic updates, form validation, responsive layout or accessibility).
- Name a trade-off you made with the alternative you rejected, for example optimistic UI against waiting for the server, or a shared component against a one-off one. Give the reason in terms of user impact, effort or maintainability.
- Cover delivery: how you split it into reviewable pull requests, how you tested it (unit, integration, visual checks against the design), and how you rolled it out (feature flag, staged release).
- End with a measurable outcome and one thing you would change. Speak in the first person singular so your own contribution is clear, not the team's.
Follow-up
- What was the hardest bug in that feature, and how did you find it?
- If you had to build it again with half the time, what would you cut and why?
- Where did the implementation differ from the original design, and how did you agree that change with the designer?
What is your process for ensuring code quality during a pull request review?
Medium · Technical & Domain Expertise
Approach
- Describe both sides. As the author: small, single-purpose pull requests; a description stating what changed, why and how to test it; before and after screenshots next to the Figma frame for UI work; and a self-review of the diff before requesting reviewers.
- Automate what a machine can check so humans review design: linting (ESLint, Stylelint), formatting (Prettier), type checking, unit tests and, where available, visual regression snapshots in CI. Branch protection with required checks and approvals on GitHub enforces this.
- Give a review order: correctness and edge cases first, then structure (component boundaries, state placement, duplication), then tests, accessibility (semantic elements, labels, keyboard focus, contrast), then naming and readability.
- Make comments actionable and labelled. Mark blocking issues apart from nits, explain the reason rather than only the instruction, and use GitHub's suggested-change feature for small fixes. When a thread runs long, move to a call and record the outcome in the PR.
- On receiving feedback: reply to every comment, change the code or explain why not, and push follow-up commits that are easy to re-review instead of force-pushing over the history mid-review.
Follow-up
- A reviewer insists on a change you think is wrong. How do you resolve it, and who decides?
- How large should a pull request be, and how do you split a feature that touches many files?
- What do you check specifically when reviewing CSS changes against a design?
How do you balance aesthetic requirements from design with performance constraints?
Medium · Technical & Domain Expertise
Approach
- Measure before trading anything away. Use Lighthouse and the browser Performance panel, and frame the target in Core Web Vitals. 'Good' thresholds are LCP at or under 2.5 s, INP at or under 200 ms and CLS at or under 0.1, measured at the 75th percentile.
- Keep the visual and change the technique. Animate transform and opacity, which can run on the compositor, rather than width, top or margin, which trigger layout. Limit heavy paint effects (large blurs, box-shadows on many elements) and use will-change only on elements about to animate.
- Handle images and fonts. Use responsive srcset and sizes, modern formats (AVIF or WebP), and explicit width and height or aspect-ratio to prevent layout shift. Lazy-load below-the-fold images but never the LCP image (give it fetchpriority="high"). Subset fonts and set font-display to limit invisible text.
- Respect user and device constraints. Honour prefers-reduced-motion, test on a throttled mid-range device, and consider content-visibility: auto for long off-screen sections.
- When a design element really costs too much, bring the designer numbers and an alternative, such as a static gradient instead of a live blur or a shorter animation. Agree the change instead of silently diverging from the Figma file.
Follow-up
- A hero animation drops frames on low-end phones. Walk through how you diagnose it in DevTools.
- Which of the Core Web Vitals is most often hurt by a design decision, and how do you fix it without changing the look?
- How do you keep performance from regressing after launch, for example with budgets in CI?
How do you approach debugging logic when a requirement is ambiguous?
Medium · Technical & Domain Expertise
Approach
- First decide whether it is a bug or a gap in the spec. Reproduce the behaviour, write down what the code does and what each plausible reading of the requirement says it should do. If the code matches one valid reading, the problem is the requirement, not the logic.
- Find the closest source of truth before guessing: the ticket's acceptance criteria, the Figma file and its annotations, existing behaviour elsewhere in the product, and the git history of the code (git log or git blame for the commit and PR that introduced it).
- Ask early with concrete options rather than an open question. For example: 'Should an empty cart show the promo banner? Option A shows it, option B hides it; B matches the mobile screen.' Record the answer where the team can find it, such as the ticket or PR.
- Encode the agreed interpretation as a failing test first, then fix the logic. Use git bisect if it is a regression with a known good commit, which finds the breaking commit in about log2(n) steps.
- Cover edge cases the ambiguity usually hides: empty and null data, boundary values, time zones and locale formatting, and loading or error states that the design file may not show.
Follow-up
- What do you do if the product owner is unavailable and the release is tomorrow?
- How do you stop the same ambiguity from coming back in the next feature?
- Tell me about a time you assumed instead of asking. What did it cost?
Suggested practice checklist
The plan follows the reported emphasis: confirm the process, build from a design, then cover each of the five reported questions, the code review conversation, and a full dry run of the assignment and a spoken round.
Day 1 — Confirm the process and choose your stack story
- Email your recruiter to ask for the list of stages, the format and expected length of the coding assignment, the tools used for review and any language or framework requirement
- List the frontend technologies you know best, pick two, and write the project, the reason you chose each and one drawback you hit
- Say your answer to the frontend technologies question aloud and cut anything that is only a list of tool names
Deliverable: A recruiter email sent, plus a written two-technology answer with one project and one drawback each.
Day 2 — Build a design at several widths
- Choose any design you can open in Figma (or a similar design file) and build one component with a header, an image, text and a button
- Style it for mobile, tablet and desktop, define spacing, colour and type values once, and include hover, focus, disabled and empty states
- Compare your result with the design element by element and write down every mismatch you find and how you fixed it
Deliverable: One working component with a mismatch list, plus a short note on your spacing and colour scheme.
Day 3 — Design fidelity versus performance
- Take your day 2 component and measure it: image sizes and formats, font loading, layout shift, and whether animations use transform and opacity
- Pick three places where the design costs performance, and for each write the change you would propose to a designer and the cost you would accept
- Practise the answer aloud in a form that names a specific project, a trade-off and the result
Deliverable: A three-row table of design cost, fix and trade-off, and a spoken answer to the design versus performance question.
Day 4 — Pull request quality and review conversations
- Write your own review checklist covering correctness, naming, tests, accessibility, CSS reuse and risk, and keep it to one page
- Open a pull request for your day 2 component, review it as a stranger would, and write at least five comments
Deliverable: A one-page checklist and a pull request with five self-review comments and your replies.
Day 5 — Ambiguity, debugging and the logic behind the UI
- Write the sequence you follow when a requirement is unclear: who you ask, what you write down, what you build first, and when you stop to check
- Pick one past bug that came from a misunderstood requirement and write what you did differently afterwards
Deliverable: A written ambiguity routine with one real bug story, and two worked data-layer exercises.
Day 6 — Walk through a complex feature
- Choose the feature from your career that you can describe from design to production, and outline it as context, your part, decisions, trade-offs and result
- Prepare the diagram or screenshot you would describe if sharing a screen, and mark the two decisions you would change today
Deliverable: A one-page feature walkthrough with a result, plus a rehearsed estimate and an unblocking story.
Day 7 — Assignment dry run and spoken round
- Give yourself a small feature spec, build it in a fresh repository in one sitting, and open a pull request with a description, assumptions and a README
- Ask a friend to leave review comments on it and reply to each as you would in the real process
- Answer all five reported questions aloud back to back and note the two weakest answers for another pass
Deliverable: A completed pull request with replies to review comments, and a ranked list of your two weakest answers.
Behavioural preparation
The questions candidates report for celebrate are experience-based, so the behavioral side is about specific past work: a feature you owned, a requirement you had to clarify, and a review conversation you handled. Build each story from a real project with the decision, the trade-off and the outcome, and be ready to explain your reasoning in plain written and spoken language.
- Walk through a complex feature you delivered in a previous role, from the design you received to production: what you owned, the hardest decision, what you cut and what happened after release.
- Describe a time you had to debug something where the requirement itself was unclear: who you asked, what you assumed in the meantime, and how you confirmed you were right.
- Tell the story of a pull request review where you disagreed with a reviewer or an author: what you wrote in the comment, how it was resolved and what you changed afterwards.
- Describe a case where a designer's vision conflicted with performance, how you measured the cost, and what you agreed on.
- Give an example of feedback on your code that changed how you work, and say what you do differently now.
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?
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?
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?