cswork / libraryOpen practice workspace

Interview guides · Button

Button Software Engineer Interview Guide

Button Software Engineer interview guide: reported rounds, REST vs GraphQL, URL shortener design, coding and behavioral prep, plus a 7-day plan.

The 60-second brief

Button is described to candidates as a company whose software helps businesses run their customer engagement. Candidates who describe the Software Engineer role mention work across backend systems, frontend applications and APIs, done alongside product managers and designers. The responsibilities listed are building software from product requirements, translating those requirements into technical specifications, reviewing code, and fixing problems in applications that already run. No specific product line or team is named, so treat the role as general web and API engineering.

This guide is for candidates interviewing for the Software Engineer role at Button. It covers the three reported stages (Phone Screen, Technical Interviews, Behavioral Questions), all eleven reported questions, and a seven-day plan. The questions span language proficiency, REST versus GraphQL, production debugging, two system design prompts, performance, ambiguous requirements, two short coding problems and two behavioral prompts, so the guide treats them as a general software engineering set rather than a specialist track.

Candidates report three stages over roughly three to five weeks: a phone screen, one or more technical interviews, and a behavioral stage. The exact path can vary with the team you interview for.

Focus areas

  • Software Engineering (General)
  • Technical Screen / Phone Interview
  • Interview Process Fundamentals

Overview

The requirements candidates describe for this role name JavaScript, Python or Ruby, experience with web frameworks and APIs, and familiarity with databases and cloud services. Docker, Agile and DevOps practice, and a frontend framework such as React or Angular appear as extras. That mix points to a generalist: someone who can write a small function cleanly, reason about an HTTP API, and explain how a service behaves under load or failure.

The reported questions match that profile. Two are short coding problems (a palindrome check and binary search). Two are design prompts: a URL shortening service, and a microservices layout for payment processing. Three are about engineering judgment: debugging a complex production issue, speeding up a slow application, and starting a project whose requirements are ambiguous. One asks you to compare REST and GraphQL, one asks which languages you are strongest in and why, and two are behavioral. Breadth is the theme, so a shallow but correct answer to each beats a deep answer to one.

Prepare in layers. First, make the two coding problems automatic in the language you will use, including the edge cases (empty input, a single element, a target at either end). Second, build a repeatable shape for open-ended answers: state assumptions, name the main components or steps, then say what breaks first. Third, write two behavioral stories with real decisions and measurable outcomes, because the reported behavioral prompts leave room for any project you choose. The seven-day plan below follows that order.

The Button interview process

Candidates describe three stages, from an initial phone call through one or more technical interviews to behavioral questions. The reports do not say which reported question belongs to which stage, so prepare every question type for the technical stage and the behavioral prompts for the last one.

Phone Screen

Candidates describe the first stage as an initial screening call about your background and fit for the role. The reported question set includes one about the languages you are strongest in and why, so have that answer ready whichever stage it lands in. Use the call to learn what the technical interviews will contain for your team, since the process is reported to vary by team.

What to demonstrate

  • Your background and recent work, which a screening call reviews by its nature
  • Fit with the role as the candidate describes it
  • How clearly you can explain the languages and tools you have used

How to prepare

  • Write a short walk through your last two or three projects, each naming the language, the web framework, the API style, the database and any cloud service, plus what you personally built.
  • Prepare the language answer: pick one of JavaScript, Python or Ruby if you can, give two concrete reasons (ecosystem, typing, runtime model, speed of iteration) and one real limitation you have hit.
  • List questions for the recruiter: which languages are allowed in coding, what the technical interviews look like for your team, and what follows them.

Technical Interviews

Candidates describe this stage as one or more interviews on technical skills and problem solving. The reported questions cover several types: REST versus GraphQL, debugging a complex production issue, two system design prompts (a URL shortening service and a microservices layout for payment processing), improving a slow application, tackling a project with ambiguous requirements, and two short coding problems (a palindrome check and binary search). Prepare each type, because the reports do not tie a question to a specific interview.

What to demonstrate

  • Working code on small problems, which a live coding format tests directly
  • Reasoning about APIs and service design, including scalability
  • A structured approach to debugging and performance problems
  • How you break down an under-specified problem

