Interview guides · AbelCine
AbelCine Software Engineer Interview Guide
AbelCine Software Engineer interview guide: four reported rounds, answers to reported questions, and a seven-day coding, design and behavioral plan.
The 60-second brief
Candidates describe AbelCine as a company that builds software for filmmakers and other media professionals. Engineers are reported to work on things like camera-system applications, tools for post-production and cloud services that support creative work. In the role you would design, code and test software, take part in code review and fix problems that users or testing report.
This guide is for candidates interviewing for the Software Engineer role at AbelCine. It covers the four reported rounds, the fifteen questions candidates report (conceptual, scenario, design, coding and behavioral), and a seven-day plan. It does not cover other AbelCine roles, and it makes no claim about how any single interviewer runs a session.
Candidates report four rounds over about 3-5 weeks: Initial Screening, Technical Assessments, Interviews with Team Members (the CTO is named, and the CEO possibly) and a Cultural Fit Evaluation.
Focus areas
- Software Engineering (General)
- Technical Questioning
- Technical Depth
Overview
Reported day-to-day work is ordinary engineering: writing and testing software, code review, requirements conversations with product management and customer support, and fixing bugs. The must-have skills candidates describe are Python or JavaScript, a working grasp of software development principles and Git. Cloud services, deployment experience and a film or media background are listed as nice to have, not required. Your preparation should therefore show that you can write and debug code, review a colleague's change, and explain what you did to someone outside engineering.
The reported question list is a mix rather than a single style. It includes conceptual questions (REST versus GraphQL, design patterns, code quality), scenario questions (a slow application, designing a feature from requirements, trading performance against maintainability), two design questions (a simple e-commerce application and data consistency in a distributed system), two coding questions (reversing a linked list and a binary-tree problem) and behavioral questions on version control, teamwork, conflict and prioritization. Candidates describe these as common patterns, not a fixed list, so practise the technique behind each one instead of memorising answers. The coding material is described as live coding sessions or take-home assignments, so be ready for both: write in your strongest language, test your own code on empty, single-item and large inputs, and say your reasoning as you go.
Candidates report meeting team members including the CTO, with the CEO possible, and a separate stage on culture and values. Prepare two or three projects you can explain at several depths: the design and trade-offs for an engineer, the user impact for someone from product or support. Film or media experience is optional; if you lack it, be ready to say how you would learn what a filmmaker or post-production user actually needs, because the role involves refining requirements with product and support.
The AbelCine interview process
Candidates describe these four stages in this order. Reports of each stage are brief, so the advice below leans on the question types candidates report instead of guessing at stage formats.
Initial Screening
Candidates report that the process opens with an initial screening that checks qualifications and fit for the role. Nothing more is reported about its format, so treat it as a first conversation about your background and be ready to line it up against the requirements candidates list for the role.
What to demonstrate
- Whether your background lines up with the requirements candidates list for the role
- Fit for the role as judged from a first conversation
How to prepare
- Write a short account of your career in order, ending with why you want to build software for filmmakers and media professionals.
- Map each listed must-have and nice-to-have requirement to one project where you used it, and note any gap you would address honestly.
- Prepare questions about the team's products and about how engineering works with product management and customer support.
Technical Assessments
Candidates report technical assessments that test technical knowledge and problem-solving. Reports describe the coding side as live coding sessions or take-home assignments. Candidates do not say which stage asks which question, but the reported set spans conceptual questions (REST versus GraphQL, design patterns, code quality), scenario questions (a slow application, designing a feature, performance versus maintainability), design questions (a simple e-commerce application, distributed data consistency) and coding questions (reversing a linked list, a binary-tree problem). Prepare each of those types.
What to demonstrate
- Technical knowledge, as the stage is described by candidates
- Problem-solving and how you reason through a scenario
- Working code, if the format is live coding or a take-home
How to prepare
- Reverse a linked list iteratively and recursively, then solve a binary-tree problem with a recursive and an iterative version, in your strongest language.
- Prepare a crisp REST versus GraphQL comparison and a definition of design patterns with two patterns you have actually used.
- Say aloud how you would diagnose a slow application, starting with measurement, and how you would weigh performance against maintainability.
- Sketch the simple e-commerce design and a data-consistency answer once each, naming the trade-offs you accepted.
Interviews with Team Members
Candidates report meeting team members, with the CTO named and the CEO possible, to discuss experience and fit. Reports do not say which questions come up here, so prepare to go deep on your own work and to talk about it clearly with the people you would work alongside.
What to demonstrate
- Your experience as you describe it, in depth
- Fit with the team you would join
How to prepare
- Pick two projects and write down the decisions, the alternatives you rejected and the results, then practise defending one decision and admitting one mistake.
- As general preparation, practise explaining one technical concept to a listener with no technical background.
- Prepare the version control story and the team-project story so each shows your own contribution.
- Bring questions about how engineers work with product management and customer support.
Cultural Fit Evaluation
Candidates report a final stage on how well you align with the company's culture and values. Behavioral questions are reported across the process, not tied to this stage alone, so prepare specific stories, not slogans.
What to demonstrate
- Alignment with the culture and values candidates describe
- Working style and values, as candidates describe the culture fit stage
How to prepare
- Write outlines (situation, task, action, result) for a team project, a conflict, a prioritization decision and a piece of peer feedback.
- Keep each story about what you did, with a result and one thing you would change.
- Rehearse the stories aloud until each stays short and specific.
Where candidates lose points
Coding silently and losing a pointer when reversing the linked list
Say the plan first: keep previous, current and next, save the next node before you overwrite the link, and return previous. Then test an empty list, a single node and two nodes out loud before you call it done.
Answering the slow-application question with guesses such as add a cache or rewrite it
Start with measurement: reproduce the slowness, profile to find where time goes (database queries, network calls, CPU, memory), change one thing and measure again. Name what you would check first and why.
Over-building the simple e-commerce design or leaving data consistency vague
Start with a modest architecture that fits a simple shop, name where state lives (catalog, cart, orders, payments), and say what must be strongly consistent, such as stock and payment, and what can lag, such as search. Add complexity only when you can state the load that forces it.
Telling teamwork, conflict and prioritization stories in the first person plural
Rewrite each story so the action steps are yours and teammates are named. Check every story contains one decision you made, one result and one thing you would change.
Answering the bug and Git questions as command lists or vague summaries
For the bug story give the symptom, how you reproduced it, the root cause, the fix and the test or monitor that stops it recurring. For Git, anchor each command to a situation, such as reflog after a bad reset or bisect for a regression.
Questions & practice
Write a function to reverse a linked list.
Medium · Coding / Algorithms
Approach
- Iterative three-pointer reversal: prev = None, curr = head. While curr is not None: nxt = curr.next; curr.next = prev; prev = curr; curr = nxt. Return prev as the new head. O(n) time, O(1) extra space.
- Recursive version: reverse the rest of the list, then set head.next.next = head and head.next = None, returning the new head from the base case. It uses O(n) stack space, and in Python a long list hits the default recursion limit of 1000, so the iterative version is the safer default.
- Edge cases to say aloud and test: an empty list, a single node, two nodes, and checking that the old head's next is None afterwards so no cycle remains.
- Test it with helpers that build a list from an array and convert it back, then compare against the reversed array for each edge case.
- Mention the variants: a doubly linked list (swap each node's prev and next), reversing only positions m to n, and reversing in groups of k.
Follow-up
- Reverse only the nodes between positions m and n in one pass.
- Reverse the list in groups of k, leaving any remainder shorter than k in its original order.
- How would you check whether the list has a cycle before reversing it?
Solve a problem involving binary trees and explain your thought process.
Medium · Coding / Algorithms
Approach
- Clarify the input first: a plain binary tree or a binary search tree, whether values can repeat or be negative, the size, and whether it is balanced. Height h is O(log n) when balanced and O(n) when skewed, and that sets the recursion depth.
- Choose the traversal from the question. Depth-first search (pre-order, in-order, post-order) suits path sums, heights and subtree aggregates. Breadth-first search with a queue (collections.deque in Python) suits level-order output, minimum depth and the right-side view.
- Use known patterns. Post-order where children return values to the parent: height, diameter, or a balance check in O(n) by returning -1 for an unbalanced subtree. In-order traversal of a BST yields sorted values. To validate a BST, pass min/max bounds down the tree; comparing each node only with its children is not enough.
- State complexity: O(n) time and O(h) space for recursion. Offer an iterative version with an explicit stack for skewed trees, where Python's recursion limit becomes a problem.
- Trace a small tree by hand before coding, and test an empty root, a single node, a skewed tree and duplicate values.
Follow-up
- Write the same solution iteratively without recursion.
- How does finding the lowest common ancestor differ between a BST and a general binary tree?
- How would you serialize and deserialize a binary tree?
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?
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?
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?
Explain the differences between REST and GraphQL.
Medium · Technical / Domain Questions
Approach
- Contrast the models: REST exposes resources at URLs and uses HTTP verbs and status codes. GraphQL exposes one typed schema, usually behind a single endpoint, and the client chooses the fields it wants, which avoids over-fetching and the extra round trips REST can need.
- Caching: a REST GET can be cached by URL in the browser, a CDN or a reverse proxy. GraphQL queries usually arrive as POSTs to one endpoint, so caching moves into client-side normalized caches or into persisted queries sent as GET.
- Errors and versioning: REST signals failure with status codes and versions with paths like /v2. GraphQL often returns HTTP 200 with an errors array next to partial data. It evolves by adding fields and marking old ones @deprecated rather than cutting a new version.
- Server-side risks of GraphQL: nested resolvers cause N+1 database queries, which I fix by batching with a DataLoader-style loader. Clients can also send arbitrarily deep queries, so I set depth and complexity limits.
- Close with a decision rule: REST for simple CRUD, public APIs, file transfer and heavy HTTP caching. GraphQL when several clients need different shapes of the same data or one API combines several backends.
Follow-up
- How would you stop a client from sending a deeply nested query that overloads the server?
- How would you cache GraphQL responses when every query hits the same endpoint?
- A resolver for a list of items triggers one database query per item. How do you fix it?
Describe a challenging bug you encountered and how you resolved it.
Medium · Technical / Domain Questions
Approach
- Structure the story as symptom and impact, reproduction, narrowing, root cause, fix, prevention. Spend most of the time on how you narrowed it down.
- Show how you got a reliable reproduction: shrink the failing input, pin the environment, and for intermittent failures (race conditions, timezones, memory pressure) add logging or tracing around the suspect path until you can catch a failing run.
- Show systematic narrowing rather than guessing. Use git bisect between a known-good and a known-bad commit, binary-search inputs or config, and state each hypothesis together with the observation that ruled it out.
- Name the root cause precisely, for example shared mutable state read by two threads, or an off-by-one at a pagination boundary. Explain why your fix removes the cause and does not just mask the symptom.
- End with prevention: a regression test that failed before the fix and passes after it, plus any alert or check that would catch the same class of bug earlier.
Follow-up
- How did you confirm the fix worked rather than just making the bug rarer?
- What would have caught this bug before it reached users?
- If you could not reproduce it locally, what would you have done?
What are design patterns, and why are they important in software development?
Medium · Technical / Domain Questions
Approach
- Define them: named, reusable solutions to recurring design problems. The Gang of Four catalogue has 23 patterns in three groups: creational, structural and behavioral.
- Give one concrete example from each group, with the problem it solves. Factory or Builder to centralise object construction. Adapter to wrap a third-party SDK behind your own interface, or Decorator to add behaviour without subclassing. Observer for event subscription, or Strategy to swap algorithms at runtime.
- Explain why they matter: they give reviewers a shared vocabulary, they encode known trade-offs, and patterns built on interfaces and composition make code easier to test because dependencies can be substituted.
- Cover the costs too: applying a pattern where no variation exists adds indirection, and Singleton is effectively global state that makes tests depend on each other.
- Tie them to the languages candidates report for this role. In Python and JavaScript, first-class functions often replace a Strategy or Command class with a function you pass in, and closures or modules can replace a Singleton.
Follow-up
- When is Singleton harmful, and what would you use instead?
- What is the difference between the Strategy and State patterns?
- Show a pattern that a Python or JavaScript language feature makes unnecessary.
How do you ensure code quality in your projects?
Medium · Technical / Domain Questions
Approach
- Answer in layers, starting with automated tests: many fast unit tests, fewer integration tests on real boundaries such as the database or an HTTP API, and a small set of end-to-end tests. All of them run in CI on every pull request.
- Static checks: a formatter and linter (for example Black or Ruff for Python, Prettier and ESLint for JavaScript) plus type checking (mypy or TypeScript). Run them in pre-commit hooks and in CI so style never comes up in review.
- Code review: keep pull requests small, write descriptions that explain why, and spend review time on design, edge cases and missing tests rather than formatting.
- Measure, without gaming the numbers: treat coverage as a floor on critical modules, not a target, and track escaped defects and flaky tests. A flaky test gets quarantined and fixed, not retried until green.
- Give a real example of a gate you added or a quality problem you fixed, and its cost in CI time or review effort.
Follow-up
- How do you deal with a flaky test that blocks merges?
- How would you raise quality on a legacy codebase with almost no tests?
- What coverage threshold would you set, and why that number?
How would you approach optimizing a slow-running application?
Medium · Problem-Solving / Case Studies
Approach
- Define slow before touching anything: which operation, for which users, and what the p50, p95 and p99 latency or the throughput is against a target. Get a baseline you can reproduce.
- Measure where the time goes: a profiler (cProfile or py-spy for Python, the Chrome DevTools Performance panel for JavaScript) and request traces that split time between CPU, database, network and disk.
- Check the usual suspects layer by layer. Database: missing indexes and N+1 queries, confirmed with EXPLAIN ANALYZE. Application: quadratic loops, repeated work you could cache. Network: chatty sequential calls you could batch. For media-heavy software, such as a slow upload or transcode of a large video file, look for whole files loaded into memory: stream or chunk the I/O and move the processing to a background or parallel job.
- Fix the largest share first. By Amdahl's law, removing a step that takes 10% of the time saves at most 10%. Re-run the same benchmark after each change and keep one change per measurement.
- Name the trade-offs of each fix: a cache brings invalidation, staleness and memory cost; denormalising data brings write complexity. Add a performance regression check so the gain does not quietly disappear.
Follow-up
- The profile shows time spread evenly with no hotspot. What next?
- The slowdown appears only under production load. How do you investigate it?
- How would you show that the improvement holds in production?
Given a set of requirements, how would you design a new feature for one of our products?
Medium · Problem-Solving / Case Studies
Approach
- Restate the requirements and ask questions: who the users are, the core use cases, and the non-functional needs (latency, data volume, offline use, permissions). Agree on acceptance criteria and what is out of scope. If the feature handles large media files, ask about file sizes, resumable uploads and processing time.
- Outline the design: data model changes, API contracts, the user flow including empty and error states, and where the logic lives. Check compatibility with existing data and older clients.
- Plan the rollout: put the feature behind a flag, change the schema in expand-then-contract steps so old and new code can run side by side, and decide which metrics will show whether it works.
- List edge cases and tests: authorisation, concurrent edits, failure of a dependency, very large or malformed input. Map each acceptance criterion to a test.
- State the trade-offs: the smallest version that meets the acceptance criteria, which alternatives you rejected and why, and what you would build next.
Follow-up
- How would you ship this without breaking existing clients or data?
- If the deadline were halved, what would you cut and what would you keep?
- After launch, how would you tell whether the feature is working for users?
Describe a situation where you had to make a trade-off between performance and maintainability.
Medium · Problem-Solving / Case Studies
Approach
- Choose a concrete case with numbers, for example replacing a readable per-row ORM loop with one bulk SQL query, adding a cache, denormalising a table or vectorising a computation, and state the latency or cost before and after.
- Show that a measurement drove the decision: a profile showed the code was on the hot path, and a requirement or SLA made the gain necessary. Optimising without that evidence is the wrong half of this trade-off.
- Explain how you limited the maintenance cost: the optimised code sits behind a clear interface, a comment explains why it exists, the simple version stays as a test oracle, and a benchmark guards the gain.
- Add the opposite judgement: a micro-optimisation you turned down because the code was not on the hot path, which shows you weigh both sides.
- Close with the outcome over time, including any later maintenance cost, and whether you would make the same call again.
Follow-up
- Would you make the same decision today? What would change your mind?
- How did you stop the next engineer from undoing the optimisation by accident?
- When is optimising before you have measurements justified?
Design a simple e-commerce application. What architecture would you choose and why?
Medium · System Design / Architecture
Approach
- Fix the scope: catalog browsing and search, cart, checkout, payment, orders and inventory. Separate the read-heavy catalog, which tolerates staleness, from orders and payments, which must be correct. To fit a media-industry shop, add a line on what changes: high-value equipment may need approval-gated orders, and large product images or video belong on the CDN.
- Choose an architecture and justify it. A modular monolith with clear module boundaries over a relational database such as PostgreSQL gives transactions for orders and inventory at low operating cost. Split out services later along those boundaries when team size or scaling needs justify it.
- Read path: images and static assets on a CDN, product pages cached in Redis with explicit invalidation on edits, and a search index such as Elasticsearch or OpenSearch fed from the catalog database.
- Write path for checkout: reserve stock with a conditional update (UPDATE inventory SET qty = qty - n WHERE sku = ? AND qty >= n, and check rows affected), create the order as pending, call the payment provider with an idempotency key, and confirm the order from the provider's webhook. Release expired reservations with a scheduled job.
- Move side effects onto a queue (confirmation emails, fulfilment) and make webhook and queue consumers idempotent, since both can deliver the same event more than once.
Follow-up
- How do you avoid overselling the last unit during a flash sale?
- The payment succeeded but writing the order failed. What happens, and how does the system recover?
- At what point would you split the monolith into services, and which piece would go first?
How would you handle data consistency in a distributed system?
Medium · System Design / Architecture
Approach
- Start from the invariant: which data needs strong consistency (balances, stock counts) and which can be eventually consistent (feeds, view counts). Under CAP, a network partition forces a choice between consistency and availability, and PACELC adds the latency-versus-consistency trade-off when there is no partition.
- Within one database, use transactions. Across services, avoid two-phase commit because the coordinator can block participants. Use a saga with compensating actions instead, and a transactional outbox so the state change and its event are committed atomically.
- Assume at-least-once delivery and make consumers idempotent by recording processed event ids. Exactly-once delivery across a network is not achievable in general, but exactly-once effects are, through deduplication.
- For replicated stores, a strict quorum with R + W > N makes every read set overlap the latest write. For read-your-writes, read from the leader after a write or use session tokens. For concurrent updates, use optimistic locking with a version column or conditional writes.
- Cover conflict resolution and detection: last-write-wins silently loses updates, while version vectors or CRDTs keep or merge concurrent writes. Add reconciliation jobs that detect drift between systems.
Follow-up
- Walk through a saga for an order where payment fails after stock was reserved.
- Why is exactly-once delivery hard, and what do you build instead?
- How does replication lag show up to a user, and how would you hide it?
Exports duplicate a row range about once a week
Hard · leases · clock skew · idempotency
Roughly once a week an export writes a file containing a duplicated range of rows. The affected job_run rows show attempt = 1, status = succeeded, one started_at, and a lease_owner naming a different host from the one whose logs show the job starting. Leases last 30 seconds and are heartbeated every 10 from inside the handler; lease_expires_at is computed on the worker and compared against the database's now(). Find the mechanism, and give a fix that holds even if you cannot fix the clocks.
Approach
- Start from the fact that eliminates the obvious answer. attempt = 1 means no retry was recorded, so this is not a re-run after failure; two workers ran the same row concurrently and the takeover path never touched the counter. lease_owner naming a host other than the one that started the job is the same statement from the other side.
- Enumerate the mechanisms that cause a premature takeover, then find the signal that separates them. Either the lease genuinely expired because the heartbeat did not fire, which is what happens when the heartbeat runs on the handler's own thread and the handler makes a long blocking call, or it only appeared expired because two clocks disagree, since lease_expires_at is written from the worker's clock and evaluated against the database's. The discriminator is the distribution: incidents clustered on the longest exports indict the heartbeat, incidents clustered on one host indict skew. Measure both, and measure each host's offset against the database directly.
- Read the reclaim query precisely. In PostgreSQL now() is transaction start time, not statement time, so a reclaimer holding a long transaction compares against an older timestamp than expected; clock_timestamp() is the statement-time function. This is worth ruling in or out before you redesign anything, because it changes which rows look expired.
- Remove the second clock rather than trying to synchronise it. Issue and extend the lease in the database, with lease_expires_at = now() + interval '30 seconds' in both the claim and the heartbeat, so exactly one clock is ever compared and worker skew stops mattering to this predicate.
Follow-up
- The displaced worker has already streamed half the file to object storage. What makes that side effect safe to repeat?
- You now count takeovers. What alert fires on that counter, and at what threshold?
Suggested practice checklist
The plan follows the four reported stages: day 1 maps the loop and your stories, days 2 to 5 cover the coding, conceptual, debugging and design questions candidates report, and days 6 and 7 are behavioral practice and a full run-through.
Day 1 — Map the loop and your evidence
- Write the four reported stages in order and one sentence on what each can cover, marking the one you are least ready for.
- List the must-have and nice-to-have requirements candidates describe for the role and attach one project of yours to each.
- Pick six projects or incidents you could draw on for stories and note which question each could serve.
Deliverable: A one-page loop map, a requirements-to-projects table and a list of six story candidates.
Day 2 — Coding: linked list and binary trees
- Reverse a linked list iteratively and recursively in your strongest language and test empty, one-node and long lists.
- Solve two binary-tree problems (a depth or traversal problem and a lowest-common-ancestor or path problem) recursively and iteratively, stating time and space.
Deliverable: Six working solutions (two list reversals, two tree problems each solved two ways) with test cases and a written complexity line for each.
Day 3 — Conceptual answers: APIs, patterns and code quality
- Write a REST versus GraphQL comparison covering over-fetching, resolver cost, HTTP caching and versioning.
- Define design patterns, then describe two you have used (for example a factory or an observer) and one case where a pattern was overkill.
- Write your code quality practice: tests, review, linting and CI, with one concrete example of each.
Deliverable: Three spoken-ready answers, each with a real example from your own work.
Day 4 — Debugging and performance
- Write the bug story as symptom, reproduction, root cause, fix and prevention, then say it aloud.
- Outline how you would diagnose a slow application: measure, find the hot spot, change one thing, re-measure.
- Prepare a performance versus maintainability trade-off from your own work, including what you measured.
Deliverable: A bug story, a diagnosis checklist, a trade-off story and notes from two practice questions.
Day 5 — Design: new feature, e-commerce, consistency and deployment
- Pick a feature from a product you use and design it from stated requirements: users, data, API and failure cases.
- Sketch a simple e-commerce architecture and mark which data needs strong consistency.
- Write your answer on consistency in a distributed system: idempotent retries, transactions in one store, and eventual consistency where it is safe.
- Explain how you would deploy the e-commerce or new-feature design on a cloud provider and roll it back if a release goes wrong.
Deliverable: Two diagrams, a one-page consistency answer and a short deploy-and-rollback outline.
Day 6 — Behavioral stories
- Write outlines for the team project, conflict and prioritization questions from your story list.
- Write the version control story with one incident (conflict, revert or recovery).
- Prepare a plain-language explanation of one technical concept and a story about peer feedback.
Deliverable: Four story outlines, each with a result and a lesson, plus one plain-language explanation.
Day 7 — Full run-through
- Answer one coding, one design and one behavioral question aloud in a row, as in a technical stage.
- Explain your strongest project in two versions, one for the CTO and one for a customer support colleague.
- Write the questions you will ask each stage.
Deliverable: A recorded run-through with notes on the weakest answer and a list of questions to ask.
Stories to prepare for the team and culture rounds
Candidates report a stage devoted to culture and values, and they list behavioral questions on version control experience, teamwork, conflict and prioritization, plus peer feedback and explaining technical ideas to non-technical colleagues. Prepare stories where your own actions are clear and the result can be stated.
- Share an experience where you had to explain a technical concept to someone without a technical background.
- How do you handle feedback from peers?
Discuss your experience with version control systems, particularly Git.
Medium · Technical / Domain Questions
Approach
- Lead with a workflow you actually ran: how branches were named and merged, whether through pull requests, squash merges or merge commits, and why. Give one trade-off, such as rebase keeping history linear but rewriting commits, so you never rebase a branch others have pulled.
- Include one real incident: resolving a merge conflict by reading the intent of both sides and re-running tests, undoing a bad change on a shared branch with git revert (a new commit) instead of git reset, or recovering lost work with git reflog.
- Show diagnosis skill: git bisect to find the commit that introduced a regression, and git blame or git log -S to learn why a line exists.
- Tie Git to collaboration: small commits with messages that say why, review on pull requests, and CI checks that keep the main branch releasable. Avoid the failure of reciting add, commit and push with no situation, or claiming you never hit a conflict.
Follow-up
- When would you rebase and when would you merge?
- You pushed a commit with a credential in it to the main branch; what do you do?
- How would you use Git to find which change broke a feature?
Tell me about a time you worked on a team project. What was your role?
Medium · Behavioral / Leadership
Approach
- Choose a project where your role is identifiable. In one or two sentences give the team shape and the outcome, then spend most of the answer on what you owned: a feature, a service, a review duty or a release.
- Cover the interfaces the role has: how you worked with product management on requirements, with customer support on user-reported problems, and with reviewers on your code.
- Include one decision you made and one moment the plan changed, such as a scope change or a blocked teammate, and what you did to keep the work moving.
- Use I for your actions and name teammates for theirs. The mistake that sinks this answer is describing everything in the first person plural, so your contribution cannot be separated, or inflating your role into a lead you were not.
Follow-up
- What would you change about how that team worked?
- Which part of the project was someone else's, and how did you depend on it?
- How did you help when a teammate fell behind?
Describe a situation where you had to deal with conflict in a team setting.
Medium · Behavioral / Leadership
Approach
- Pick a disagreement about substance, such as API shape, patching versus rewriting, review standards or speed versus quality, not a personality clash. State both positions fairly in a sentence or two, including why the other person had a point.
- Show your steps: you spoke to the person directly first, agreed on the shared goal, and replaced opinion with evidence such as a benchmark, a prototype, a failing test or a user report. Involve a lead only after that attempt.
- Give the outcome honestly. If your view lost, say you committed to the decision; if it won, say what you conceded. Describe how the working relationship stood afterwards and any process change, such as a written design note or a review checklist.
- Avoid the villain story, blaming language, and the answer that you have never had a conflict.
Follow-up
- What would you have done if the evidence had been inconclusive?
- Would the other person describe the outcome the way you do?
- When would you take a disagreement to a lead?
How do you prioritize tasks when you have multiple deadlines?
Medium · Behavioral / Leadership
Approach
- State your method, then prove it with a story. List every item with its deadline, size and who is waiting, then rank by cost of delay and by what blocks other people, not by who is loudest.
- Separate kinds of engineering work: user-reported bugs, feature work and review requests. Say how you triage, for example data loss or blocked users first, and how you contain review time so it does not swallow your day.
- Show communication: tell the product manager or support colleague what slips and why, early, with options such as cutting scope, moving a date or getting help. Negotiate instead of silently working late.
- Close with a concrete collision of two deadlines: what you deferred, what happened, and what you would do differently. The answer that sinks is 'I stay organised and work longer', which names no decision or trade-off.
Follow-up
- Two urgent requests arrive from different managers; what do you do?
- Halfway through you realise you will miss a deadline; what happens next?
- How do you decide a bug outranks a feature?