cswork / libraryOpen practice workspace

Interview guides · Bumble

Bumble Software Engineer Interview Guide

Bumble Software Engineer interview guide: four reported rounds, PHP, SQL, live pairing and system design questions, with a 7-day plan and behavioral prep.

The 60-second brief

Bumble builds consumer products that connect users with each other, and candidates describe engineering work at large user scale with heavy concurrency and real-time requirements. Reported work areas include recommendation and matching systems, backend infrastructure, and an AI initiative called Bee AI. The Software Engineer role, as candidates describe it, covers backend services, database query work, Backend For Frontend (BFF) layers for mobile and web clients, and integrating machine learning models into recommendation and matching pipelines. Candidates also describe owning what you deploy: monitoring, debugging and incident response, plus code review and architecture discussions with product managers, data scientists and mobile and web engineers.

This guide is for engineers preparing for Bumble's Software Engineer loop, at the levels candidates mention, from mid-level up to Staff and Principal. It covers the four reported rounds, the PHP, SQL, networking, coding and system design questions candidates describe, the behavioral prompts, and a seven-day plan. Questions are reported to vary by team and seniority, so treat the reported questions as patterns to practise, not a fixed list.

Candidates report four rounds over roughly 3-5 weeks: Recruiter Alignment, Technical Deep Dives, Live Pairing Sessions and Behavioral Evaluations. Formats are reported to differ by team, with backend, AI and infrastructure pods mentioned.

Focus areas

  • PHP
  • SQL
  • Live Coding

Overview

The twenty reported questions fall into four groups of five. Domain questions cover character encoding in PHP, how CORS works and how to troubleshoot it, optimizing database queries without standard joins, state and auto-refresh in real-time feeds, and mobile networking with backend serialization. Coding questions cover transforming nested arrays, a data manipulation problem on a shared coding platform, a self-updating feed with hidden state requirements, straightforward data-structure problems, and a take-home real-time reactive application you discuss afterwards. Design questions cover a recommendation or matching system under heavy concurrency, a resilient BFF, scaling database reads and writes for hundreds of millions of users, the trade-offs in your take-home, and identity and access management in a distributed setting. The last group is behavioral.

The topic mix candidates report is PHP, SQL, live coding, scalability and concurrency. That points to backend mechanics sitting beside algorithms: candidates report that questions go past algorithmic coding into things like HTTP, serialization, encoding and indexing. Reciting definitions will not be enough for these. Be ready to show them: a PHP snippet where strlen and mb_strlen disagree, a curl trace of a CORS preflight, an EXPLAIN plan that explains why a query got faster.

Feeds recur across the groups: auto-refresh and state in real-time feeds, a self-updating feed component, a real-time reactive take-home, and feed updates at scale. One working mental model of a feed pays off in several rounds. Know how new items are fetched (polling, long polling, server-sent events, WebSockets), how duplicates and ordering are handled, what state survives a refresh, and how the same feature changes shape when millions of clients hold it open.

Recruiter Alignment and Behavioral Evaluations bracket the technical rounds. Candidates describe the pairing sessions as collaborative, with clarifying questions and thinking aloud encouraged, so rehearse narrating while you code. For the behavioral stories, prepare real decisions and outcomes from your own work, written in the first person singular, and a specific reason for wanting this product's problems.

The Bumble interview process

These are the four rounds candidates describe, in the order they report. Timelines and formats are said to vary by team, so confirm the details for your pod with the recruiter.

Recruiter Alignment

Candidates describe the opening stage as a conversation with a recruiter to align on the role and expectations. Treat it as a two-way fit check. Bring a short account of your backend experience and a specific reason for this team, and use the call to find out which pod the role sits in, since reports say formats differ across backend, AI and infrastructure pods. Ask what the later rounds contain so you can aim the remaining preparation.

What to demonstrate

  • Shared understanding of the role and what is expected in it
  • How your background maps to the team and level you are interviewing for
  • Practical details of the rest of the loop

How to prepare

  • Write a short account of your recent backend work that names the languages and data stores you used; candidates describe PHP, Python and Go as backend languages for this role, and Redis as a plus.
  • Prepare a specific answer to why you want to work on this product's problems, using the same material as your answer to the career-motivation question.
  • Ask which pod the role is in, which language the pairing round expects, and whether a take-home is part of the loop.

Technical Deep Dives

Candidates describe in-depth technical interviews built around collaborative problem-solving and architectural reasoning. The reported question types that fit this format are domain fundamentals (PHP character encoding, CORS, database queries without standard joins, feed state and auto-refresh, mobile networking and serialization) and system design (matching or recommendation under heavy concurrency, a resilient BFF, scaling reads and writes, identity and access management). Answer by stating requirements and assumptions, then walk the read path and the write path separately, then say what fails and how the system degrades.

What to demonstrate

  • Architectural reasoning, including trade-offs between consistency and availability
  • Depth in backend fundamentals: databases, HTTP, encoding and serialization
  • Working through a problem collaboratively instead of reciting a memorised answer