How to prepare

  • Write the palindrome function twice (reverse and compare, then two pointers) and binary search iteratively, from a blank file, with a list of edge-case tests.
  • Prepare a one-page REST versus GraphQL comparison: over- and under-fetching, endpoint shape, HTTP caching, N+1 resolver queries, versioning and error handling.
  • Outline the URL shortener and the payment microservices answers: assumptions, components, data model, and what fails first under load.
  • Write a fixed sequence for production debugging and slow-application questions: scope, recent changes, measurement, hypothesis, fix, prevention.

Behavioral Questions

Candidates describe the last stage as behavioral questions that assess cultural fit. The reported prompts are about a significant project challenge and about prioritizing across several projects, and the reports also mention team disagreements and your usual role in group projects. Because the prompts are open, the story you choose does most of the work.

What to demonstrate

  • How you describe decisions you personally made
  • How you handle competing demands and communicate trade-offs
  • How you work with teammates, including when you disagree

How to prepare

  • Write two full stories in outline form, one for the project challenge and one for prioritizing multiple projects, each with the situation, your decision, the alternatives you rejected and a measurable result.
  • Prepare shorter stories for a team disagreement and for the role you usually take in a group, using I for your own actions and naming teammates' contributions separately.
  • Rehearse each story aloud so the setup stays short and the reasoning gets most of the time.

Where candidates lose points

Writing the palindrome check without settling what counts as a palindrome

Ask whether case, spaces and punctuation are ignored, then say your answer. The two-pointer version (compare from both ends, skip non-alphanumerics, fold case) runs in O(n) time and O(1) extra space, while reversing a copy uses O(n) space. Test the empty string, a single character and a mixed-case sentence.

Binary search that loops forever or misses the ends

Fix the invariant before coding: search the closed range lo..hi, compute mid as lo + (hi - lo) // 2, move lo to mid + 1 or hi to mid - 1, and stop when lo exceeds hi. Walk it by hand on an empty array, one element, a target at each end and a missing target. Mention the lower-bound variant if duplicates are possible.

Answering REST versus GraphQL with 'GraphQL is newer' or by listing definitions

Compare on specifics: REST uses a URL per resource and caches well over HTTP, while GraphQL has one endpoint where the client picks fields, which fixes over- and under-fetching but moves cost into resolvers (N+1 queries, batching with a loader) and makes caching and query-depth limits your job. Finish with a case where you would pick each.

Jumping to technologies in the URL shortener and payment microservices designs

Start with assumptions about read and write volume, then name the pieces. For the shortener: key generation (counter encoded in base 62 versus hashing, and how collisions are handled), a key-value store, a cache on the redirect path, and the 301 versus 302 trade-off. For payments: service boundaries, idempotency keys on every write, and how a timeout after a charge is reconciled.

Debugging and performance answers that stop at 'I would check the logs'

Give an ordered sequence: bound the impact, ask what changed recently, mitigate first (roll back or disable a flag), then reproduce and form a hypothesis from metrics, logs and traces. For a slow application, measure before changing anything, find whether time goes to database queries, repeated calls, I/O or CPU, and fix the largest share first.

Questions & practice

Walk us through how you would tackle a project with ambiguous requirements.

Medium · Problem-Solving / Case Studies

Approach

  1. Treat this as a process question, not an algorithm: show a repeatable way to turn a vague request into something buildable, using one real project as the running example.
  2. Start from the goal: who asked, what problem or metric the project should move, and what 'done' means. List the open questions and sort them by cost of being wrong. Answer irreversible ones (data model, public API contract) before building, and make cheap, reversible ones yourself as written assumptions.
  3. Write a one-page spec covering scope, non-goals, assumptions and open questions, and get it confirmed by the requester and anyone downstream (product, design, the teams consuming your API). Written agreement stops the scope from drifting quietly.
  4. Build a thin end-to-end slice or prototype early and put it in front of users or stakeholders. Real feedback settles ambiguity faster than more meetings. Keep the design easy to change where requirements are still uncertain.
  5. Close with how you handled change: a short decision log, regular check-ins, and explicitly re-agreeing scope when requirements shifted. Name one assumption that turned out wrong and what it cost.

Follow-up

  • Two stakeholders want contradictory things and neither will decide. What do you do?
  • Halfway through, the requirements change significantly. How do you decide what to keep?
  • How do you know when you have enough clarity to start writing code?

Can you write a function that checks if a string is a palindrome?

Medium · Coding / Algorithms

