cswork / libraryOpen practice workspace

Interview guides · BookedBy

BookedBy Software Engineer Interview Guide

BookedBy Software Engineer interview guide: five reported rounds, coding, React and JavaScript, system design and behavioral questions, plus a 7-day plan.

The 60-second brief

BookedBy builds a booking platform that customers use directly. Candidates describe the Software Engineer job as developing and maintaining that software, improving the user experience, and working with product managers and designers to turn requirements into technical specifications. Day to day, candidates report code reviews, feature development and bug fixes, with task order driven by user needs and business goals. The role lists JavaScript, Python or Java, a front-end framework such as React or Angular, and Git as must-haves, with cloud services (AWS or Azure), SQL or NoSQL databases and Agile practice as extras.

This guide is for candidates interviewing for a Software Engineer role at BookedBy. It covers the five reported stages, every question candidates report (web fundamentals, classic coding, case studies, system design and behavioral), and a seven-day plan. It does not cover pay, and it does not claim to describe how BookedBy scores candidates. Where candidates do not say which stage a question belongs to, the guide says so and has you prepare the whole set.

Candidates report five stages over roughly 4-6 weeks: Initial Screening, Technical Interviews, Coding Assessments, Behavioral Questions and a possible Take-Home Assignment. Candidates do not report which stage carries which question, so prepare every question type.

Focus areas

  • Front-End Development
  • Data Structures & Algorithms
  • Problem Solving (Algorithmic)

Overview

The reported loop has five stages that start with a recruiter call and end with a take-home that may or may not appear. The reported questions fall into five groups: web and technical fundamentals, classic coding problems, behavioral prompts, problem-solving case studies and system design. Candidates do not tie individual questions to individual stages, so the safe plan is to be ready for all five groups and not bet on a particular stage.

The technical group leans toward the web front end. The reported questions are closures in JavaScript, state management in React, responsive web design, REST versus GraphQL and optimizing a slow-loading web application. The requirements candidates describe name JavaScript, Python or Java, React or Angular, and Git, with AWS or Azure, SQL or NoSQL and Agile as extras. Pick one language for the coding problems and know it cold, and be able to explain JavaScript and React behavior with small code examples and not only definitions.

The coding group is five fundamentals: reverse a string, find two numbers that sum to a target, implement binary search, detect a cycle in a linked list, and merge two sorted arrays. Because the problems are familiar, the differences come from the details you say and test: complexity, empty and single-element input, duplicates, integer overflow in the binary search midpoint, and whether the merge works in place or builds a new array. Practise writing each one without an editor's help.

The case-study and design groups reward structure. Two reported prompts are about a booking feature and a mobile app, so practise reasoning about availability, double booking, time zones and cancellations. The system design prompts (URL shortener, chat application, data consistency, high availability, microservices) are general, so prepare a repeatable outline: requirements, read and write paths, storage, failure modes, trade-offs. Candidates also report code review and collaboration with product managers and designers as part of the work, so choose behavioral stories that show those. Topic breakdowns that candidates share also list AI for customer behavior analysis, but no reported question covers it, so treat it as background and spend your time on the question groups above.

The BookedBy interview process

These are the five stages candidates describe, in the order they report them. Which stages you meet, and whether a take-home appears at all, can vary by team and role.

Initial Screening

Candidates report this as a call with a recruiter to judge fit for the role. No technical questions are reported for it. Treat it as the stage where you set up the rest of the loop: a clear account of your background, the languages and frameworks you have shipped with, and a sense of the remaining stages and their format. Candidates also report a timeline of roughly 4-6 weeks end to end, so ask the recruiter what to expect next.

What to demonstrate

  • Fit for a Software Engineer role, as the call is reported to assess
  • How clearly you summarise your experience and motivation
  • Whether your stack lines up with the listed languages and frameworks

How to prepare

  • Write a short walk through your CV that lands on your most relevant web project, and name the language and framework you used in it.
  • Match your experience against the listed must-haves (JavaScript, Python or Java, React or Angular, Git) and the extras (AWS or Azure, SQL or NoSQL, Agile) and be ready to say honestly where you are thin.
  • Prepare questions for the recruiter: which stages follow, whether a take-home is part of the loop, and what the coding stage looks like.

Technical Interviews

Candidates report interviews with team members or hiring managers that assess technical capability. The reported technical questions are JavaScript closures, React state management, responsive web design, REST versus GraphQL and optimizing a slow-loading web application. The system design topics may also be discussed here, but candidates do not say which stage they belong to, so prepare them alongside. Answer with a concrete example each time: a short code snippet for closures, a component tree for state, a measured investigation for the slow page.

What to demonstrate

  • Working knowledge of JavaScript and a front-end framework
  • Ability to reason about web performance and API design trade-offs
  • Technical communication with team members and hiring managers

How to prepare

  • For closures, write a stale-closure example (a loop variable captured in a callback, or a React effect reading old state) and explain what the engine keeps alive and why.
  • Prepare a state-management answer that separates local state, lifted state, context, server cache and URL state, and says when you would reach for each.
  • Rehearse the slow-page investigation in order: measure first (network waterfall, bundle size, long tasks), then fix the largest cost, then verify the change.
  • Make a one-page comparison of REST and GraphQL with over-fetching, caching, versioning and error handling, and one case where you would pick each.