How to prepare

  • Be able to demonstrate the single-byte versus multi-byte difference in PHP: strlen against mb_strlen, mb_substr, UTF-8 byte lengths, and where encoding must be declared (HTTP Content-Type, database connection, JSON output).
  • Walk through a CORS failure with curl: simple versus preflighted requests, the OPTIONS response, the Access-Control-Allow-* headers, and why credentialed requests need an explicit origin rather than a wildcard.
  • Prepare one design each for a matching service, a BFF layer and read/write scaling, each with a list of failure modes and a statement of where stale data is acceptable.

Live Pairing Sessions

Candidates describe collaborative coding sessions where you work next to the interviewer on a technical challenge. The coding questions reported for this loop are transforming nested arrays, a data manipulation problem on a shared coding platform, a self-updating feed with hidden state requirements, and straightforward data-structure problems where correctness and edge cases come first. Reported live scenarios also include a miniature Twitter-like app with auto-refresh in a coding sandbox and fixing a failing test in an unfamiliar codebase. Ask clarifying questions, think aloud, get a correct simple version working, then improve it.

What to demonstrate

  • Working, correct code with edge cases handled
  • Communication while coding, including how you handle bugs and unfamiliar platform APIs
  • Incremental implementation: core behavior first, optimization afterwards

How to prepare

  • Write flatten, group and filter-by-depth functions for nested arrays both recursively and with an explicit stack, and state complexity and behavior at deep nesting.
  • Build a small auto-refreshing feed from scratch and list the hidden requirements you would ask about first: ordering, duplicates, what survives a refresh, error handling.
  • Practise reading a failing test in code you have not seen: run it, read the assertion, find the smallest failing input, and narrate each step.

Behavioral Evaluations

Candidates describe the final interviews as an assessment of cultural fit and behavioral alignment with the team. Reported prompts include pride in a complex project, handling technical disagreements, why you want to build a career at Bumble, debugging an unfamiliar codebase under pressure, and balancing delivery speed against code quality; reported scenarios also include a project whose scope changed drastically. Prepare stories where you made the decision and can state the result, and keep them in the first person singular.

What to demonstrate

  • Project ownership and what you learned from it
  • Collaboration with peers and with product, data and mobile colleagues, including disagreements
  • Motivation for joining and fit with the team
  • Handling ambiguity and changing scope

How to prepare

  • Write four stories (a complex project, a technical disagreement, debugging under pressure, a speed-versus-quality call) and map each to the reported prompts so one story can serve more than one.
  • Prepare a scope-change story that shows your technical plan before and after the change.
  • Prepare a motivation answer built from using the product and from the reported work areas: recommendation and matching, BFF layers, and scale.

Where candidates lose points

Coding in silence, or starting the feed component without asking what its hidden requirements are

Before writing code, ask about ordering, duplicates, what survives a refresh, error handling and input size, and say your assumptions out loud. Candidates report the pairing format as collaborative, so use the interviewer. Get a correct simple version working, then optimize.

Treating the query-without-joins question as a trick or answering with 'add an index'

Name the real alternatives and their costs: an EXISTS or IN subquery, a batched lookup by ids in the application, and a denormalised or precomputed column. For each, say which index it uses and what happens when the table grows tenfold. Show it with EXPLAIN output you have actually read.

Giving shallow answers on PHP encoding or CORS, such as 'use UTF-8' or 'add a wildcard header'

Know that strlen counts bytes while mb_strlen counts characters in a stated encoding, and where charset must be declared end to end. For CORS, distinguish simple requests from preflighted ones, describe the OPTIONS response and the Allow headers, and explain why credentialed requests cannot use a wildcard origin.

Drawing a component diagram for the matching or BFF design without separating read and write paths, or saying what fails

State requirements first, then trace the read path and write path separately with their own bottlenecks. For every dependency, say what the client sees when it is down for an hour, and name where stale data is acceptable and where it is not.

Behavioral answers told as 'we' with no personal decision, no result, and nothing you would change

Pick stories where you made the call, state the options you weighed, give a result you can verify, and add one thing you would do differently. Do the same for your take-home: be ready to defend each architectural choice and name what you would change at higher load.

Questions & practice

Write a function to manipulate and transform nested arrays efficiently.

Medium · Coding & Algorithms

Approach

  1. Clarify the shape before coding: arbitrary or fixed depth, mixed scalars and arrays, whether keys must be preserved (PHP associative arrays), whether to return a new structure or mutate in place, and the expected output for an empty nested array.
  2. For flattening or deep mapping, use a depth-first walk that visits each element once: O(N) time over all elements and nested arrays, O(D) stack for depth D. For very deep input, replace recursion with an explicit stack to avoid stack overflow.
  3. Avoid hidden quadratic work: calling array_merge($result, ...) inside a loop copies the growing result each time, so append to one accumulator instead (in JS, push rather than repeated concat). In PHP, array_walk_recursive visits leaves only and discards structure, so it fits plain flattening but not transforms that need the path.
  4. Offer the useful variants: flatten with dotted path keys ('user.address.city') and its inverse, flatten to a given depth only (JS Array.prototype.flat(depth)), and a generator version (PHP yield / yield from) that streams leaves without building the whole result in memory.
  5. Edge cases to test aloud: empty arrays at any level, null values versus missing keys, numeric versus string keys colliding after flattening, and cyclic references (objects in JS, references in PHP), which need a visited set.