Approach

  1. Clarify the rules first: is it case-sensitive, should spaces and punctuation be ignored (as in 'A man, a plan, a canal: Panama'), and what does an empty string return? An empty or one-character string is a palindrome.
  2. Simple version: normalise, then compare with its reverse (s == s[::-1] in Python). That is O(n) time but O(n) extra space for the copy.
  3. Better version: two pointers, i from the start and j from the end. Skip characters that are not alphanumeric, compare lowercase forms, and return false on the first mismatch. Stop when i >= j. That is O(n) time and O(1) extra space, with no normalised copy.
  4. Edge cases to test aloud: empty string, single character, even and odd lengths, strings made only of punctuation, mixed case. Mention Unicode: JavaScript strings are UTF-16, so reversing by index splits surrogate pairs such as emoji, and in any language combining accents mean code points are not always user-visible characters.

Follow-up

  • Return true if the string can become a palindrome by deleting at most one character. What changes?
  • Find the longest palindromic substring. What is the complexity of expanding around each center?
  • How would you check whether a singly linked list is a palindrome in O(1) extra space?

How would you implement a binary search algorithm in your preferred language?

Medium · Coding / Algorithms

Approach

  1. State the precondition: the input is sorted ascending. Then fix one loop invariant and keep to it. Closed interval: lo = 0, hi = n - 1, while lo <= hi. Half-open: lo = 0, hi = n, while lo < hi. Most off-by-one bugs come from mixing the two.
  2. Compute mid = lo + (hi - lo) // 2. In Java or C++, (lo + hi) / 2 can overflow 32-bit ints; Python ints cannot overflow, but the safe form shows you know why. Compare a[mid] with the target and discard the half that cannot contain it, so every iteration shrinks the range.
  3. Complexity: O(log n) time. O(1) space when iterative, O(log n) call stack when recursive. Prefer the iterative version in an interview.
  4. Handle duplicates and misses with the lower-bound variant: the first index whose value is >= target. With a half-open range, set hi = mid when a[mid] >= target, else lo = mid + 1; return lo, which is also the insertion point. Python's bisect.bisect_left and C++ std::lower_bound do this. Java's Arrays.binarySearch returns -(insertion point) - 1 on a miss.
  5. Test aloud: empty array, one element, target below the first or above the last element, target present several times, and that the loop always terminates (setting lo = mid with a lower mid can loop forever).

Follow-up

  • Modify it to return the first and last positions of a target that appears several times.
  • Search a sorted array that has been rotated at an unknown pivot.
  • How would you binary search on the answer instead of an index, for example the minimum capacity to ship packages within D days?

Find version gaps and relay lag with window functions

Medium · window functions · event tables · frame semantics

outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.

Approach

  1. Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
  2. Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
  3. Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
  4. Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.

Follow-up

  • Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
  • Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

What programming languages are you most proficient in, and why?

Medium · Technical / Domain Questions

Approach

  1. Lead with one primary language and back it with evidence: what you shipped in it, roughly how large the codebase was, and what you debugged or profiled in it. Ranking two or three languages honestly is stronger than listing six.
  2. Tie the 'why' to concrete language properties, not taste. For example: Python for its standard library and readable data handling; JavaScript/Node for one language across the browser and server on a single-threaded event loop; Ruby for Rails conventions that make CRUD and API work fast to build.
  3. Show that you know each language's limits. In standard CPython the GIL stops threads from running Python bytecode in parallel, so CPU-bound work goes to multiprocessing and I/O-bound work to threads or asyncio. In Node, a long synchronous loop blocks the event loop for every request.
  4. The requirements candidates describe for this role list JavaScript, Python or Ruby. If one of those is your main language, prepare one deeper detail about it (async/await and the microtask queue, Python generators, Ruby blocks), because a follow-up on internals is a natural next question.
  5. Finish with how you choose a language for new work: team familiarity, the libraries the problem needs, runtime and concurrency model, and deployment. That turns a preference into an engineering decision.

Follow-up

  • What is one thing you dislike about your strongest language, and how do you work around it?
  • How does your main language handle concurrency, and when has that model caused you a problem?
  • If you had to pick up Ruby or JavaScript for this job, how would you get productive in it within a couple of weeks?

Can you explain the difference between REST and GraphQL?

Medium · Technical / Domain Questions