Coding Assessments

Candidates report that they may complete coding assessments to show programming skill. The reported coding problems are reversing a string, finding two numbers that sum to a target, binary search, detecting a cycle in a linked list and merging two sorted arrays. Candidates do not report the format or the platform, so be ready both to type code into an unfamiliar editor and to talk through it. Choose one language in advance and keep to it.

What to demonstrate

  • Working code for standard algorithm and data structure problems
  • Correct complexity analysis for the solution you chose
  • Handling of edge cases such as empty input, duplicates and boundaries

How to prepare

  • Write all five problems from a blank file in your chosen language, run them against empty, single-element and duplicate-heavy inputs, and state time and space complexity out loud.
  • For two-number sum, write the hash map version and the sorted two-pointer version and say when each is the better fit.
  • For cycle detection, write both the visited-set and the fast and slow pointer versions and state their space costs.
  • Practise typing in a plain editor without autocomplete, since the assessment environment is not described.

Behavioral Questions

Candidates report behavioral questions that cover interpersonal skills and cultural fit. The reported prompts include working with a difficult team member, prioritizing across multiple projects, taking the lead on a project, what motivates you, approaching feedback and troubleshooting a significant bug. Candidates also describe code review and collaboration with product managers and designers as part of the job, so pick stories from that kind of work. Each story needs a decision you made, a result, and one thing you would change.

What to demonstrate

  • Interpersonal skills and teamwork as reported for this stage
  • Your own contribution and judgment in past projects
  • How you handle disagreement, feedback and competing priorities

How to prepare

  • Build a story bank of four or five real examples (a conflict, a prioritization call, a project you led, a hard bug, feedback you gave or received) and map each to the reported prompts.
  • Write each story in the form situation, your decision, the result and what you would change, and keep the situation to a couple of sentences.
  • Replace every 'we did' with what you personally did, and add one detail from working with a product manager, designer or code reviewer.

Take-Home Assignment

Candidates report that a take-home assignment appears in some loops and is meant to reflect real-world work for the role. No task details are reported, so prepare for a small web feature built on the stack the role lists. Treat the submission as something another engineer will read: a working feature, tests, a short README with your assumptions and what you left out, and a clean Git history.

What to demonstrate

  • Ability to turn loosely specified requirements into working software
  • Code readability, testing and use of Git, which the role lists among its basics
  • How well you explain your trade-offs in writing

How to prepare

  • Build a small booking-style feature end to end (list available slots, choose one, confirm, show an error when the slot was just taken) with React state, a responsive layout and a simple API.
  • Write a README covering how to run it, the assumptions you made, what you cut and what you would do next.
  • Add unit tests for the logic that is easiest to get wrong, such as slot availability and double-booking checks, and commit in small, readable steps.

Where candidates lose points

Solving a coding problem correctly but saying nothing about complexity or edge cases

For each of the five reported coding problems, say time and space cost and test aloud with empty input, one element and duplicates. Name the usual traps: reusing the same element in two-sum, the overflowing midpoint in binary search, and a cycle that begins at the head.

Defining closures or React state in words with no code

Keep one tiny example for each: a stale closure in a loop callback or effect, and a component tree showing where state lives and why. A snippet you can explain beats a memorized definition.

Starting a design answer with boxes and arrows before requirements

For the URL shortener, chat application, consistency and availability prompts, begin with who uses it, expected read and write volume and what may be stale. Then name the read and write paths, storage choice, failure modes and the trade-off you picked.

Telling a behavioral story with 'we' throughout and no outcome, or one that blames a teammate

State what you personally decided, give a result you can point to, and describe the difficult colleague with curiosity, not blame. End each story with one thing you would change.

Treating a take-home as a quiz and submitting code with no tests, README or stated assumptions

Candidates describe the take-home as reflecting real-world work, so ship a small, working feature with tests, a README listing assumptions and cuts, and readable commits.

Questions & practice

Explain the concept of closures in JavaScript.

Medium · Technical / Domain Questions

Approach

  1. Define it: a closure is a function bundled with the lexical environment it was created in, so an inner function keeps access to the outer function's variables after the outer function has returned; scope is fixed where the function is written, not where it is called.
  2. Show it: makeCounter() declares let count = 0 and returns () => ++count; each call to makeCounter creates a separate environment, which gives private state, function factories, partial application, once() and debounce().
  3. Name the classic trap: for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) prints 3, 3, 3 because var is function-scoped and every callback shares one binding; let creates a fresh binding per iteration, so it prints 0, 1, 2.
  4. Stress that closures capture bindings, not values: later reassignments are visible. In React this produces stale closures, such as a setInterval callback reading the state from its first render; fix it with correct effect dependencies, a functional updater setCount(c => c + 1), or a ref.
  5. Mention memory: everything a reachable closure captures stays alive, so long-lived event listeners or timers that capture large objects leak until they are removed.

Follow-up

  • Write debounce(fn, ms) and explain which variables the returned function closes over.
  • Why does an interval inside useEffect keep logging the initial state, and how do you fix it?
  • How would you emulate private fields with closures, and what do #private class fields change?

Write a function to reverse a string. What are the time complexities of your approach?

Medium · Coding / Algorithms

