Monzo Backend Coding Interview: Choosing Between Take-Home and Pair Programming
Quick Overview
Choose between Monzo’s backend take-home and pair-programming options using verified official guidance, clearly dated candidate accounts, and a practical format comparison. Rehearse one original TransferBook interface in both settings, test replay and failure behavior, and prepare a README and code-review explanation that make your design decisions clear.
Choosing Monzo’s take-home or pair-programming option is a decision about how you demonstrate engineering judgment. Choose take-home if you can protect a bounded work session and explain the result afterward. Choose pairing if a live conversation helps you resolve ambiguity and show incremental progress. Both formats call for readable code, tests, and a defensible design.
This guide focuses on that choice and the backend coding round. For broader practice, use the PracHub Backend Engineer question collection. The exercise below is original preparation material, not a leaked Monzo assignment.

What Monzo officially says—and what remains uncertain
Official fact, checked September 8, 2026: Monzo’s current UK Staff Backend Engineer listing includes a take-home task or pair-coding exercise. That establishes a current example of the choice, not a guarantee for every level or location. Confirm your own invitation. Monzo job listing
Official process guidance: Monzo’s March 2025 backend interview article describes a small take-home program followed by a review, or pairing on a supplied interface with automated tests. Take-home instructions request a README covering setup, structure, and tradeoffs; extra functionality earns no bonus. Pairing emphasizes incremental reasoning rather than typing speed, and finishing everything is not essential. Use a familiar supported language and development environment. The article’s overview says one hour for pairing, while its detailed section says 45 minutes. Ask which duration applies to your scheduled session. Monzo’s backend process
Candidate reports: A June 2025 discussion includes a candidate describing take-home work followed by a detailed design discussion. A June 2026 poster describes a take-home followed by a 45-minute discussion. These are individual accounts with different dates and contexts; they do not establish a universal schedule or which option has a higher pass rate. 2025 discussion, 2026 account
Two independent, detailed coding reports from the same hiring cycle were not available in this research. Accordingly, the recommendations below are preparation inferences, not claims about undisclosed scoring or exact questions.
Choose the format you can execute well
Compare your actual working conditions before choosing. “I dislike live coding” is useful information, but a take-home can also go badly if you spend every evening adding unnecessary architecture.
| Your constraint | Take-home may fit when… | Pair programming may fit when… |
|---|---|---|
| Available time | You can reserve focused time and stop at a planned boundary. | An appointment is easier than finding several uninterrupted work periods. |
| Ambiguity | You can record assumptions clearly and avoid silently changing requirements. | You think more clearly after a short clarification conversation. |
| Communication | You explain completed code well and can retrace the reasoning. | You can narrate decisions while keeping the implementation moving. |
| Main failure risk | You can resist polishing infrastructure beyond the brief. | You can recover calmly from a failing test or unfamiliar API. |
This table is a decision aid, not Monzo’s rubric. Try the same small task once under each condition. Judge whether a reviewer could understand the behavior and your reasoning, rather than counting lines written.
For take-home, rehearse a stopping rule: the agreed behavior works, boundary tests pass, and another person can run it. For pairing, rehearse a recovery rule: reproduce the failure, state one hypothesis, inspect the relevant state, then make one change. Neither rule requires flawless first attempts.
Before committing, confirm the deadline or session length, available language, test harness, submission format, and any adjustments you need. Let your available time and working conditions settle a close decision.
Rehearse one interface in both formats
Use this original practice contract for a single-threaded, in-memory TransferBook. Its banking vocabulary makes the consequences easy to discuss, but it does not model Monzo’s production ledger.
The constructor receives valid, nonnegative integer balances in minor currency units. A transfer receives a nonempty string ID, source account, destination account, and positive integer amount. Accounts must exist and differ. Invalid requests raise ValueError without changing state.
A successful transfer moves the amount and records its ID with the request details. Repeating that ID with identical details returns False without another movement. Reusing it with different details raises ValueError. New successful transfers return True. Failed requests do not reserve IDs, so a corrected retry may succeed. Reject booleans as amounts even though Python treats them as integers.
The interface is deliberately small:
class TransferBook:
def transfer(self, transfer_id, source, destination, amount):
"""Return True for a new transfer, False for an exact replay."""
def balance(self, account):
"""Return the current integer balance."""
Assume valid account-name and transfer-ID types at the boundary. There is no network, persistence, concurrent access, currency conversion, or overdraft facility. Those omissions make the rehearsal assessable; they are not production design recommendations.
Start with A holding 100 and B holding 20. Use fresh state for each independent row below, except where the row explicitly describes a sequence.
| Provided test or sequence | Expected result | What it exposes |
|---|---|---|
Transfer t1, A → B, 30 | True; A=70, B=50 | Basic movement and conservation |
| Repeat that exact request | False; A=70, B=50 | Successful replay does not move money twice |
Reuse t1 with amount 31 after success | Error; A=70, B=50 | ID conflicts cannot silently change meaning |
| Transfer A → missing C | Error; A=100, B=20 | Validation must precede mutation |
| Transfer A → B, 101 | Error; A=100, B=20 | Insufficient funds leave balances unchanged |
Fail t2 for 101, then retry t2 for 30 | Second call succeeds | Failure does not consume an ID |
Transfer zero, a negative amount, or True | Error; balances unchanged | Input rules are explicit |
A subtle case belongs in your own added tests: after a successful transfer spends most of A’s balance, replay must still return False. Checking current funds before recognizing an already successful request would reject a valid replay.
The central implementation order should therefore be visible:
request = (source, destination, amount)
if transfer_id in self.completed:
if self.completed[transfer_id] != request:
raise ValueError("conflicting transfer ID")
return False
# Validate amount, accounts, and funds before any balance mutation.
self.balances[source] -= amount
self.balances[destination] += amount
self.completed[transfer_id] = request
return True
This excerpt assumes basic input validation has already run and omits the remaining validation body. It is an ordering sketch, not a complete copy-and-run solution. Explain that ordinary validation failures are atomic under this exercise’s assumptions; this is not a crash-safe transaction or a distributed exactly-once guarantee.