Follow-up

  • Flatten only to depth 2 and keep deeper arrays intact. What changes?
  • Write the inverse: rebuild the nested array from dotted keys. How do you handle conflicts such as 'a' and 'a.b' both present?
  • The input is a 2 GB JSON file. How do you process it without loading it all?

Solve a practical data manipulation problem on a shared collaborative coding platform during a live session.

Medium · Coding & Algorithms

Approach

  1. Restate the task with a tiny concrete input and the exact expected output, then ask about duplicates, missing or null fields, ordering of the result, case and whitespace normalisation, and input size.
  2. Most data manipulation tasks reduce to one pass with a hash map (grouping, counting, deduping, indexing by key): O(n) time and O(k) space for k keys. Sort only when the output order requires it (O(n log n)), and use a size-k heap for top-k (O(n log k)).
  3. Get a correct version running in the shared editor first, run it on the example, then add the edge cases: empty input, a single record, ties, malformed rows. Keep small, named helper functions so changes in a new requirement touch one place.
  4. State complexity and memory once it works, then improve only the part that matters, for example replacing a nested-loop lookup (O(n·m)) with a prebuilt map (O(n + m)).

Follow-up

  • The input is a stream too large for memory. How does the solution change?
  • Two groups tie on the ranking value. How do you make the output deterministic?
  • Which test cases would you add before shipping this?

Implement a self-updating feed component with hidden state requirements under strict time constraints.

Medium · Coding & Algorithms

Approach

  1. Model state explicitly before writing UI code: items keyed by id, an ordered id list, the newest cursor, pending new items, and a loading/error status. A reducer with named actions (refreshSucceeded, itemUpdated, itemDeleted) makes requirements revealed mid-task cheap to add.
  2. Schedule refreshes with a recursive setTimeout that starts the next timer only after the current fetch settles. setInterval can overlap requests when a fetch is slower than the interval. Clear the timer and abort in-flight requests (AbortController) when the component unmounts.
  3. Guard against stale responses: tag each request with an incrementing id and ignore any response older than the latest applied one, so a slow early refresh cannot overwrite newer data.
  4. Merge rather than replace: dedupe by id, sort by timestamp with id as tie-breaker, update edited items in place, and remove deleted ones. Keep the user's scroll position by holding new items behind a 'new items' indicator.
  5. Under time pressure, ship the smallest working loop first (fetch, render, refresh), then add pause-when-hidden, retry with backoff and the banner. Test timing with fake timers (vi.useFakeTimers / jest.useFakeTimers) instead of waiting in real time.

Follow-up

  • Two refreshes resolve out of order. What does the user see, and how do you prevent it?
  • The user has scrolled halfway down when new items arrive. What should happen?
  • How would you test the refresh behaviour without real network calls or real time?

Write clean, working code for straightforward data structure problems, prioritizing correctness and edge cases

Medium · Coding & Algorithms

Write clean, working code for straightforward data structure problems, prioritizing correctness and edge cases first.

Approach

  1. Before coding, list the edge cases out loud and agree on the expected output for each: empty input, one element, all duplicates, negatives or zero, already-sorted or reverse-sorted input, and very large values (overflow in fixed-width integer languages).
  2. Choose the structure from the operation you repeat: a hash map or set for O(1) average lookups, a stack for matching and nesting problems, a queue for BFS, two pointers on sorted arrays, a heap for top-k or running minimum.
  3. Write the plainest correct version first, with clear names and explicit loop bounds (inclusive versus exclusive), then state its time and space complexity before optimising.
  4. Dry-run the code on one normal case and one edge case, tracing variables line by line, and add a few asserts or test calls in the editor so correctness is shown rather than claimed.

Follow-up

  • Can you do it in O(1) extra space? What do you give up?
  • How does the solution change if the input is already sorted?
  • Which input would break your first version, and why?

Complete a take-home implementation of a real-time reactive application and be ready to discuss trade-offs.

Medium · Coding & Algorithms

Approach

  1. Scope ruthlessly: list the required behaviours from the brief, build those end to end first, and record every assumption and shortcut in a README with run instructions, trade-offs and what you would do next.
  2. Separate layers: a transport module (WebSocket, SSE or polling behind one interface), a state store that applies events, and views that subscribe to the store. Swapping polling for a socket should not touch the UI.
  3. Handle real-time edge cases explicitly: reconnect with exponential backoff and resubscribe; detect gaps with sequence numbers and refetch; make event application idempotent by id and version so duplicates and replays are harmless; roll back optimistic updates when the server rejects them.
  4. Test the state logic without a network: feed the reducer or store scripted event sequences (out of order, duplicated, after a disconnect) and assert on the resulting state, with a fake transport for integration tests.
  5. Keep the commit history small and readable, and prepare to explain each choice (library, transport, state approach) with the alternative you rejected.

Follow-up

  • How would this design change to support many thousands of concurrent clients?
  • Why did you choose that transport over the alternatives?
  • Which part of the code would you rewrite first with more time?

How do you optimize complex database queries and handle relational data structures without relying on standard

Medium · Technical / Domain

How do you optimize complex database queries and handle relational data structures without relying on standard join operations?