Approach

  1. Clarify the input: ASCII or full Unicode, and whether to reverse in place (a character array) or return a new string; strings are immutable in JavaScript, Python and Java, so returning a string always needs O(n) space for the result.
  2. Two pointers: swap a[left] and a[right] and move inward until they meet; O(n) time, about n/2 swaps, and O(1) extra space when the input is already a mutable array.
  3. Know the idioms and their limits: Python s[::-1] is O(n); in JavaScript s.split('').reverse().join('') splits UTF-16 code units and breaks emoji, [...s] splits by code point but still separates combining marks, and Intl.Segmenter handles grapheme clusters; Java's StringBuilder.reverse() keeps surrogate pairs together.
  4. Avoid building the result with result = ch + result in a loop: each step copies the string, which is O(n^2) in the worst case; collect into an array or builder and join once.
  5. A recursive version is O(n) deep and can overflow the stack on long inputs; if it slices the string at every call it also becomes O(n^2).

Follow-up

  • Reverse the order of words in a sentence in place, using O(1) extra space.
  • How does your solution handle an emoji or an accented character built from two code points?
  • Use the same two-pointer idea to check whether a string is a palindrome, ignoring case and punctuation.

Given an array of integers, find the two numbers that sum up to a specific target.

Medium · Coding / Algorithms

Approach

  1. Clarify: return indices or values, is exactly one answer guaranteed, can the same element be used twice, may values be negative or duplicated, and is the array sorted.
  2. Brute force checks every pair: O(n^2) time, O(1) space; say it, then improve it.
  3. One-pass hash map: for each index i compute need = target - nums[i]; if need is already in the map return [map[need], i], otherwise store nums[i] -> i. Checking before inserting stops an element pairing with itself and still handles [3, 3] with target 6. O(n) time, O(n) space.
  4. If the array is sorted, or sorting is allowed, two pointers from both ends give O(n) after an O(n log n) sort with O(1) extra space, but sorting loses the original indices unless you sort (value, index) pairs.
  5. Edge cases: fewer than two elements, no valid pair (return an empty result or raise, as agreed), and overflow of target - nums[i] in fixed-width integer languages.

Follow-up

  • Return all unique pairs that sum to the target, without duplicates.
  • Extend it to three numbers summing to the target. What complexity can you reach?
  • Design a structure with add(number) and find(target) for a stream of numbers. Which operation do you make cheap?

Implement a binary search algorithm.

Medium · Coding / Algorithms