Approach

  1. REST models resources at URLs (GET /orders/42) and uses HTTP verbs and status codes for meaning. GraphQL exposes a single endpoint with a typed schema, and the client sends a query naming exactly the fields and nested relations it wants.
  2. Fetching trade-off: REST endpoints often over-fetch (fixed response shapes) or under-fetch (several round trips to assemble one screen). GraphQL fixes both in one request but moves cost to the server: a naive resolver per field causes N+1 database queries, which you fix with batching such as DataLoader.
  3. Caching and transport: REST GETs work with HTTP caches, CDNs, ETag and Cache-Control out of the box. GraphQL queries are usually POSTs to one URL, so caching needs persisted queries sent as GET, or a normalized client cache like Apollo's.
  4. Errors and evolution: GraphQL commonly returns HTTP 200 with an errors array beside partial data, so monitoring cannot rely on status codes alone. REST tends to version through URLs or headers, while GraphQL adds fields and marks old ones @deprecated so it can evolve without versions.
  5. Give a clear recommendation: REST for public, cache-heavy or simple CRUD APIs and webhooks; GraphQL when many clients (web, mobile) need different shapes of related data. Add query depth or cost limits if you expose GraphQL publicly.

Follow-up

  • How would you stop a client from sending an expensive, deeply nested GraphQL query?
  • How do you handle authorization per field in GraphQL compared with per endpoint in REST?
  • How would you paginate a large list in each style, and why is cursor pagination usually preferred?

Design a URL shortening service. What considerations would you make for scalability?

Medium · System Design / Architecture

Approach

  1. Pin down requirements: create a short code for a long URL, redirect quickly, optional custom aliases and expiry, click analytics. Traffic is heavily read-dominated, so redirect latency and availability drive the design. Estimate writes per second, reads per second and storage before choosing components.
  2. Key generation: base62-encode a unique numeric ID. Seven base62 characters give 62^7, about 3.5 trillion codes. To avoid one central counter, give each app server a block of IDs from a coordinator, or use random codes with a uniqueness check on insert. Truncating a hash of the long URL needs collision handling.
  3. Storage and read path: a key-value store keyed by short code (code -> long URL, owner, expiry) that partitions cleanly by code. Put a cache such as Redis in front for hot codes, and optionally a CDN. Since mappings never change once written, caching is simple; only deletion and expiry need invalidation.
  4. Redirect semantics: 301 lets browsers cache the redirect and cuts load but hides repeat clicks from analytics; 302/307 sends every click through the service. Write click events to a queue asynchronously so analytics never slows the redirect.
  5. Scale and abuse: stateless redirect servers behind a load balancer, replicated storage, rate limits on creation, scanning target URLs for malware or phishing, and choosing whether the same long URL gets one code or a new code per user.

Follow-up

  • How do you guarantee two servers never hand out the same short code?
  • One link goes viral and gets millions of hits a minute. What breaks first, and what do you change?
  • How would you support custom aliases and expiry without slowing the redirect path?

How would you structure a microservices architecture for a payment processing system?

Medium · System Design / Architecture

Approach

  1. Split by responsibility and data ownership: an API gateway (auth, rate limits), a payment orchestration service that owns each payment's state machine (created -> authorized -> captured -> settled, plus failed/refunded), a ledger service, a fraud/risk service, PSP adapter services, and a notification/webhook service. Each one owns its own database.
  2. Correctness before scale: clients send an idempotency key on create-payment, stored with a unique constraint and the saved response, so a retry after a timeout returns the original result instead of charging twice. The ledger is append-only double-entry: every movement is balanced debits and credits, and corrections are new entries, never updates.
  3. Cross-service consistency: avoid two-phase commit across services. Use a saga, where each step has a compensating action (void an authorization, issue a refund), and the transactional outbox pattern so a state change and its event are committed together and published at least once. Consumers must therefore be idempotent.
  4. Failure handling with the PSP: a timeout means the outcome is unknown, not failed. Record it as pending, retry with exponential backoff and jitter using the PSP's idempotency key, query the PSP for status, and reconcile daily against the PSP's settlement reports. Treat incoming webhooks as duplicate-prone and possibly out of order.
  5. Security and compliance: tokenize card data at the edge or through the PSP so most services never handle raw card numbers, which keeps PCI scope small. Add per-service authentication, encryption in transit and an audit trail.

Follow-up

  • The PSP charged the card but your service timed out before recording it. Walk through how the system recovers.
  • How do you keep the ledger and the payment service consistent if the event between them is delivered twice or not at all?
  • Which service would you split out first from a monolith, and why that one?