Approach

  1. Measure before rewriting: run EXPLAIN / EXPLAIN ANALYZE (Postgres) or EXPLAIN plus EXPLAIN ANALYZE (MySQL 8.0.18+) and look for full scans, filesorts or temporary tables, and estimated rows far from actual rows. Fix the plan's biggest cost first.
  2. Index for the access pattern: in a composite index, put equality columns first, then the range or sort column, and add selected columns for a covering index. Name what defeats an index: a function on the column (WHERE DATE(created_at) = ..., rewrite as a half-open range), a leading-wildcard LIKE, and implicit type casts.
  3. Without joins, use an application-side join: query the parent rows, collect their ids, fetch the children in one batched WHERE id IN (...) query, and stitch them in a hash map. That is O(n + m) work in two round trips, instead of the N+1 pattern that issues one query per row. Chunk very large IN lists.
  4. Other join-free shapes: EXISTS / IN subqueries for semi-joins, denormalised columns (a copy of a display name, a counter) kept current on write, and summary tables or materialised views refreshed on a schedule. Each moves cost to the write path and adds a staleness window you must state.
  5. Explain when joins are off the table: data sharded across databases, or services owning separate stores. Paginate with keyset pagination (WHERE (created_at, id) < (?, ?) ORDER BY created_at DESC, id DESC LIMIT n) instead of a large OFFSET, which scans and discards every skipped row.

Follow-up

  • How do you keep a denormalised counter correct when many requests update it at once?
  • The two tables now live on different shards. How does the query change?
  • When would a plain join be the better choice after all?

Denormalise tenant onto revisions and backfill it live

Hard · denormalisation · online migration · backfill

resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.

Approach

  1. Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
  2. Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
  3. Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
  4. Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.

Follow-up

  • The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
  • How do you verify the backfill actually finished, given rows are still being inserted while it runs?

What are the nuances of character encoding on single-byte versus multi-byte systems in PHP?

Medium · Technical / Domain

Approach

  1. Start from the core fact: a PHP string is a byte array with no encoding attached. strlen() counts bytes, so strlen('é') is 2 in UTF-8, while mb_strlen('é', 'UTF-8') is 1. Always pass the encoding to mb_* functions explicitly rather than relying on mb_internal_encoding().
  2. Name the byte-based functions that corrupt multi-byte text: substr() can cut through a multi-byte sequence and leave invalid UTF-8, str_split() splits bytes, and strtoupper() works on bytes (ASCII-only since PHP 8.2). Use mb_substr, mb_str_split and mb_strtoupper instead. For PCRE, add the /u modifier, or patterns match bytes; with /u, invalid UTF-8 input makes preg_match return false.
  3. Treat encoding as an end-to-end chain: HTTP Content-Type charset, PHP default_charset, the DB connection charset and the column charset. In MySQL, 'utf8' means utf8mb3 (at most 3 bytes), so emoji need utf8mb4 on both the column and the connection (charset=utf8mb4 in the PDO DSN).
  4. Validate at the boundary with mb_check_encoding($s, 'UTF-8') and convert legacy single-byte input (ISO-8859-1, Windows-1252) once with mb_convert_encoding. Know the failure modes: json_encode returns false on invalid UTF-8 unless JSON_INVALID_UTF8_SUBSTITUTE is set, and htmlspecialchars returns an empty string unless ENT_SUBSTITUTE is set.
  5. Edge cases: mb_* functions count code points, not what the user sees as one character. A flag, a skin-tone emoji or a letter with a combining accent spans several code points, and grapheme_strlen / grapheme_substr from intl handle those. To diagnose mojibake such as 'é', inspect the bytes with bin2hex to find where UTF-8 was decoded as Latin-1 and encoded again.

Follow-up

  • How would you truncate a user-entered bio to 100 visible characters without splitting an emoji?
  • Emoji show up as '????' after a database round trip. Which layers do you check, and in what order?
  • What happens to strlen-based length validation when a client sends UTF-16 or Windows-1252 input?

What strategies do you use for managing state and auto-refresh mechanisms in real-time feeds?

Medium · Technical / Domain

Approach

  1. Choose the transport from the update pattern. Short polling is simple but wasteful; long polling cuts empty responses. Server-Sent Events push one-way over HTTP and reconnect with Last-Event-ID. WebSockets are bidirectional but need connection-aware load balancing. On mobile, a push notification can tell the app to fetch rather than holding a socket open.
  2. Keep client state cursor-based: store the newest item seen (timestamp plus id as a tie-breaker) and ask for items after it. Offset pagination shifts when new items arrive and causes duplicates or gaps. Keep items in a map keyed by id plus an ordered id list, so a refresh is a dedupe-and-merge, not a replace.
  3. Do not move content under the reader: buffer new items behind a 'N new posts' indicator and insert them on tap or when the user is at the top, anchoring scroll position to the first visible item.
  4. Make refresh cheap and polite: pause when the page or app is in the background (Page Visibility API on the web), back off exponentially with jitter on errors, and use ETag / If-None-Match so an unchanged feed returns a 304 with no body.
  5. Handle changes to items already shown (edits, deletions, blocked authors) with versioned update or tombstone events, and say how the server builds the feed: fan-out on write into per-user lists (for example Redis sorted sets scored by time) versus fan-out on read, with a hybrid for accounts that have very large audiences.