Approach

  1. State the precondition (sorted input) and the invariant: if the target exists it lies in [lo, hi]. Use lo = 0, hi = n - 1, loop while lo <= hi, compute mid = lo + (hi - lo) / 2 to avoid integer overflow in fixed-width languages, then move lo = mid + 1 or hi = mid - 1; return -1 when the loop ends.
  2. Complexity: O(log n) comparisons and O(1) space iteratively; a recursive version uses O(log n) stack.
  3. Write the lower-bound variant with a half-open range [lo, hi): while lo < hi, if a[mid] < target then lo = mid + 1 else hi = mid. It returns the first index >= target, which gives the first occurrence among duplicates and the insertion point (Python's bisect_left).
  4. Test the edges out loud: empty array, one element, target below the first or above the last element, all duplicates; the usual infinite loop comes from setting lo = mid while mid rounds down.
  5. Generalize: binary search works on any monotonic predicate, so it can search an answer space, such as the smallest capacity that finishes all jobs within a deadline.

Follow-up

  • Search a sorted array that has been rotated at an unknown pivot.
  • Return the first and last positions of a target that appears many times.
  • How would you search a sorted stream whose length you do not know?

How would you detect a cycle in a linked list?

Medium · Coding / Algorithms

Approach

  1. Baseline: walk the list and store visited node references in a hash set; a repeat means a cycle. O(n) time, O(n) space; compare node identity, not node values.
  2. Floyd's tortoise and hare: slow moves one step and fast moves two; if fast or fast.next becomes null there is no cycle, and if they meet there is one. O(n) time, O(1) space.
  3. Finding the cycle's entry: after they meet, reset one pointer to the head and advance both one step at a time; they meet at the entry, because the distance from the head to the entry equals the distance from the meeting point to the entry, modulo the cycle length.
  4. Cycle length: from the meeting point, advance one pointer until it returns, counting the steps.
  5. Edge cases: an empty list, one node pointing to itself, a two-node cycle; check fast and fast.next before reading fast.next.next.

Follow-up

  • Return the node where the cycle begins and prove why the reset trick works.
  • Find the duplicate in an array of n + 1 integers in the range 1..n using the same idea, without modifying the array.
  • How would you break the cycle once you have found it?

Describe how to merge two sorted arrays.

Medium · Coding / Algorithms

Approach

  1. Two pointers i and j: append the smaller of a[i] and b[j], advance that pointer, then copy whatever remains of the other array. O(m + n) time and O(m + n) space for the output; taking from the first array on ties (<=) keeps the merge stable.
  2. In-place variant, where the first array has m values plus n empty slots: fill from the back with k = m + n - 1, comparing the last unread elements, so no unread value is overwritten. If the second array runs out you are done; if the first runs out, copy the rest of the second. O(m + n) time, O(1) extra space.
  3. Contrast with concatenating and sorting, which costs O((m + n) log(m + n)) and ignores that both inputs are already sorted.
  4. Edge cases: one array empty, every element of one array smaller than the other, duplicates, negative values.
  5. Generalize: k sorted arrays merge with a min-heap of the current heads in O(N log k), which is also the merge step of external merge sort when the data does not fit in memory.

Follow-up

  • Merge k sorted arrays. What does the heap hold and what is the complexity?
  • Return the intersection of two sorted arrays instead of their union.
  • Find the median of two sorted arrays faster than merging them.

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?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.

Approach

  1. Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
  2. Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
  3. Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
  4. State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.

Follow-up

  • A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
  • The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?

Describe how you would approach optimizing a slow-loading web application.

Medium · Technical / Domain Questions

Approach

  1. Measure before changing anything: run Lighthouse and the DevTools Performance and Network panels on a throttled mobile profile, and read real-user Core Web Vitals at p75 (LCP good at 2.5 s or less, INP at 200 ms or less, CLS at 0.1 or less) to tell a slow server apart from a heavy client.
  2. If time to first byte is high, profile the slow endpoint: look for N+1 queries and missing indexes with EXPLAIN, then add caching (Cache-Control headers, a CDN for static assets, a server-side cache for hot reads).
  3. Cut transfer cost: Brotli or gzip compression, images resized and served as WebP or AVIF through srcset with loading="lazy" below the fold, content-hashed filenames so assets can carry long cache lifetimes.
  4. Cut JavaScript cost: inspect the bundle with an analyzer, code-split by route with dynamic import(), defer non-critical scripts, inline critical CSS so rendering is not blocked.
  5. Close the loop: re-measure on the same profile, compare p75 field data after release, and add a performance budget to CI so the regression cannot return silently.

Follow-up

  • The LCP element is a large hero image. What exactly do you change?
  • The first load is fast but clicks feel sluggish. How do you find the long tasks behind a poor INP?
  • How would you stop a later release from undoing these gains?

What are the differences between REST and GraphQL?

Medium · Technical / Domain Questions

Approach

  1. Model: REST exposes resources at URLs and uses HTTP verbs and status codes; GraphQL exposes one endpoint backed by a typed schema, and the client names the exact fields it wants through queries, mutations and subscriptions.
  2. Fetching: REST can over-fetch (unused fields) or under-fetch (several round trips for nested data); GraphQL returns a nested shape in one request, but naive resolvers cause N+1 database calls, usually fixed by batching with DataLoader.
  3. Caching: REST GET responses are cached by browsers and CDNs keyed on the URL, with ETags for revalidation; GraphQL POSTs to one URL bypass HTTP caching, so clients rely on normalized caches (Apollo, Relay) or persisted queries sent over GET.
  4. Errors and evolution: REST signals failure through status codes and often versions in the URL; GraphQL commonly returns 200 with an errors array next to partial data, and evolves by adding fields and marking old ones @deprecated.
  5. Operations: an open GraphQL endpoint needs depth and cost limits plus rate limiting by query cost; choose REST for simple, cache-heavy public resources and GraphQL when many clients need different shapes of related data.

Follow-up

  • Which would you pick for a mobile screen that lists bookings with nested customer and service details, and why?
  • How would you stop a client from sending a deeply nested query that overloads the database?
  • How do you cache GraphQL responses at the edge?

How do you manage state in a React application?

Medium · Technical / Domain Questions

Approach

  1. Classify the state first: local UI state (useState, useReducer), state shared by several components, server data, URL state (filters, page, selected id in query params) and form state; each kind has a different best home.
  2. Keep state local by default, lift it to the nearest common parent when siblings need it, and derive values during render instead of storing copies (useMemo only when the derivation is expensive).
  3. Use Context for low-frequency values such as the theme or the signed-in user; every consumer re-renders when the value changes, so split contexts and memoize the value, and move high-frequency shared state to a store with selectors (Redux Toolkit, Zustand).
  4. Treat server data as a cache, not app state: TanStack Query, SWR or RTK Query give deduplication, stale-while-revalidate, invalidation after mutations and optimistic updates with rollback.
  5. Update immutably so React's Object.is comparison sees a new reference, and reach for useReducer when one action changes several related fields.

Follow-up

  • When would you add Redux instead of staying with Context?
  • A list re-renders on every keystroke in a search box. How do you find the cause with the React Profiler and fix it?
  • Where should a search page keep its filters so the page can be shared and reloaded?

Can you discuss the principles of responsive web design?

Medium · Technical / Domain Questions

Approach

  1. Start with the viewport meta tag (width=device-width, initial-scale=1); without it mobile browsers lay the page out at a wide virtual width and scale it down.
  2. Build fluid layouts with relative units (%, rem, vw), max-width containers, flexbox, and CSS grid patterns such as repeat(auto-fit, minmax(16rem, 1fr)) that reflow without breakpoints.
  3. Write mobile-first min-width media queries, put breakpoints where the content breaks rather than at device widths, and use container queries (@container) when a component must adapt to its parent instead of the viewport.
  4. Make media flexible: img { max-width: 100%; height: auto }, srcset and sizes for resolution switching, <picture> for art direction, and aspect-ratio or width/height attributes to reserve space and avoid layout shift.
  5. Cover input and readability: tap targets of roughly 44 to 48 CSS pixels, no hover-only controls, fluid type with clamp(), then test in DevTools device mode and on at least one real phone.

Follow-up

  • What are the trade-offs between mobile-first and desktop-first CSS?
  • How would you make a wide data table usable on a narrow phone screen?
  • How do you stop a phone from downloading the desktop-sized image?

How would you approach designing a new feature for our booking platform?

Medium · Problem-Solving / Case Studies

Approach

  1. Start from the user problem, not the code: who uses the feature (the person booking or the business managing its calendar), what success looks like, and which constraints apply; agree acceptance criteria with product and design before choosing a design.
  2. Pick a concrete example and walk it through, for instance a waitlist for fully booked slots: entities (slot, booking, waitlist entry with a status), API endpoints, and the states an entry moves through (waiting, offered, accepted, expired).
  3. Handle the booking-specific hard parts: prevent double booking with a unique constraint on (resource_id, slot_start) or a conditional update, accept an idempotency key on create so a retried request cannot book twice, and store times in UTC with the business's time zone to survive DST changes.
  4. Plan the side effects: notifications sent through a queue, with a transactional outbox so the booking row and the event are written together.
  5. Ship it safely: feature flag, staged rollout, logs and metrics on the new flow, and tests for the race conditions as well as the happy path.

Follow-up

  • Two customers press book on the last open slot at the same moment. What happens in your design?
  • How do you make sure the confirmation notification is sent exactly once from the user's point of view?
  • Which metric tells you the feature worked, and what guardrail would make you roll it back?

If you were tasked with improving the user experience for our mobile app, what would be your first steps?

Medium · Problem-Solving / Case Studies

Approach

  1. Start with evidence: funnel analytics for the main flow to find the step with the largest drop-off, crash and ANR rates, app store reviews and support tickets; pick the problem with the biggest measurable cost.
  2. Measure performance, since it is often the UX problem: cold start time, time to first meaningful screen, dropped frames (above about 16 ms per frame at 60 Hz) and behaviour on slow or flaky networks.
  3. Check usability and accessibility with design: short usability sessions on the weakest step, screen reader labels, dynamic text sizes, contrast and tap target sizes.
  4. Choose targeted fixes: fewer steps, prefilled fields, skeleton screens instead of spinners, optimistic UI where a rollback is safe, cached data and clear retry paths when offline.
  5. Release behind a flag and compare against a control: one primary metric (for example, flow completion rate) and guardrails such as crash rate and latency.

Follow-up

  • You find ten problems. How do you decide which one to fix first?
  • How would you prove that the change, not seasonality, improved completion?
  • The slowest screen waits on three API calls. What do you change on the client and on the server?

Discuss how you would balance technical debt with new feature development.

Medium · Problem-Solving / Case Studies

Approach

  1. Make the debt visible and costed: list each item with what it costs now (incidents, hours lost, slower delivery of specific features) and what it risks, so it can be ranked next to feature work.
  2. Prioritise by interest rate: debt in code that changes often or blocks the next roadmap item comes first; stable code nobody touches can wait, however ugly it is.
  3. Pay it down with the work: refactor the area a feature already touches, and agree a standing share of capacity with the product manager rather than asking for a separate cleanup project.
  4. Reduce risk while doing it: write characterization tests before refactoring, use the strangler fig pattern to replace a component piece by piece, and keep changes behind flags.
  5. Give a real example from your own work: the debt, the trade-off you made, how you argued for it in business terms, and the measured result.

Follow-up

  • The product manager wants to skip the refactor to hit a deadline. What do you do?
  • How would you show that paying down a piece of debt actually paid off?
  • When would you rewrite a component instead of refactoring it?

How would you measure the success of a new implementation?

Medium · Problem-Solving / Case Studies

Approach

  1. Define success before launch: the goal, one primary metric tied to it (for example, completion rate of the flow the change touches), guardrail metrics that must not get worse, and a baseline measured beforehand.
  2. Track technical health from the first minute: error rate, p95 and p99 latency, resource usage and crash rate on dashboards, with alerts and a rollback threshold agreed in advance.
  3. Isolate the effect: an A/B test or a staged rollout with a control group, a sample size computed for the minimum effect worth detecting, and no stopping early because of peeking; if no experiment is possible, compare before and after while controlling for seasonality.
  4. Add qualitative signals: support tickets, user feedback and session reviews explain why a number moved.
  5. Write the decision rule down beforehand: what result means ship, iterate or roll back.

Follow-up

  • The primary metric improved but p99 latency got worse. Do you ship?
  • How do you separate a novelty effect from a lasting improvement?
  • How would you measure success for a backend refactor that users should not notice at all?

Design a URL shortening service. What considerations would you take into account?

Medium · System Design / Architecture

Approach

  1. Scope it: create a short link, redirect, optional custom alias and expiry, click analytics; redirects vastly outnumber creates, so design the read path first and estimate requests per second and storage.
  2. Generate keys: base62-encode a unique ID from a counter or ID allocator that hands out ranges to each server; seven base62 characters give 62^7, about 3.5 trillion codes. Random codes need a collision check through a unique constraint; truncated hashes of the long URL need collision handling too.
  3. Store a mapping keyed by short code (code -> long URL, owner, created_at, expires_at) in a key-value store or a relational table, partitioned by a hash of the code.
  4. Serve redirects from a cache (Redis or the CDN) in front of the store; a 301 is cached by browsers and cuts load but hides repeat clicks, while a 302 sends every click to the service and keeps analytics complete.
  5. Move analytics off the request path through a queue, and cover abuse: rate limits on creation, malicious URL screening, and non-sequential codes so links cannot be enumerated.

Follow-up

  • Should the same long URL always map to the same short code?
  • How do you handle a custom alias that someone else already owns?
  • How would you serve redirects with low latency in several regions?

How would you architect a scalable chat application?

Medium · System Design / Architecture

Approach

  1. Scope it: one-to-one and group chat, message history, online presence, delivered and read receipts, push notifications for offline users; estimate concurrent connections and messages per second.
  2. Connections: clients hold WebSocket connections to gateway servers behind a load balancer that supports long-lived connections, and a session registry (user -> gateway) in Redis tells the system where each user is connected.
  3. Message path: the client sends a message with a client-generated ID; the chat service writes it to storage partitioned by conversation ID with a per-conversation sequence number for ordering, then publishes it through pub/sub to the recipients' gateways, or to push notifications if they are offline.
  4. Storage: an append-heavy wide-column store (Cassandra-style) keyed by (conversation_id, sequence) suits writes and paginated history; large groups make fan-out on write expensive, so switch to fan-out on read above a size threshold.
  5. Delivery: at-least-once with acknowledgements, deduplication by message ID on the client, and resync from the last seen sequence number after a reconnect.

Follow-up

  • How do you keep message order consistent for a user on two devices?
  • How would presence work for millions of users without flooding everyone with updates?
  • A gateway server crashes. What happens to its users' connections and in-flight messages?

Discuss how you would handle data consistency in a distributed system.

Medium · System Design / Architecture

Approach

  1. Ask what must be strongly consistent (never selling the same slot twice, money) and what can be eventually consistent (counters, feeds, search indexes); under a network partition you must give up consistency or availability, so choose per operation.
  2. Within one datastore: single-leader replication, with synchronous or quorum writes when needed; with N replicas, R + W > N makes read and write quorums overlap; read-your-writes by reading from the leader or using session tokens.
  3. Across services, avoid two-phase commit where you can because a failed coordinator blocks participants; use a saga with compensating actions, a transactional outbox so the state change and its event commit together, and idempotent consumers because delivery is at-least-once.
  4. Control concurrent writers with unique constraints, optimistic locking (UPDATE ... WHERE id = ? AND version = ?), or locks with fencing tokens so a paused client holding a stale lock cannot write.
  5. Plan for drift: reconciliation jobs that compare systems and repair differences, and idempotency keys on every externally retried request.

Follow-up

  • The payment succeeded but writing the booking failed. Walk through the recovery.
  • Why is last-write-wins risky when server clocks drift?
  • What should a user see while an eventually consistent update is still propagating?

What strategies would you employ to ensure high availability for a web service?

Medium · System Design / Architecture

Approach

  1. Set the target first: an availability SLO such as 99.9% allows roughly 43 minutes of downtime a month, and each extra nine costs far more in redundancy and process.
  2. Remove single points of failure: several stateless instances behind a load balancer with health checks, spread across availability zones, with autoscaling; keep session state out of the instances.
  3. Make the data tier survivable: replicated databases with automated failover, backups that are regularly restored as a test, and caches treated as optional so a cache outage degrades speed rather than availability.
  4. Contain failures: timeouts on every call, retries with exponential backoff and jitter, circuit breakers, bulkheads, load shedding and graceful degradation of non-essential features.
  5. Reduce change risk and detect problems fast: rolling, blue-green or canary deploys with automatic rollback, alerts on SLO burn rate, runbooks, and a deliberate choice between active-passive and active-active across regions.

Follow-up

  • The primary database fails. Walk through what happens during and after failover.
  • Retries from clients are making an outage worse. How do you stop the retry storm?
  • How would you test that your failover actually works before a real incident?

Describe how you would approach the design of a microservices architecture.

Medium · System Design / Architecture

Approach

  1. Question the split first: a modular monolith is often the better start, and services pay off when teams need to deploy independently; draw boundaries along business capabilities (for a booking product, for example: accounts, availability, bookings, payments, notifications).
  2. Give each service its own data store and never share tables; services talk through versioned APIs (REST or gRPC) for queries that need an immediate answer and through events for everything else.
  3. Handle consistency without distributed transactions: sagas with compensating actions, a transactional outbox for events, idempotent handlers. Avoid long synchronous call chains, because availability multiplies (five services at 99.9% each give about 99.5% for the chain).
  4. Add the shared platform pieces: an API gateway, service discovery, auth propagated as tokens, and observability with distributed tracing, correlation IDs, centralized logs and per-service metrics.
  5. Deploy each service through its own CI/CD pipeline, keep contracts backward compatible, and use consumer-driven contract tests so one team's change does not break another's.

Follow-up

  • How would you migrate an existing monolith to services without a big-bang rewrite?
  • When would you advise against microservices?
  • A request is slow and crosses six services. How do you find which one is responsible?

One log partition stops advancing while the others drain

Easy · poison message · head-of-line blocking · consumer lag

Search results for a subset of tenants are hours stale; the rest are current. The projection consumer reports lag of zero on 15 of 16 partitions and 400,000 on one. Its error rate is flat and its CPU is idle. outbox_event has no pending rows older than a second, so the relay has published everything it holds. Identify the mechanism, give the ordered checks, and state what you do in the first ten minutes versus what you change permanently.

Approach

  1. Read the lag distribution first. A slow consumer lags everywhere; zero on fifteen partitions and 400,000 on one is not throughput. Idle CPU on the stuck partition means the consumer is not advancing its offset at all, which points at one message it cannot get past rather than at a rate problem.
  2. Exonerate the producer before touching the consumer. No pending outbox rows older than a second means the relay published, so the event exists in the log. This separates never sent from sent and never applied, which are different code paths and usually different owners.
  3. Read the message at the stuck offset and the handler's log lines for its event_id. A flat error rate with no progress has two explanations and you must distinguish them: the handler is throwing and the retry loop is swallowing it, or the handler is blocking on something and never returning. Idle CPU with no error lines favours the second.
  4. Mitigate before diagnosing further. Move the offending event to a dead-letter store and commit the offset past it. Adding consumers does nothing here, because a partition is consumed by exactly one member of the group, and the blast radius is every aggregate hashed to that partition, not only the aggregate that produced the bad event.

Follow-up

  • The dead-lettered event carried aggregate_version 7 and the projection had applied 6. What must the replay do differently if 8 and 9 landed in the meantime?
  • How do you show staleness to the user while the partition is behind, given the API already returns the projection's watermark?

Suggested practice checklist

The seven days follow the reported loop: JavaScript and React fundamentals, web delivery, the five coding problems, system design and case studies, a take-home style build, and behavioral stories. Practice-library exercises are used where they match.

Day 1 — Map the loop and pick your stack

  • Write the five reported stages in order and note which question groups you will prepare for each, remembering that candidates do not tie questions to stages.
  • Choose one language (JavaScript, Python or Java) for coding problems and list the must-have and extra skills from the role requirements, marking your weakest.
  • Write your CV walk-through and a short list of questions for the recruiter call.

Deliverable: A one-page loop map, a chosen coding language, a ranked list of weak skills and a written CV walk-through with recruiter questions.

Day 2 — JavaScript and React fundamentals

  • Write a closure example (loop variable in a callback, a counter factory, a stale effect) and explain each result out loud.
  • Build a small component tree and decide where each piece of state lives, using local state, lifted state, context and URL state, and justify the choices.
  • Write a two-minute spoken answer for each question and record it to check for vague wording.

Deliverable: Two code examples (closure and state placement) and two recorded spoken answers.

Day 3 — Web delivery: speed, responsiveness, APIs

  • Take any web page you know, measure it (network waterfall, bundle size, long tasks) and write the ordered fixes you would make for slowness.
  • Sketch a responsive layout with breakpoints, fluid units and a mobile-first stylesheet, and note the accessibility basics.
  • Make a REST versus GraphQL comparison table covering over-fetching, caching, versioning and error handling.

Deliverable: A measured slow-page fix list, a responsive layout sketch and a REST versus GraphQL table.

Day 4 — The five coding problems

  • Write reverse a string, two-number sum, binary search, linked-list cycle detection and merge of two sorted arrays from a blank file in your chosen language.
  • Run each against empty, single-element, duplicate and boundary inputs, and fix what fails.
  • Write down time and space complexity for each, plus one alternative solution (for two-sum, the sorted two-pointer version; for cycles, the visited set).

Deliverable: Five working solutions with edge-case tests and a complexity table with one alternative each.

Day 5 — System design outlines

  • Use one repeatable outline (requirements, read and write paths, storage, failure modes, trade-offs) to answer the URL shortener and chat application prompts.
  • Write a short answer for data consistency in a distributed system (strong versus eventual, idempotent retries) and for high availability (redundancy, health checks, graceful degradation).

Deliverable: Five outlined designs on one page each and one worked concurrency exercise.

Day 6 — Case studies and a take-home style build

  • Answer the new booking feature, mobile app experience, technical debt and measuring success prompts with a structure: users, goal, options, trade-off, metric.
  • Build a small booking-style feature (choose a slot, confirm, handle a slot that was just taken) with React state and a responsive layout.
  • Add tests for availability logic and write a README listing assumptions and what you cut.

Deliverable: Four structured case-study answers plus a small tested feature with a README.

Day 7 — Behavioral story bank and full run-through

  • Write five stories in the form situation, your decision, result and what you would change, covering conflict, prioritization, leading, feedback and a hard bug.
  • Give your answers to the reported behavioral prompts aloud and cut any 'we' that hides your own role.

Deliverable: A five-story bank, a rewritten bug story and spoken answers to every reported behavioral prompt.

Behavioral story bank for BookedBy

Candidates report the behavioral stage as a check on interpersonal skills and cultural fit. For a Software Engineer, that means stories about working through conflict, setting priorities, leading, giving and receiving feedback, and fixing bugs. Candidates also describe code review and collaboration with product managers and designers as part of the job, so favor stories from those settings. Have a real example ready for each prompt, told in the first person.

Describe a time when you had to work with a difficult team member. How did you handle it?

Medium · Behavioral / Leadership

Approach

  1. Choose a conflict where the other person's behavior really cost the team something, such as repeated missed handoffs or review comments that stalled a pull request, and where you acted rather than waited.
  2. Spend the first part of the story on understanding them: you talked one-to-one, asked what constraints or pressures they had, and separated the behavior from the person.
  3. Describe the concrete change you agreed on, for example a working agreement on review turnaround or a shared checklist for handoffs, and what happened afterwards.
  4. Be honest about the limit: say what you escalated, to whom, and when, if the direct conversation was not enough.
  5. Close with the result and one thing you would do differently, such as raising it a week earlier.

Follow-up

  • What if the person had not changed after that conversation?
  • At what point did you involve your manager, and what did you ask them to do?
  • What would you do differently if you ran that situation again?

How do you prioritize tasks when working on multiple projects?

Medium · Behavioral / Leadership

Approach

  1. Start with the method, then prove it with a real case: list everything competing for your time, note deadlines and who is waiting, and rank by cost of delay and by what unblocks others.
  2. Give one real example of two projects colliding, for instance a customer-facing bug fix and a planned feature, with what you did first, what you deferred and why.
  3. Show the communication step: you told the product manager or teammates what slipped, offered options and agreed on the trade-off in writing.
  4. Mention how you protect focus, such as batching reviews or time-boxing the interruptible work, and how you re-rank when priorities change mid-week.
  5. Avoid claiming you simply work harder; the strongest answers include something you said no to.

Follow-up

  • Two product managers each say their request is the top priority. What do you do?
  • How do you decide between fixing a bug and finishing a feature that has a date attached?
  • Tell me about a time your prioritization turned out wrong.

Give an example of a project where you took the lead.

Medium · Behavioral / Leadership

Approach

  1. Pick a project where you set the direction without necessarily having the title, and say how it started: you saw a gap, wrote the plan and got agreement from the people involved.
  2. Show the mechanics of leading: you broke the work into pieces, assigned them by strength, set up a place for questions and checked progress without micromanaging.
  3. Describe how you worked with product and design to settle scope, and one decision you made when people disagreed.
  4. Give a measurable result, such as shipped on the date, errors reduced or a metric moved, and credit teammates by name for what they did.
  5. Mention one thing that went wrong and how you recovered, since a story with no setback sounds rehearsed.

Follow-up

  • How did you handle a teammate who disagreed with your direction?
  • What did you delegate, and what did you keep for yourself and why?
  • What would you change about how you ran the project?

What motivates you to succeed in your role?

Medium · Behavioral / Leadership

Approach

  1. Give a pattern rather than a slogan: name two or three things that actually drive you, such as shipping something users touch, solving hard debugging problems or helping teammates improve.
  2. Back each driver with a specific moment from your work, for example the release where you watched a booking-style flow work for real users, so the answer sounds lived-in.
  3. Connect it to the reported shape of this job: building features with product managers and designers, reviewing code, fixing bugs that affect users. Say it as your motivation, not as a claim about the company.
  4. Be ready to name what drains you and how you handle it, which makes the answer credible.
  5. Avoid answering with money alone, or with generic passion that could fit any employer.

Follow-up

  • Tell me about a time your motivation dropped. What did you do?
  • Which part of this role would motivate you least, and how would you handle it?
  • How do you stay motivated on routine work like bug fixes or reviews?

How do you approach feedback, whether giving or receiving?

Medium · Behavioral / Leadership

Approach

  1. Cover both directions with one example each. For receiving, use a code review comment or a performance review point that stung, then say how you checked it against evidence and what you changed.
  2. For giving, describe your habit in code review: comment on the code and not the person, give a reason, suggest an alternative, and mark what is blocking versus optional.
  3. Describe how you handle sensitive feedback: privately, soon after the event, with a specific example and a question about their view first.
  4. Say how you ask for feedback yourself, for example asking a reviewer what they would have done differently on a recent pull request.
  5. Avoid saying you rarely get negative feedback or that you simply agree with everything; both undermine the answer.

Follow-up

  • Tell me about feedback you disagreed with. What did you do?
  • How would you give hard feedback to a more senior engineer?
  • How do you handle a review that turns into a long back-and-forth?

Describe a time when you had to troubleshoot a significant bug. What steps did you take?

Medium · Problem-Solving / Case Studies

Approach

  1. Pick a bug with real user impact and open with it: what broke, who noticed, how you knew the scale, and what you did to limit the damage (rollback, feature flag, or a quick mitigation).
  2. Walk through the debugging sequence: reproduce it, narrow the cause (recent deploys, logs, network panel or debugger, bisecting commits), form a hypothesis, then test it by changing one thing at a time.
  3. Say how you separated the root cause from the symptom and how you confirmed the fix, including the regression test you added.
  4. Finish with prevention: a monitor, an alert, a test or a process change, and a short write-up for the team.
  5. Avoid the version that jumps from 'it was broken' to 'I fixed it' with nothing in between.

Follow-up

  • How did you know you had found the root cause and not just a symptom?
  • What did you change so the same bug could not return unnoticed?
  • What would you have done if you could not reproduce it?