How would you improve the performance of a slow-running application?

Medium · Problem-Solving / Case Studies

Approach

  1. Define 'slow' with a number before changing anything: which operation, which percentile (p50 vs p95/p99), under what load, and the target. Reproduce it and record a baseline so every change can be measured against it.
  2. Find where the time goes: distributed tracing or APM to split time across database, downstream calls, CPU and queueing, then a profiler on the hot path (py-spy or cProfile for Python, the Node --prof flag or Chrome DevTools for JavaScript, browser performance tools for the frontend). Optimise the biggest part first, since speeding up a small part caps the overall gain.
  3. Fix the usual backend causes: N+1 queries (eager loading or batching), missing indexes (check with EXPLAIN ANALYZE), unbounded result sets (pagination), chatty serial network calls (batch or run in parallel), exhausted connection pools, and slow work done inside the request that belongs in a background job.
  4. Add caching where the data allows: per-request memoisation, an application cache such as Redis with a clear invalidation or TTL rule, HTTP caching or a CDN for static and public responses. Say what staleness is acceptable before caching.
  5. Verify and protect the gain: rerun the same benchmark or a load test, compare percentiles rather than averages, and add a latency alert or performance budget so the regression cannot return unnoticed.

Follow-up

  • The slowness only appears under load, not for a single request. What does that suggest, and how do you confirm it?
  • How do you choose between adding a cache and fixing the underlying query?
  • An endpoint's p99 is ten times its p50. Where would you look?

How do you approach debugging a complex issue in production?

Medium · Technical / Domain Questions

Approach

  1. Scope and stabilise first: which users, endpoints or regions are affected, since when, and how badly (error rate, latency percentiles). If impact is growing, mitigate before root-causing: roll back, turn off a feature flag, shed load or fail over.
  2. Correlate the start time with changes: deploys, config or flag flips, dependency upgrades, data migrations, traffic spikes, third-party incidents. Check that the dashboard itself is not wrong (a missing metric or a sampling change) before trusting the symptom.
  3. Follow one failing request end to end using a correlation or trace ID across logs, traces and metrics, to find which hop adds the error or latency. Compare a failing request with a succeeding one to isolate the difference (input, tenant, host, version).
  4. Form one testable hypothesis at a time and pick checks that rule out half the possibilities: reproduce with captured input in staging, bisect commits with git bisect, or add targeted logging behind a flag. Intermittent bugs usually point to race conditions, timeouts, connection-pool exhaustion or one bad host.
  5. Close the loop: fix the cause, add a regression test and an alert that would have caught it sooner, and write a blameless postmortem that separates the trigger from the root cause.

Follow-up

  • The bug only happens about once in a thousand requests and you cannot reproduce it locally. What do you do next?
  • How do you decide between rolling back right away and debugging live?
  • What would you add to logging or monitoring so the next incident like this is found in minutes?

Suggested practice checklist

The plan starts with the short coding problems, then covers API and design questions, debugging, and ambiguity, and ends with a full run-through. Each day produces something you can read back later.

Day 1 — Map the loop and settle your language

  • Write the three reported stages (Phone Screen, Technical Interviews, Behavioral Questions) and list the question types candidates report, marking the ones you are least ready for.
  • Pick the language you will use for coding, from JavaScript, Python or Ruby, and set up a local file where you can run tests quickly.
  • Write your answer to the question about the languages you are strongest in: two reasons, one limitation, one recent project.

Deliverable: A one-page loop map, a ready coding setup, and a written answer on language proficiency.

Day 2 — Two coding problems from a blank file

  • Write the palindrome function in two ways (reverse and compare, then two pointers), and a version that ignores case and non-alphanumeric characters.
  • Write binary search iteratively returning an index or -1, then a lower-bound variant for arrays with duplicates.
  • Build a list of test inputs for both: empty, one element, both ends, missing target, repeated values.

Deliverable: Two working functions plus the variants, each with a test list that passes.

Day 3 — REST versus GraphQL, and list endpoints

  • Take one data model with nested relations and write the same fetch as REST calls and as a single GraphQL query.
  • Write a comparison covering over- and under-fetching, HTTP caching, N+1 resolvers, versioning and error reporting, ending with when you would choose each.

Deliverable: A comparison table with a recommendation for each side, and a solved keyset paging exercise.