Follow-up

  • A client reconnects after being offline for an hour. How do you fill the gap, and when do you reset the feed instead?
  • Two items have the same timestamp. How do you keep ordering stable across refreshes?
  • How do you scale WebSocket connections across many servers?

How do you approach mobile app networking and backend data serialization for high-throughput clients?

Medium · Technical / Domain

Approach

  1. Cut round trips first: on high-latency mobile links, the number of requests matters more than bandwidth. Aggregate screen data in one endpoint, batch small calls, reuse connections (keep-alive, TLS session resumption), and use HTTP/2 multiplexing. HTTP/3 over QUIC avoids TCP head-of-line blocking and survives network switches through connection migration.
  2. Pick the serialization format with its trade-offs. JSON is readable and universal but verbose and slower to parse. Protocol Buffers are compact and typed, and they evolve safely if you only add fields and never reuse or renumber a field number. Compress responses with gzip or brotli, and return only the fields the screen renders.
  3. Design for old clients: installed app versions stay in use long after a release, so changes must be backward compatible. Parsers ignore unknown fields, new fields are optional with defaults, and a minimum-supported-version check is the last resort for breaking changes.
  4. Make the network layer resilient: set explicit connect and read timeouts, retry only idempotent requests with exponential backoff and jitter, attach an idempotency key to writes so a retry after a timeout cannot create a duplicate, and queue writes while offline.
  5. Use HTTP caching (Cache-Control, ETag), cursor pagination, prefetching of the next page, and image variants sized for the device, and measure payload size and p95 latency per endpoint from the client side.

Follow-up

  • How would you roll out a field rename while three older app versions are still in use?
  • When would you keep JSON instead of switching to a binary format?
  • A user taps 'send' on a flaky connection and the request times out. How do you avoid a duplicate on retry?

How would you design a recommendation or matching system capable of handling massive concurrency?

Medium · System Design & Architecture

Approach

  1. Fix the requirements and split the paths: the read path serves a ranked batch of candidates for a user; the write path records swipes or likes at high volume; a match is a mutual like. Ask for scale (users, QPS, latency target) and how fresh results must be.
  2. Candidate generation then ranking: filter on hard constraints (location via geohash or S2 cells, stated preferences), exclude already-seen profiles (a per-user seen set, or a Bloom filter whose false positives only skip some unseen profiles and never repeat a seen one), score with a ranking model, and precompute per-user queues in a cache such as Redis so serving is a cheap pop.
  3. Detect matches safely under concurrency: store like edges keyed by (liker, likee) and, on A likes B, look up (B, A). If both like at the same moment, both checks can miss, so use one row per canonical pair (min id, max id) updated atomically, or a conditional write, so exactly one match is created and creation is idempotent.
  4. Partition swipe data by user id so a user's writes and lookups hit one shard; the reverse check is a point lookup on the other user's key. Send swipe events to a log for model features, counters and notifications instead of doing that work synchronously.
  5. Plan for skew: very popular profiles receive far more inbound likes and impressions, so cap their exposure in ranking, shard hot counters, and refresh queues when a user changes preferences or location.

Follow-up

  • Two users like each other in the same millisecond. Walk through how you avoid zero or two matches.
  • A user changes their preferences. How quickly do recommendations update, and what work does that trigger?
  • Which component fails first at 10x traffic, and how do you protect it?

Walk through your approach to designing a resilient Backend For Frontend (BFF) architecture.

Medium · System Design & Architecture

Approach

  1. Define the role: a BFF is a thin layer per client type (iOS, Android, web) that aggregates downstream services and shapes responses for specific screens. It holds presentation and orchestration logic, not core business rules, which stay in the domain services.
  2. Fan out in parallel: call independent downstreams concurrently so latency approaches the slowest call, not the sum. Tail latency compounds with fan-out: if each call is slow with probability p, at least one of n is slow with probability 1 - (1 - p)^n.
  3. Build resilience per dependency: timeouts inside an overall request budget shorter than the client's timeout, circuit breakers, bulkheads (separate connection pools per downstream), and retries only for idempotent calls with backoff, jitter and a retry budget.
  4. Degrade gracefully: mark each section of a screen response as required or optional, return partial data with the optional sections omitted or served from cache, and let the client render what it received.
  5. Keep it stateless and horizontally scaled, validate auth once and propagate identity downstream, version responses for older app releases, and trace every request across downstream calls to show which dependency caused a slow response.

Follow-up

  • One downstream's p99 rises to two seconds. What does the user see, and what does the BFF do?
  • One BFF per platform or one shared BFF? What decides it?
  • How do you stop the same logic being copied into several BFFs?

How do you scale database read and write operations for an application serving hundreds of millions of users?

Medium · System Design & Architecture

