A Software Engineer at Patreon is responsible for building and scaling the financial and creative ecosystem that powers the creator economy. Engineers at Patreon design and maintain systems that handle millions of users and process billions of dollars in payments. The engineering team works on core product features, payment infrastructure, creator tools, and community engagement platforms, ensuring a seamless and reliable experience for both creators and patrons.
The impact of this role is direct and highly visible, as the code you write directly influences how creators fund their livelihoods. Whether you are optimizing high-throughput APIs, designing secure payment pipelines, or building intuitive frontend interfaces, your work enables creative freedom globally. Engineers must balance rapid feature delivery with the high-security and high-availability demands of a global fintech platform.
Joining the engineering team means tackling complex, ambiguous problems alongside collaborative peers. Patreon operates at a scale where small optimizations in database queries, caching strategies, or payment routing can yield massive benefits for the creator community. The role requires a strong sense of ownership, technical curiosity, and a deep alignment with the company's mission to support creative professionals.
Recruiter Screen
reportedBefore anything technical happens, someone has to decide which rung of the ladder your loop is calibrated to, and that decision sets the bar for every round after it. It comes from how you describe scope, not from your title, because titles do not convert cleanly between companies. The weak version of the answer is team size and years. The strong version names the largest change you shipped where nobody reviewed the design, what would have broken if you had been wrong, and what you were paged for. Get the level said out loud on this call, because the range and the loop both follow from it.
What to demonstrate
- Whether the scope in your own account maps onto a level the team actually has an opening at, so a mismatch ends the process cheaply rather than after four interviewers have spent a day
- Whether your title needs re-mapping: the same word describes very different amounts of independent decision-making at a twenty-person company and a ten-thousand-person one
- Whether your compensation expectation can be filled at that level in the structure the role pays in, which is why the number gets asked for before any engineer is scheduled
How to prepare
- Write down two changes from the last two years: the largest one you designed with nobody reviewing the design, and the largest one where someone more senior did. Lead with the first when scope comes up, and be ready to say which parts of the second were yours
- Ask which level the loop is calibrated to and what changes at the level above it, then plan your weeks from that answer rather than from the posting
- Settle a total-compensation range beforehand with the split named, base against bonus against equity and its vesting period, so a question about numbers gets a number instead of the word market
Technical Screen
reportedInput bounds are the part of the prompt most often skimmed, and they usually contain the answer. They tell you which complexity class is admissible, which narrows the search before you have thought about the problem itself. As a rough planning figure, a compiled language does on the order of 10^8 simple operations per second and an interpreted one roughly an order of magnitude less. So n up to about twenty admits enumerating subsets, a few thousand admits a quadratic pass, and a million admits neither: you need near-linear, or linear with a log factor. If the bounds are missing, ask for them.
What to demonstrate
- Whether the approach is justified by the stated input size rather than by whichever pattern you recognised first
- Whether you ask about the properties that change the algorithm: whether the input arrives sorted, whether duplicates occur, whether values are bounded integers, whether it all fits in memory
- Whether you can name the bottleneck in your own solution and what would remove it, even when you deliberately leave it in place
- Whether a claimed speedup is real, since memoising a recursion only helps when subproblems genuinely overlap and the state can be keyed cheaply
How to prepare
- For each algorithm you rely on, write down the largest n it handles in roughly a second, then check two of those figures by timing them in the language you will actually type in
- For two weeks, write one line naming your target complexity and the bound that justifies it before you write any code, then compare that line with what you ended up submitting
- Practise the conversion backwards: given a required O(n log n), list the mechanisms that get you there (sorting, a heap, an ordered map, divide and conquer) and choose by what the problem needs to query, not by what you used last
Virtual Onsite Loop
reportedCoding rounds mostly set a floor. They decide whether you clear the bar, not where you land on the ladder. Level tends to come out of the design discussion and the ownership stories, so the question worth auditing beforehand is whether the scope you describe matches the scope of the job. Work that stops at your own service, or a story whose hard part was writing the code rather than getting several people to agree on an interface, reads a level below where you think you are interviewing, and that gap is usually resolved downwards.
What to demonstrate
- Whether the largest thing you describe owning ran end to end — the decision, the migration path, the rollout, and what you did when it went wrong — or stopped at the change you merged
- Whether design answers include what you would not build, what you would defer, and what you would measure before committing, rather than only what the boxes are
- Whether a disagreement in a story was settled with something checkable — a benchmark, a prototype, a written proposal — instead of by seniority or by waiting it out
- Whether you can say which calls you made alone and which you escalated, and why the line sat where it did
How to prepare
- Write your largest piece of owned work as a timeline of decisions — who decided what, when, and what you did when the plan broke — then delete every sentence whose subject is "we" and see how much survives
- Take one system you know well and drill the migration answer: how old and new paths run side by side under live traffic, how you compare their outputs, what the rollback is once writes are going to both, and which step you would not automate
- Map each line of the ladder in the job posting to a specific thing you have done, find the line you cannot support, and prepare the closest evidence you have plus an honest account of the gap
1 candidate reports. Individual accounts describe a particular role and hiring cycle.
Patreon Software Engineer Interview Experience — A Wordle-Style Coding Screen Nobody Warns You About
Not a question from the forum, which surprised me — I'd seen people on the forum say the phone screen here is basically coding rounds, IC/manager rounds, rate limiter, shopping cart, that sort of thing. The problem was roughly a guessing-word game: given a guess word and a target word, mark the status of each position. green: the two chars at this index are the same yellow: the two chars at this…
Read full experiencePracHub editorial advice for the preparation topics above.
Writing the state change to the database and publishing the event in the same code path
No transaction spans a relational database and a message broker, so a crash between the two leaves one done and the other not, and the failure is asymmetric in both orderings: publish-then-commit invents events for state that never existed, while commit-then-publish silently loses events for state that does. Retrying the publish after the commit is not a fix, because the process can die before the retry runs. The working shape is an outbox row written inside the same transaction plus a relay that publishes it at least once, which makes consumer-side idempotency mandatory rather than optional. Note also that 'exactly-once' in a stream processor means exactly-once processing within that system's own read-process-write transaction, and says nothing at all about an external side effect such as charging a card.
Assuming the default isolation level enforces the invariant you wrote down
PostgreSQL defaults to READ COMMITTED, where every statement takes a fresh snapshot, so a read-modify-write on a balance loses updates under concurrency. Its REPEATABLE READ is snapshot isolation, which blocks that particular anomaly by aborting the loser with SQLSTATE 40001 but still permits write skew across two different rows; only SERIALIZABLE closes that, and both levels therefore require a bounded retry loop on 40001 that many implementations simply never write. MySQL's InnoDB REPEATABLE READ behaves differently again — it does not abort on a conflicting write, so the identical application code silently changes behaviour when the engine changes. Two-sided transfers add a second failure mode on top: without a deterministic lock ordering, such as always locking account ids in ascending order, concurrent opposing transfers deadlock (SQLSTATE 40P01).
Naming no test cases at all
State what you would test before being asked: empty input, a single element, all elements equal, the maximum permitted size, and the input that exercises the branch you just wrote. It costs thirty seconds and is much of what separates someone who has shipped code from someone who has only solved puzzles.
Arguing past a hint
When the interviewer asks what happens for a particular input or floats a different data structure, stop and take it seriously; it is almost always a correction rather than idle curiosity. Talking over it converts a recoverable wrong turn into a data point about how you handle review.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Implement an in-memory LRU (Least Recently Used) cache that supports a…
Implement an in-memory LRU (Least Recently Used) cache that supports a TTL (Time-to-Live) expiration policy for its keys.
Approach
- Name the brute-force solution and its complexity before improving on it.
- Walk one small example through your approach before writing the whole thing.
- Choose the data structure from the access pattern, not from familiarity.
Follow-up
- How does this change if the input no longer fits in memory?
- What is the worst case, and how likely is it on real data?
Design a storage and retrieval system that guarantees O(1) time comple…
Design a storage and retrieval system that guarantees O(1) time complexity for insertions, lookups, and deletions under strict memory constraints.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- State the target complexity and say which constraint rules the naive version out.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- What is the worst case, and how likely is it on real data?
- Which test case would catch an off-by-one here?
Solve a dynamic programming challenge to find the optimal way to distr…
Solve a dynamic programming challenge to find the optimal way to distribute creator payouts across different payment processors to minimize transaction fees.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- Choose the data structure from the access pattern, not from familiarity.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- How does this change if the input no longer fits in memory?
- What is the worst case, and how likely is it on real data?
Write a function to parse, traverse, and flatten highly nested diction…
Write a function to parse, traverse, and flatten highly nested dictionaries into a single-level key-value structure.
Approach
- Restate the input: its shape, its size, and what is guaranteed about it.
- Choose the data structure from the access pattern, not from familiarity.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- Which test case would catch an off-by-one here?
- What is the worst case, and how likely is it on real data?
Build a series of helper functions using native JavaScript promises, c…
Build a series of helper functions using native JavaScript promises, callbacks, and timeouts to orchestrate asynchronous API requests.
Approach
- Name what is shared across threads and what owns each piece of state.
- Say what the runtime actually does before reasoning about the code.
- Distinguish a value from a reference to it, and say which one you handed out.
Follow-up
- How would you prove the race exists rather than suspect it?
- Where could this allocate more than you expect?
Implement a custom, robust version of the Lodash deepClone method to h…
Implement a custom, robust version of the Lodash deepClone method to handle nested objects, arrays, and edge cases without using external libraries.
Approach
- Reach for the cheapest primitive that closes the race, not the broadest lock.
- Distinguish a value from a reference to it, and say which one you handed out.
- Identify the window where an invariant is briefly untrue.
Follow-up
- What happens if two callers reach this at the same time?
- How would you prove the race exists rather than suspect it?
Compute peak held exposure from overlapping authorisation holds
An account has up to 2 million authorisations on one business date: (auth_id, amount_minor, created_at, expires_at) with the hold live over the half-open interval [created_at, expires_at), plus capture events (auth_id, captured_minor, captured_at) that reduce the hold at captured_at, and explicit reversals that drop the remainder to zero. Timestamps are microsecond-precision timestamptz. Return the maximum total held amount across the day and the earliest instant it is reached, with complexity. Then say what changes if the deliverable is the peak per minute instead.
Approach
- Expand each authorisation into signed delta events rather than reasoning about intervals:
+amountatcreated_at,-remainingatexpires_at,-captured_minorat eachcaptured_at,-remainingatreversed_at. The problem collapses to a running sum over a sorted event list. - Sort the 2n to 4n events by
(timestamp, sign)with negative deltas ordered first on a tie. The half-open convention forces that: at exactlyexpires_atthe hold is already gone, so a-must land before a+at the same instant or you report a one-microsecond peak that never existed. O(n log n) time, O(n) space. - Sweep once, tracking
running,bestandbest_at, taking the first instant that attains the maximum. Say out loud which tie rule you are using — 'the peak' is ambiguous when the same level is reached twice, and the caller needs to know which instant they are being handed. - If timestamps are bucketed (1,440 minute buckets for the per-minute variant), drop the sort for a difference array: add the delta at the start bucket, subtract at the end bucket, prefix-sum once. O(n + B) time and O(B) space, strictly better, at the cost of answering only at bucket resolution.
- Assert the invariant during the sweep:
runningmust never go negative. A negative total means a capture exceeded its authorisation, which is an invariant violation upstream rather than a sweep bug — fail loudly instead of clamping at zero and reporting a plausible number. - Handle carry-in: a hold created before the window contributes its remaining amount as the sweep's initial value, not as a
+event inside the window. Omitting that is the off-by-a-day that makes the first minute of every day look artificially quiet.
Worked solution 25 min
- Write the event expansion and the comparator first; the comparator is the part that is wrong in most first attempts.
- Fixture A: two holds of 10,000 minor units where the first's
expires_atequals the second'screated_at. - Fixture B: one hold of 10,000 with a partial capture of 4,000 at t+1 and expiry at t+2, so the held series is 10,000 then 6,000 then 0.
- Fixture C: a hold opened the previous day and still live at the window start; seed
runningwith its remaining amount. - Shuffle the events in all three fixtures before sorting and re-run, proving the answer depends only on the comparator.
Follow-up
- An incremental authorisation raises an existing hold after the fact. Where does that event go, and does it disturb the tie rule?
- You now need the peak for 10 million accounts inside a nightly window. What changes, and what must the partitioning key be?
- The peak sizes a funding transfer. Does the business date or the timestamp decide which day that transfer lands on?
Decide whether balance is derived or materialised, then hold the floor
Authorisation needs an account's available balance inside an 80 ms budget at 3,000 requests per second; that account already has 200 million ledger_entry rows. A product rule says the balance may never fall below the account's negative overdraft limit. Decide whether the balance is summed from entries or held in a materialised account_balance(account_id, balance_minor, floor_minor, currency, version) row, and justify the choice from the read pattern. Then give the write path that holds the floor, naming the isolation level, the anomaly a weaker level permits, and the SQLSTATE you retry.
Approach
- Size the derived read before arguing about it: summing 200 million rows is not an 80 ms operation under any index, since even a covering index on (account_id, entry_id) still reads work proportional to the rows. The authorisation read pattern forces one materialised row fetched by primary key; the statement read pattern, which is low-rate and historical, stays derived from entries. That is the whole justification for the denormalisation, and its price is a write on that row per posting.
- Put the floor where it can be a single-row constraint: CHECK (balance_minor >= floor_minor) on account_balance, updated in the same transaction as the entries. As a predicate over a set of entry rows it cannot be a CHECK at all, which is the reason the materialised row earns its keep twice.
- Write the mutation as one statement: UPDATE account_balance SET balance_minor = balance_minor - $1, version = version + 1 WHERE account_id = $2 AND balance_minor - $1 >= floor_minor. Zero rows updated means refused. This is safe even under READ COMMITTED, because the UPDATE re-evaluates its predicate against the locked, post-update version of the row.
- Name the anomaly in the shape that is not safe: SELECT the balance, compute a new value in application code, then UPDATE to that constant. Every statement under READ COMMITTED takes a fresh snapshot, so two concurrent 60-unit withdrawals against 100 both read 100 and both write 40. The row then claims 40 while 120 has actually left, so the true position is -20, below a floor of 0, and the row it is checked against cannot show it. PostgreSQL REPEATABLE READ is snapshot isolation and aborts the loser with SQLSTATE 40001; SERIALIZABLE additionally closes write skew across two rows. Both require a bounded retry with backoff, and InnoDB REPEATABLE READ does not abort at all, so identical code changes behaviour on a different engine.
- For a two-account transfer, acquire the rows in a deterministic order such as ascending account_id; without it, opposing concurrent transfers deadlock and the database kills one with SQLSTATE 40P01. That is a retry, not a correctness failure, but it is a retry somebody has to write.
- Finish on the ceiling: the hot row admits one committed write per lock hold, so at a 2 ms hold it caps near 500 per second regardless of cores. Sharding into N sub-rows multiplies throughput and immediately makes the floor check cross-row again, which then needs the shard sum under SERIALIZABLE or a per-shard reserved allowance.
Worked solution 35 min
- Seed one account with balance_minor = 100 and floor_minor = 0, then run two concurrent 60-unit withdrawals under READ COMMITTED using SELECT-then-UPDATE and record the final balance.
- Replace it with the single-statement conditional UPDATE and re-run the same race, asserting on rows affected rather than on an exception.
- Re-run under SERIALIZABLE with the read-modify-write shape, count the 40001 aborts, and add a retry loop with a fixed attempt cap and jittered backoff.
- Measure committed writes per second against that single row, then write the drift query: sum signed entries per account and compare against balance_minor.
Follow-up
- Shard the balance into eight sub-rows. Write exactly what the floor check now does, and what it costs per authorisation.
- Someone proposes an AFTER INSERT trigger on ledger_entry to maintain the balance. What does that change about ordering, about batch posting, and about failure handling?
- How do you detect that the materialised row has drifted from the entries, how often do you run it, and on which replica?
Rebuild a statement with a running balance from entries alone
From ledger_entry(entry_id bigint and monotonic, account_id, direction, amount_minor, currency, business_date, posted_at) produce one month's statement for a single account: every entry in order, its signed amount, and the running balance after it, starting from a supplied opening balance. The account is a customer deposit, so a credit increases it. Also return the first business_date in the month on which the running balance went negative, or null if it never did. Write the query using window functions, state which frame you depend on, and say what makes the ordering deterministic.
Approach
- Sign the amount first: CASE WHEN direction = 'credit' THEN amount_minor ELSE -amount_minor END, because the sign lives in direction and never in the column. State the convention out loud, since a customer deposit is a liability of the institution and a credit increases it, while for an asset account the same expression inverts.
- Compute the running balance as SUM(signed) OVER (PARTITION BY account_id ORDER BY business_date, entry_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), then add the opening balance as a scalar. The opening figure is a constant for the statement, not a second window.
- Be explicit about the frame, because it is the whole exercise: with an ORDER BY and no frame clause the default is RANGE UNBOUNDED PRECEDING AND CURRENT ROW, which includes every peer row tied on the ordering key. Ordering on business_date alone therefore reports the same end-of-day figure on every line of that day, and the statement still looks plausible.
- Make the order deterministic with a column that has no ties: business_date has ties by construction, entry_id is monotonic and unique, and posted_at is neither guaranteed unique nor correct as an ordering key when a backdated entry posts late.
- Get the first breach from the same computation rather than a second pass: wrap the running balance in a subquery and take MIN(business_date) FILTER (WHERE running_balance_minor < 0). LAG is the wrong tool here, because the question is about a level rather than a change.
- Support it with an index on (account_id, business_date, entry_id) so the partition and the order both come from the index; then confirm in EXPLAIN that there is no Sort node above the index scan.
Follow-up
- An entry for 3 March posts on 7 March, after the statement for that week was sent. Where does it appear, and what does the running balance do?
- The same statement has to be reproducible a year from now. What stops it changing, and what would silently change it?
- At what account size does deriving this per request stop being viable, and what would you materialise first, a daily closing balance or a monthly one?
Design a robust, distributed ledger system that handles multi-currency…
Design a robust, distributed ledger system that handles multi-currency processing, refund flows, and creator balance updates with transactional consistency.
Approach
- Name the read and write paths separately; they rarely have the same bottleneck.
- Choose a partition key and say what query it makes expensive.
- Name the failure you are designing for, then the recovery path.
Follow-up
- What breaks first when traffic grows ten times?
- How does this behave when that dependency is down for an hour?
Architect a high-throughput notification service that alerts patrons i…
Architect a high-throughput notification service that alerts patrons immediately when a creator publishes new content, ensuring delivery across email, push, and SMS.
Approach
- Name the failure you are designing for, then the recovery path.
- Fix the scope first: who calls this, how often, and what they do when it fails.
- State the consistency you need, and where you are willing to be stale.
Follow-up
- What would you drop to keep the system up under load?
- What breaks first when traffic grows ten times?
Serve balances and statements without re-summing the ledger
Balances are derived from an append-only ledger_entry table: entry_id, transaction_id, account_id, direction (debit|credit), amount_minor, currency, source_type, source_id, business_date, posted_at. Reads run at 50,000/s from customer apps and merchant dashboards; postings run at 10,000 entries/s. Large accounts hold tens of millions of entries, so summing per read is not viable. Design the read path: where the current balance lives, when it is written relative to the entries, what a caller sees immediately after its own posting, and how a statement for a closed business date stays byte-identical on every future read. State your invalidation rule.
Approach
- Split the request into two reads with different requirements rather than one 'balance API'. Current balance is a single row that must be read-your-writes for the party who just posted. A statement is a bounded range over entries for one business_date window, and once that date's cutoff has passed the range is immutable, so it is cacheable indefinitely rather than for a guessed TTL.
- Write the materialised balance inside the same database transaction as the entries: UPDATE account_balance SET amount_minor = amount_minor + $delta, version = version + 1 WHERE account_id = $1, alongside the INSERTs. An asynchronous updater fed from the entry stream is the tempting alternative and is wrong here, because it makes the balance disagree with the system of record for exactly as long as the consumer lags, which is exactly when someone is refreshing the screen.
- Serve the balance as a primary-key lookup, O(1) and sub-millisecond, and serve the statement as an index range scan on (account_id, business_date, entry_id), O(k) in rows returned rather than O(n) in account history.
- Invalidate by writing through on commit, keyed by account_id and carrying the version, not by TTL. A TTL on a balance is a defined interval during which you knowingly display a stale number; a version lets a reader detect staleness instead of hoping.
- Price the design honestly: the extra balance UPDATE serialises postings on that account row, and that lock hold is what sets the per-account write ceiling. Measure it before deciding the read path is free.
Worked solution 20 min
- Create ledger_entry with an index on (account_id, business_date, entry_id) and load 5,000,000 entries for one account spread across 400 business dates.
- Time three reads: a full-history SUM signed by direction, the same SUM restricted to one business_date, and a single-row lookup on the materialised balance.
- Post a transaction that writes two entries plus the balance update in one database transaction, and read the balance from a second session both before and after that commit.
- Run the statement query for a business date older than the cutoff twice and diff the two outputs line by line.
Follow-up
- A customer disputes a balance shown three months ago. Reproduce that exact number from the data you kept.
- The materialised balance and the sum of entries differ by one minor unit. How do you find that before the customer does, and what do you do about it?
- What changes if the balance must also be readable from a second region with a 70 ms round trip?
Reconciliation breaks that close overnight and then reopen
Reconciliation opens 400 to 900 ledger_only breaks each morning, most resolve on the next run, and a subset reopens two days later. The three-way match joins on (external_reference, amount_minor, currency, business_date). ledger_entry.business_date is populated as posted_at::date, where posted_at is timestamptz. The processor closes its day at 22:00 US/Eastern. Breaks cluster on movements posted between 00:00 and 03:00 UTC. Give the ordered checklist, the cause, the fix, and what happens to the breaks already in the table.
Approach
- Test the hypothesis with data you already have rather than by reasoning about it. Bucket open breaks by hour of day on posted_at. A cutoff mismatch concentrates breaks in a fixed window every day and produces almost none outside it; a flaky job produces breaks with no time-of-day structure. One query eliminates half the candidate explanations.
- Compute the offset explicitly and note that it moves. A 22:00 US/Eastern cutoff is 03:00 UTC next day under EST and 02:00 UTC under EDT, so the processor's business date D spans UTC 03:00 on D to 03:00 on D+1, while posted_at::date assigns D to UTC 00:00 through 24:00. The disagreement window is exactly 00:00 to 03:00 UTC, narrowing to two hours during EDT, which matches the observed cluster.
- Find the second bug in the same expression. Casting a timestamptz to date in PostgreSQL applies the session TimeZone, so the identical query returns different dates for different sessions and for the same session after a SET. A business date that depends on who is asking is not a business date.
- Name the missing column rather than a better expression. business_date is a business fact determined by a cutoff rule and a business-day calendar; it is not derivable from any UTC timestamp. It must be stored and set at write time from the rule, with posted_at kept separately for ordering.
- Account for the self-resolving and reopening pattern before claiming the cause is complete. Check whether the day-spanning fuzzy fallback is resolving breaks by matching against an adjacent date, and whether two movements sharing amount, currency and merchant on adjacent dates can be crossed by it. A crossed pairing resolves today and surfaces as duplicate_match later, which is exactly the resolve-then-reopen shape and is a second reason not to widen tolerance.
- Plan the correction against the append-only rule. The trigger forbids UPDATE on a posted entry, so a business_date correction is either a deliberate, logged exception for a non-financial classification column with the trigger amended, or a separate correction table joined at read time. Pick one and get it signed off, because it decides whether prior statements change. Then re-run the matcher over corrected dates rather than bulk-resolving the open breaks; whatever survives is the genuine break population the date bug was masking.
Follow-up
- The fuzzy fallback resolved some breaks by matching an adjacent date. How do you prove it never crossed two lines?
- What does your fix do on the DST transition days themselves, when the local day is 23 or 25 hours long?
- Where does the business-day calendar live, who updates it for a newly announced holiday, and what breaks if they forget?
Day one measures instead of guessing, under a fixed rubric, and the remaining hours are allocated in proportion to the gaps before any studying begins. The allocation is deliberately not renegotiated midweek, because the area that feels worst on day three is usually the one that is moving.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Diagnostic, scored before you study anything
- Sit a 110-minute diagnostic in four blocks: forty-five minutes on two coding problems, twenty-five on one design prompt taken to interface and data model, twenty of short-answer fundamentals, and twenty delivering two behavioural answers aloud.
- Score each block from 0 to 3 on a fixed rubric where 3 is correct and fluent, 2 is correct but slow or prompted, 1 is partially correct and 0 is stuck, grading the artifact rather than how the attempt felt.
- Allocate days two to five in proportion to 3 minus each block's score, write the allocation down, and commit to leaving it alone.
Deliverable: A scored rubric and a fixed hour allocation for the rest of the week.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Worked solution ↗02Largest gap: find the boundary rather than the subject
- Split the weakest area into named sub-skills and rate each separately. For coding those are restating the problem, choosing the structure, stating the invariant, turning the invariant into loop bounds, handling empty and single-element input, and accounting for complexity out loud.
- Attempt three items positioned just above where the rating drops off, and for each write the first move you failed to make.
- Re-attempt one of them from blank four hours later with nothing open.
Deliverable: A sub-skill map with the two blocking sub-skills circled.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Drill the blocking sub-skill by repeating the shape
- Do eight short repetitions of the same shape rather than eight different problems, so what gets practised is the pattern and not the puzzle.
- State the rule you now hold in one sentence, then test it against a case built to break it, a sliding window over an array containing negative values, or a cache-aside read path whose invalidation message is dropped.
- Have someone else read your one-sentence rule and find the precondition you left out.
Deliverable: One rule statement with its preconditions attached and one counterexample that would have caught the incomplete version.
Practice prompt ↗Practice prompt ↗04Second gap, plus maintenance on the strongest area
- Run the same sub-skill decomposition on the second-largest gap in half the time.
- Spend twenty-five timed minutes on the block you scored highest, choosing the hardest item you can still finish rather than a warm-up.
- Write whether each area fails you on recall, on setup, or on execution, and set the fix accordingly: repetition for recall, a written checklist for setup, timed work for execution.
Deliverable: A second sub-skill map plus a one-line failure diagnosis for each area.
Practice prompt ↗Practice prompt ↗Worked solution ↗05The gap that is not a skill
- Record one technical and one behavioural answer, then count two things in the playback: seconds before your first clarifying question, and sentences you began without knowing where they would end.
- Practise saying that you do not know, followed by how you would find out, without letting it soften into a guess, and practise stating a complexity or an estimate before being asked for it.
- Redeliver one answer under a hard ninety-second cap, which forces structure ahead of detail.
Deliverable: Two recordings with a counted reduction in time-to-first-question.
Practice prompt ↗Practice prompt ↗06Retest under day-one conditions
- Sit the same 110-minute structure with new prompts of comparable difficulty and score it on the identical rubric.
- For any block that did not move, change the method rather than adding hours: a block stuck at 1 usually means the practice was too varied, not too short.
- Write down which single block you would still lose the offer on.
Deliverable: A second scored rubric placed beside the first, with one named remaining risk.
Practice prompt ↗Practice prompt ↗07Full loop under interview conditions
- Run a sixty-minute mock over the two blocks that moved least, with an interviewer briefed to interrupt and change direction mid-answer.
- Write the recovery script for going blank: restate the question, state your assumption, name the first thing you would check.
- Say every rule from the week aloud without reading it, and cut any you cannot state in a single sentence, since a rule you have to reconstruct mid-answer will not survive an interruption.
Deliverable: A one-page card holding the recovery script and only the rules you could state from memory.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Bring the two or three numbers the story rests on and know how they were collected. A p99 whose timer starts inside your handler excludes the time a request spent queued, so it can sit flat while users wait longer. Give the window, the percentile and what the measurement left out, or drop the number.
Describe a situation where you had to collaborate with a cross-functio…
Describe a situation where you had to collaborate with a cross-functional team (such as Product or Design) to resolve a highly ambiguous product requirement.
Approach
- Pick a story where you made the decision, not one where you watched it.
- Close with what you would do differently, concretely.
- State the situation in two sentences and spend the rest on the reasoning.
Follow-up
- What would you do differently if you ran that again?
- What did you decide not to do, and why?
Unblock an engineer on double-posted interest accrual
An engineer two years into their career has a nightly accrual job that double-posts interest for some accounts whenever the batch is partially re-run after a failure. They have spent two days on it and are now rewriting the batch runner. You have 30 minutes. Describe how you have unblocked someone without taking the keyboard: the question you asked first, how you chose between handing over the answer and handing over the method, what you left behind so the next person does not get stuck here, and how you knew they were unblocked rather than deferring to you.
Approach
- The probe is whether you grow people or absorb their work. Open with the diagnostic question rather than the solution: ask what identifies one unit of work, because the answer reveals immediately that accrual is keyed by (account_id, accrual_date) and that the job has no uniqueness on it.
- Redirect from the runner to the write. The rewrite is aimed at never re-running, which is unachievable; the property needed is that re-running posts nothing new, enforced by a unique index on (account_id, accrual_date) or by passing the same idempotency key to the ledger posting operation so the second attempt is a no-op rather than a second transaction.
- Choose deliberately between answer and method and say why. Two days in and blocked on the wrong layer is usually the moment to hand over the framing (restartable at account granularity, idempotent per unit) and let them write the code, because the lesson is the framing and the code is the easy part.
- Leave an artefact, not a conversation: a test that re-runs one account twice and asserts one posting, plus two lines in the runbook stating that per-account work must be idempotent because the batch is always partially re-run.
- Check that they are unblocked by asking them to predict the failure that the fix does not cover, such as a mid-run rate change producing two different correct amounts for the same key. If they can find the next edge themselves, they own it; if they ask you to confirm each step, they are deferring and you have hidden the block rather than removed it.
- Say what you deliberately did not do. Not fixing it yourself before the standup is the whole exercise, and a strong answer names the pressure it resisted.
Follow-up
- The unique index rejects the re-run, but the first run posted the wrong amount. How should the job behave now?
- How do you tell whether you taught them or just unblocked them, a month later?
- The same engineer is blocked again next week on a similar problem. What does that tell you about your first intervention?
Argue against a dual write you were assigned to build
A senior engineer specifies that the orchestration service should update payment_intent.status and publish the merchant event in the same code path, wrapping the publish in a retry. You believe it is wrong and you have been told to build it. Describe a time you argued against a design you were assigned. State the failure you predicted, the evidence you brought, how long the disagreement ran, what you did once the decision went against you, and what production eventually showed. Include the version of your argument that did not persuade anyone, and why it did not.
Approach
- The probe is whether you can disagree with a technical authority using evidence rather than taste, and still execute. Establish the failure precisely first: no transaction spans the database and the broker, so a crash between them leaves one side done, and it fails asymmetrically in both orderings. Publish-then-commit invents events for state that never existed; commit-then-publish loses events for state that does.
- Kill the retry argument explicitly, because it is the one that keeps the design alive: the retry loop lives in the same process that can die, so it narrows the window and never closes it. Quantify the window if you can (deploy restarts per week times request rate times the in-flight fraction) rather than asserting it is rare.
- Bring evidence in the form the decision-maker can check in a day: a count of merchant events with no corresponding intent version, or intent versions with no event, over a window you can query now. An argument that costs the other person nothing to verify is the one that moves.
- Say what you proposed instead in one sentence with its cost owned honestly: an outbox row written in the same transaction with UNIQUE (aggregate_type, aggregate_id, aggregate_version), a partial index on published_at IS NULL, and a relay that delivers at least once. The cost is that every consumer now has to be idempotent, and that is a real tax you are asking others to pay.
- Describe the disagree-and-commit mechanics concretely: what you built, what you instrumented so the prediction could be checked, and what threshold would have proved you wrong. A strong answer is falsifiable; a generic one says 'I raised concerns and moved on'.
- Report the outcome including the possibility that you were partly wrong about severity or timing, and separate 'I was right' from 'the disagreement was handled well'.
Follow-up
- You lost the argument. What instrumentation do you add so the question gets settled by data in a month rather than by another meeting?
- The stream processor's documentation says exactly-once. Why does that not settle the question for a card charge?
- What would have made you drop the objection entirely?
- 01
Describe a situation where you had to collaborate with a cross-functional team (such as Product or Design) to resolve a highly ambiguous product requirement.
- 02
An engineer two years into their career has a nightly accrual job that double-posts interest for some accounts whenever the batch is partially re-run after a failure. They have spent two days on it and are now rewriting the batch runner. You have 30 minutes. Describe how you have unblocked someone without taking the keyboard: the question you asked first, how you chose between handing over the answer and handing over the method, what you left behind so the next person does not get stuck here, and how you knew they were unblocked rather than deferring to you.
- 03
A senior engineer specifies that the orchestration service should update payment_intent.status and publish the merchant event in the same code path, wrapping the publish in a retry. You believe it is wrong and you have been told to build it. Describe a time you argued against a design you were assigned. State the failure you predicted, the evidence you brought, how long the disagreement ran, what you did once the decision went against you, and what production eventually showed. Include the version of your argument that did not persuade anyone, and why it did not.
Is this an official Patreon interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Patreon. Rounds and questions reflect what candidates have reported, not a process Patreon has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗What programming languages can I use during the technical interviews?
You can generally use any modern programming language of your choice, such as Python, JavaScript, Go, Java, or C++. However, if you are interviewing for a specialized role, such as a Frontend or React Specialist, you will be expected to demonstrate deep proficiency in JavaScript and web fundamentals.
PracHub interview research ↗How heavily does Patreon weigh behavioral and culture-fit interviews?
Extremely heavily. Patreon values team cohesion, collaborative empathy, and mission alignment. Candidates who perform flawlessly on technical coding but show arrogance, lack of empathy, or disinterest in the company's mission are routinely rejected.
PracHub interview research ↗What is the typical timeline from the initial screen to an offer?
While the actual interviewing stages can be completed in two to three weeks, scheduling delays, team-matching phases, or head-count adjustments can extend the process to several weeks. It is recommended to maintain active communication with your recruiter to stay updated on your status.
PracHub interview research ↗Does Patreon provide detailed feedback after an interview rejection?
In alignment with standard industry practices, Patreon generally does not provide specific, detailed feedback to candidates post-rejection. Rejections are typically communicated via a standard notification email.
PracHub interview research ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24