Use a bounded take-home rehearsal
For a personal rehearsal, choose a 90-minute cap—not an asserted Monzo time limit. Spend roughly the first 15 minutes turning the contract into tests, the next 45 implementing and debugging, and the remaining 30 reviewing the result and writing the handoff. Adjust that practice cap if it teaches you nothing about your actual constraints.
Keep the implementation small enough to review in one sitting. Two dictionaries can represent balances and completed requests. An abstract repository layer adds little to an exercise that explicitly excludes persistence. If you introduce an abstraction, explain the concrete duplication or complexity it removes today.
Your README should function as a review map:
| README section | Useful content for this exercise |
|---|---|
| Run | Python version, exact test command, and required dependencies—or none |
| Contract | Integer minor units, rejected inputs, successful replay behavior, and failed-ID reuse |
| Design decision | Completed-request lookup occurs before the funds check so replay survives later balance changes |
| Verification | Named tests for missing destinations, conflicting IDs, and unchanged state after errors |
| Limits | Single-threaded memory only; request history grows with successful transfers |
| Next change | If concurrent calls become a requirement, protect the whole decision and update together |
Do a clean handoff check: open a fresh directory or environment and follow only your README. A missing dependency or undocumented working-directory assumption is a useful rehearsal finding.
Then close the editor and explain the code aloud. If you cannot explain why you retained request details instead of only a set of IDs, inspect the conflict test. It provides a concrete reason for the data structure.
Use a pair-programming rehearsal that exposes decisions
A 45-minute practice session is useful because it forces prioritization; it is not a promise about your appointment. Ask a partner to supply the contract and tests, then introduce one ambiguity such as whether failed requests reserve IDs.
First, restate observable behavior: “I will validate before mutating, and an exact successful replay must not move funds again.” Clarify the ambiguity before deciding how to implement it.
Next, build the smallest successful path and run a test. Add one failure path, then replay behavior. Keep your explanations attached to changes: “I’m moving the completed-ID lookup earlier because the replay should not depend on the current balance.” This tells a reviewer more than narrating every keystroke.
When a test fails, avoid editing several branches at once. Suppose missing-destination handling leaves A debited. Inspect the order of validation and mutation, move the existence check, and rerun both the failing case and the happy path. State the invariant the fix restores.
Finish with a short status report: working behaviors, known gaps, and the next test you would add. If the clock ends before conflict handling is complete, describe the missing case precisely. “Some edge cases remain” hides information; “reusing a successful ID with a different destination is not rejected yet” is reviewable.
Defend the design without expanding the assignment
Use a claim, evidence, limitation sequence in the review. For example: “A failed destination lookup cannot debit the source; the missing-account test asserts both balances afterward. This holds for validation failures in this single-threaded model, not process crashes.”
Expect your own practice reviewer to challenge the following decisions:
- Why integer amounts? They match the defined minor-unit contract and avoid introducing rounding into this exercise.
- Why retain the full request? A set of successful IDs detects repetition but cannot distinguish an exact replay from conflicting reuse.
- What happens as history grows? Memory usage grows with successful IDs. Expiry would change replay behavior, so it needs an explicit retention requirement.
- What changes with concurrency? Separate checks and dictionary writes could interleave. Synchronization must cover the check-and-update operation; adding a lock to only one assignment is insufficient.
These are original review prompts. Answer the extension briefly, then return to the implemented contract. Name the condition that would break your design and the requirement that would justify changing it.
Keep AI and reference-tool rules explicit
Official policy: Monzo’s AI guidance supports preparation and refining your own work, prohibits AI answering live interview questions, and advises checking with the recruiter about task-specific use. Preparation permission does not automatically authorize generated take-home code or an active coding assistant during pairing. Confirm the written rules for your process and arrange any AI-related accessibility adjustment beforehand. Monzo AI guidance
Practice with the tools you will actually be permitted to use. You should be able to explain every submitted line and reproduce the reasoning behind a fix.
Five questions for targeted follow-up practice
These verified PracHub records come from other companies. They are transferable preparation, not reported Monzo interview questions. The two discussion prompts specifically support the take-home review, while the implementation and debugging prompts support both formats.
| Practice question | Use it to rehearse |
|---|---|
| Implement Notification Rate Limiter | Turn stateful rules into an interface and keep denied requests from corrupting state. |
| Find Bugs in an LRU Cache | Produce a minimal failing test before changing update or eviction behavior. |
| Debug and Improve a Load Balancer | Explain a debugging hypothesis and verify the resulting correction. |
| Explain a Difficult Technical Decision | Connect a design choice to constraints and rejected alternatives. |
| Critique a Recent Engineering Project and Explain What You Would Redo | Discuss improvements without pretending the original work had no limitations. |
Choose your format after a rehearsal, then use the Backend Engineer collection to target the weakness you observed: implementing a contract, diagnosing a failure, or defending a decision.
Sources and Further Reading
- Monzo: Demystifying the backend engineering interview process, March 2025
- Monzo: Staff Backend Engineer listing, checked September 2026
- Monzo: Guidance on using AI during hiring
- Candidate discussion: Monzo software engineer process, June 2025
- Candidate account: Take-home review before system design, June 2026
Comments (0)