Approach

  1. Start from the workload: the read/write ratio, the hottest queries, data size and growth. Fix query plans and indexes and add connection pooling (for example PgBouncer) before adding infrastructure, since many app instances can exhaust database connections long before CPU runs out.
  2. Scale reads with cache-aside caching (TTL plus invalidation on write) and read replicas. State the cost: replica lag breaks read-your-own-writes, so route a user's reads to the primary for a short window after they write, or wait for the replica to reach the write's log position.
  3. Scale writes by batching, moving non-critical writes to a queue, and sharding by a key such as a hash of user id so one user's data lives on one shard. Use many logical shards mapped to fewer physical nodes, or consistent hashing, so adding capacity moves only part of the data.
  4. Name what sharding makes expensive: cross-shard queries and transactions. Avoid them by choosing the key from the main access pattern, and use an outbox or saga for multi-shard workflows. Handle hot keys with sharded counters or in-memory increments flushed periodically.
  5. Match the store to the access pattern: relational for transactional data, a wide-column or key-value store for append-heavy, key-addressed data such as activity logs.

Follow-up

  • How do you add shards or reshard without downtime?
  • A user updates their profile and immediately sees the old version. What happened, and how do you fix it?
  • An operation touches two users on different shards. How do you keep it consistent?

Discuss the architectural decisions and trade-offs made in your take-home system implementation.

Medium · System Design & Architecture

Approach

  1. Prepare a short walkthrough of your own submission: the main components, how data flows through them, and the one or two decisions that shaped everything else.
  2. For each major decision, name the alternative you rejected and what your choice costs, for example an in-memory store versus a database, or polling versus WebSockets, and the point at which you would switch.
  3. State known limitations before anyone finds them: what breaks with multiple server instances (in-memory state, no shared pub/sub), missing tests, error paths you skipped, and what production would need (persistence, auth, monitoring).
  4. Re-read your code before the interview so you can extend it live; an added feature or changed requirement against your own code is a likely next step, so know where it would go.
  5. Explain your testing strategy and failure handling concretely: which logic is unit-tested, how you would test reconnects or concurrent updates, and what the app does when the backend is unavailable.

Follow-up

  • What would you change to support 100 times more users?
  • Which part of the submission are you least satisfied with, and why?
  • Add a new requirement to the code now: where does it go?

How do you design identity and access management layers securely in a distributed environment?

Medium · System Design & Architecture

Approach

  1. Separate authentication from authorization and centralise identity: an identity provider issues tokens through OAuth 2.0 / OpenID Connect. Use short-lived access tokens plus refresh tokens that are rotated on each use and stored securely; hash passwords with bcrypt or Argon2, and rate-limit login attempts.
  2. Let services verify tokens locally: sign JWTs with an asymmetric key (RS256 or ES256) and publish public keys through a JWKS endpoint with a kid header, so keys rotate without downtime. Pin accepted algorithms to block alg=none and HS256/RS256 confusion attacks.
  3. Address revocation, the weak point of stateless tokens: keep access-token lifetimes short, revoke refresh tokens server-side, and for immediate logout check a per-user token version or a jti denylist held in a fast cache.
  4. Secure service-to-service calls with mTLS or workload identity, and give each service least-privilege scopes. Do not forward a user's broad token everywhere; exchange it for a narrower, audience-restricted token.
  5. Enforce coarse checks (valid token, scopes) at the gateway and object-level checks (does this user own this resource) inside the service that owns the data, to prevent insecure direct object references. Log every authorization decision for audit.

Follow-up

  • A user reports a stolen phone. How do you log them out everywhere right away?
  • How do you rotate the token-signing key without logging everyone out?
  • Where do you enforce 'a user can read only their own conversations', and why there?

Can you explain how Cross-Origin Resource Sharing (CORS) works and how you troubleshoot related issues?

Medium · Technical / Domain

Approach

  1. Explain the model: browsers enforce the same-origin policy, where an origin is scheme + host + port. CORS lets a server opt in by returning Access-Control-Allow-Origin. Non-browser clients (curl, native mobile code) are never subject to it, which is why a call can work in Postman and fail in a page.
  2. Separate simple requests from preflighted ones. GET, HEAD or POST with only safelisted headers and a form, multipart or text/plain content type go straight through. A JSON body, PUT/DELETE or a custom header such as Authorization triggers an OPTIONS preflight with Access-Control-Request-Method/-Headers, and the server must answer with a 2xx plus matching Allow-Methods, Allow-Headers and optionally Max-Age.
  3. Cover credentials: with cookies, the client sets credentials: 'include', the server returns Access-Control-Allow-Credentials: true, and Allow-Origin must be the exact origin, never '*'. When you echo the origin from an allowlist, add Vary: Origin so a CDN or cache does not serve one origin's headers to another. Never reflect an arbitrary Origin with credentials.
  4. Troubleshooting sequence: read the exact console message, then the Network tab, to see whether the preflight or the actual request failed. Reproduce outside the browser with curl -i -X OPTIONS -H 'Origin: https://app.example' -H 'Access-Control-Request-Method: PUT'. Common causes are auth middleware rejecting OPTIONS with 401, a redirect on the preflight, a 500 whose error page lacks CORS headers, and duplicate Allow-Origin headers set by both app and proxy.
  5. Mention Access-Control-Expose-Headers: without it, JavaScript cannot read custom response headers such as pagination cursors, even when the request succeeds.

Follow-up

  • Is CORS a protection against CSRF? Why or why not?
  • Preflights are adding a round trip to every API call from the web app. How would you reduce that?
  • The CORS error appears only in production behind the CDN. What do you check?

Describe a situation where you had to debug an unfamiliar codebase under pressure.