Day 4 — Design: URL shortener and payment microservices

  • Outline the URL shortener: assumptions, key generation, storage, redirect path with a cache, expiry, and the 301 versus 302 choice.
  • Outline the payment microservices answer: service boundaries, idempotency keys, a ledger or event log, retries, and reconciliation after a timeout.
  • For each design, write down what breaks first when traffic grows and what you would do about it.

Deliverable: Two one-page design outlines, each ending with a failure and scaling section.

Day 5 — Debugging and performance

  • Write your fixed sequence for debugging a complex production issue: scope, recent changes, mitigation, hypothesis, fix, prevention.
  • Write a checklist for a slow application: measure, locate the cost (query, repeated calls, I/O, CPU), fix the largest share, re-measure.

Deliverable: A debugging runbook, a performance checklist, and one solved practice query.

Day 6 — Ambiguity and behavioral stories

  • Write how you start a project with ambiguous requirements: questions to ask, assumptions to record, the smallest slice to build first, and how you check in.
  • Outline the project-challenge story and the prioritization story, each with situation, decision, alternatives rejected and a measurable result.
  • Outline shorter stories for a team disagreement and for the role you usually take in a group.

Deliverable: One written approach to ambiguity and four story outlines.

Day 7 — Full rehearsal

  • Run a mock phone screen: walk through your projects, give the language answer and explain REST versus GraphQL.
  • Solve the palindrome and binary search problems again from a blank file, then deliver one design answer aloud.

Deliverable: A recorded run-through and a short list of the three weakest spots to fix before the call.

Stories for the behavioral stage

The reported behavioral stage is described as an assessment of cultural fit, and the two reported prompts ask about a significant project challenge and about prioritizing across several projects. For a Software Engineer, the strongest stories are about decisions you made in code, delivery or teamwork, with an outcome you can state plainly.

  1. How do you handle disagreements within a team?
  2. What role do you typically take in group projects?
  3. Describe a time you had to debug a production problem while other work was waiting.
  4. Tell me about a project where the requirements were unclear and how you moved it forward.

Describe a time when you faced a significant challenge in a project. How did you handle it?

Medium · Behavioral / Leadership

Approach

  1. Choose a challenge where your own decision changed the outcome: a failing integration, a scope change late in the project, a performance regression, a migration that went wrong. A story where you only carried out someone else's plan shows nothing about your judgment.
  2. State the situation and your task in two sentences, then spend most of the answer on the options you weighed, why you picked one, and what you did. Use I for your actions and credit teammates by name for theirs.
  3. Make the result checkable: a metric that moved (error rate, latency, delivery date, number of support tickets) and what you changed afterwards so the problem could not recur, such as a test, an alert, or a runbook.
  4. Include one thing that went wrong or one disagreement and how you resolved it with evidence rather than seniority. The common mistake is a challenge that was only workload, or a story that blames another team without showing what you did about it.

Follow-up

  • What would you do differently if you faced the same situation again?
  • How did you know your change, and not something else, fixed the problem?
  • When did you decide to escalate or ask for help, and what did you bring when you did?

How do you prioritize your tasks when managing multiple projects simultaneously?

Medium · Behavioral / Leadership

Approach

  1. Name the method you really use, in concrete terms: list every commitment, note the deadline and who is blocked on each, estimate the cost of delay, then rank. Say what you do with the lower items: renegotiate the date, hand them off, or cut scope.
  2. Anchor it in one real case with at least two competing demands, for example a feature due to a product manager while a production bug and a code review queue were also open. State what you chose first and why, in one sentence each.
  3. Show the communication step: you told the affected people early, with the new date or reduced scope, instead of letting a deadline slip silently. Name who you told and how they responded.
  4. Say how you handle interruptions, such as a production incident in the middle of planned work, and close with the outcome. A to-do app or working longer hours is not an answer on its own, because it names no trade-off.

Follow-up

  • What do you do when two stakeholders each say their project is the top priority?
  • Tell me about a time you got the priority wrong and what it cost.
  • How do you decide whether an urgent bug interrupts the work already in progress?

Ship under a deadline and bound the debt you chose

Medium · pagination · technical debt · tradeoffs

You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.

Approach

  1. Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
  2. Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
  3. Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
  4. Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.

Follow-up

  • At what page depth does the offset version breach your latency budget, given your page size and row counts?
  • What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?