Medium · Behavioral & Culture Fit

Approach

  1. Choose a real incident where you led the investigation, and give the stakes in a sentence or two: what was broken, who was affected, and why it was urgent.
  2. Show the method step by step: reproduce or confirm the symptom, check what changed (recent deploys, config, traffic), then find the entry point from a stack trace, log line or route and read outward from it, using tests, git log and git blame to learn the code's intent. Use git bisect when it was a regression.
  3. Separate mitigation from root cause: say how you stopped the damage first (rollback, feature flag, disabling a path), then how you tested hypotheses, cheapest to falsify first, adding targeted logging where the code was opaque.
  4. Close with the fix, the regression test you added, and what you changed so the next person would find it faster (a runbook, better logs, a doc). The story fails if it is guesswork that happened to work, or if someone else found the fix.
  5. Rehearse the live version as well: talking through how you would locate and fix a failing test in an unfamiliar repository uses the same sequence.

Follow-up

  • How did you decide where to start reading in that codebase?
  • What would you have done if you could not reproduce the problem locally?
  • What did you change afterwards so the same issue would be quicker to diagnose?

Suggested practice checklist

The seven days follow the four reported rounds and question groups: PHP and web fundamentals, SQL, nested-data coding, feeds and real-time state, system design, then behavioral stories and a mock loop. Each day ends with something you can check.

Day 1 — Map the loop and choose your weakest areas

  • Write the four reported rounds in order (Recruiter Alignment, Technical Deep Dives, Live Pairing Sessions, Behavioral Evaluations) and next to each the question types it can contain: domain fundamentals, coding, design, behavioral.
  • Score yourself from 1 to 5 on PHP encoding, CORS, SQL without joins, nested-array coding, feed state, system design and behavioral stories, and circle the three lowest.
  • Write the questions you will ask the recruiter: which pod, which language the pairing round expects, and whether a take-home is part of the loop.

Deliverable: A one-page grid of rounds against topics with your scores, plus a written list of recruiter questions.

Day 2 — PHP encoding, CORS and mobile networking

  • Write a short PHP script that compares strlen with mb_strlen and substr with mb_substr on a string containing accented letters and an emoji, printing bytes against characters. List where encoding must be declared: HTTP Content-Type, database connection, JSON output.
  • Reproduce a CORS failure with two local ports, fix it with response headers, then add a credentialed request and a custom header to see the preflight OPTIONS request.
  • Outline mobile networking and serialization choices: pagination, payload trimming, compression, response versioning, retry with backoff and idempotency keys.

Deliverable: A tested PHP script with its output, a CORS troubleshooting checklist from your reproduction, and a one-page networking and serialization outline.

Day 3 — SQL without relying on joins

  • Take a feed or matches schema (users, matches, messages). Write one query three ways: a join, an EXISTS or IN subquery, and a two-step application-side lookup using batched ids. Run EXPLAIN on each and note which uses an index.
  • Design indexes for the hot queries: composite column order, covering indexes, and what makes an index unusable (a function on the column, a leading wildcard, a type mismatch).
  • Work through the practice drill on denormalising a column and backfilling it live, then write down when you would denormalise instead of joining.

Deliverable: Three versions of one query with EXPLAIN notes, an index list with reasons, and the completed denormalisation drill.

Day 4 — Nested arrays and data-structure coding

  • Write flatten, group-by-key and filter-by-depth for nested arrays, once recursively and once with an explicit stack, in the language you expect to use in the pairing round. State time and space complexity and what happens at deep nesting.
  • Solve three straightforward data-structure problems (hash-map counting, two pointers, stack matching), writing the edge cases first: empty input, a single element, duplicates, null values.
  • Redo each one in a shared online editor while talking out loud, and record yourself once to check that you narrate decisions.

Deliverable: Working solutions for the nested-array functions and the three data-structure problems, each with a list of edge-case tests.

Day 5 — Feeds, real-time state and the take-home

  • Build a small feed in a plain editor: a list, a post box, and auto-refresh that polls for new items, dedupes by id, keeps scroll position, pauses when the tab is hidden and shows an error state.
  • Write the hidden-requirement questions you would ask first: ordering, duplicates, what is kept after a refresh, failure handling, how many items.
  • Write the trade-off note you would present for it: polling versus long polling versus server-sent events versus WebSockets, client versus server state, optimistic updates, and what changes at higher traffic.

Deliverable: A working feed app, a list of clarifying questions, and a one-page trade-off note you can talk through when discussing a take-home.

Day 6 — System design: matching, BFF, scaling, identity

  • Design a matching or recommendation service under heavy concurrency: candidate generation, ranking, a Redis cache layer, the write path for user actions, and how you avoid a single point of failure.
  • Design a BFF for mobile clients: one aggregated call per screen, per-dependency timeouts, fallbacks, caching, response versioning, and what the screen shows when each downstream is slow.
  • Write a scaling plan for a database serving hundreds of millions of users: read replicas, caching, partition key and hot keys, asynchronous writes, and a migration without downtime.
  • Outline identity and access management: token issuance, short-lived access tokens with refresh, service-to-service authentication, per-service authorization, revocation and secret handling.

Deliverable: Four one-page designs, each with a read-path and write-path diagram and a table of failure modes.

Day 7 — Behavioral stories and a full mock loop

  • Write four stories in situation, decision, result form (complex project, technical disagreement, debugging an unfamiliar codebase, speed versus quality), each with one real number or visible result and one thing you would change.
  • Write your answer to why you want to build your career at Bumble, using the product and the reported work areas.
  • Run a mock loop: a recruiter-style walkthrough, one pairing problem narrated aloud, one design, two behavioral prompts. Note where you went silent or skipped edge cases.

Deliverable: Four written stories, a written motivation answer and a mock-loop log with three fixes to make.

Behavioral stories for the final rounds

Candidates describe the final stage as an assessment of cultural fit and behavioral alignment with the team, and the reported prompts cover pride in a complex project, technical disagreement, motivation for joining, debugging under pressure and speed versus quality. Prepare real decisions from your own work, with the options you weighed, the result, and one thing you would change. Build the stories first, then map each one to several prompts.

  1. Why do you want to build your career at Bumble, and how do your values align with its mission?
  2. Describe a situation where you had to debug an unfamiliar codebase under pressure.
  3. Describe a time when a project scope changed drastically and how you adapted your technical plan.

Tell me about a time you took pride in a complex project and how you collaborated with your team to deliver it

Medium · Behavioral & Culture Fit

Tell me about a time you took pride in a complex project and how you collaborated with your team to deliver it.

Approach

  1. Choose a project whose difficulty you can state in one checkable line: a migration, a traffic spike, a service rewrite, a dependency across several teams. Say what made it hard in terms of components, load or deadlines.
  2. Spend most of the answer on your own decisions: the options you weighed, why you picked one, what you personally built or unblocked. Replace blanket 'we' with 'I owned the schema change, and my teammate owned the client'.
  3. Show collaboration through mechanics, not adjectives: a design review you ran, how work was split across engineers, how you agreed interfaces with product, data science or mobile colleagues, how you handled review feedback.
  4. Close with a result from your own work that you can back with a number or visible change (latency, error rate, release cadence), then one thing you would do differently. Use real figures only; an invented number collapses under a follow-up.

Follow-up

  • What was the hardest trade-off in that project, and who disagreed with you?
  • What would you change if you ran it again?
  • How did you confirm the project actually worked after it shipped?

How do you handle disagreements on technical direction or architecture with your peers?

Medium · Behavioral & Culture Fit

Approach

  1. Open with a one-sentence principle: agree on the decision criteria before arguing options, such as latency, operability, cost, reversibility and the team's familiarity with the tool.
  2. Give one specific example. State both positions fairly, including the strongest argument for the other side, so the story shows you understood the disagreement.
  3. Show how evidence settled it: a prototype, a benchmark or load test, a short written design note, or a reversible pilot behind a flag. Name the artifact you produced.
  4. Report the outcome honestly, including a case where you were overruled and committed to the decision anyway, and describe the working relationship afterwards.
  5. Avoid two failure modes: a story in which the other person was simply wrong, and an answer that says you avoid conflict.

Follow-up

  • What if the other engineer is more senior and does not change their mind?
  • What do you do when there is no data that could settle it?
  • Tell me about a time you turned out to be the one who was wrong.

Why do you want to build your career at Bumble, and how do your values align with our mission?

Medium · Behavioral & Culture Fit

Approach

  1. Build the answer from three parts: what interests you in the problems candidates describe (high concurrency, real-time feeds, matching and recommendation, BFF layers, ML integration), what you have done that is close to them, and what you want to learn there.
  2. Use the product the way a user does and mention something specific you noticed. Claim only what you can verify; do not assert facts about the company that you cannot source.
  3. For values, read the company's own published mission and values, choose one or two, and tie each to a past behavior with a short 'I did this when...' example instead of repeating its wording.
  4. Keep it forward-looking: what you would want to work on in the first months and how it fits your career direction. Cut generic praise such as great culture or big scale, which would fit any company.
  5. Do not criticise your current employer to explain the move; frame it as what the new work offers.

Follow-up

  • What would you want to work on first if you joined?
  • What concerns do you have about joining, and how would you test them?
  • What sets this role apart from the other opportunities you are considering?

How do you balance speed of delivery with long-term code quality and system maintainability?

Medium · Behavioral & Culture Fit

Approach

  1. Show a rule, not a mood: classify work by reversibility and blast radius. A data model, public API contract or authentication change gets design care; a reversible feature behind a flag can ship thin.
  2. Name the quality floor you do not trade for speed: tests on the core path and boundary cases, code review, monitoring and alerting, and a rollback path. Speed comes from cutting scope, not from cutting these.
  3. Make debt explicit when you take it on: a ticket with an owner and a revisit trigger (for example, when traffic doubles), not a vague intention to fix it later.
  4. Give one concrete story with its cost: you shipped with a known shortcut, what it cost later (an incident, slower changes), and how you quantified it to negotiate time with a product manager.
  5. Avoid absolutes. 'I never compromise on quality' and 'I always ship first' both sound untrue, and neither shows judgment.

Follow-up

  • How do you persuade a product manager to schedule time to pay down debt?
  • Tell me about a shortcut you regret.
  • Which tests or checks would you refuse to skip under a deadline, and why?