The take-home assignment
This is the round your profile should win outright. Four production applications and an AI platform mean you have shipped the exact thing they are asking for, at real scale, with real edges. The candidates who beat you here are not better engineers — they are the ones who made the reviewer's fifteen minutes easy.
A senior engineer opens your repository between two meetings. In order: reads the README, tries to run it, skims the folder structure, opens the one file the task is really about, looks at the tests, checks the commit list. Fifteen minutes, sometimes ten.
Everything follows from that. If npm install && npm run dev does not produce a working thing on the first try, the rest of your work is not read — not because they are lazy, but because "it does not run on a clean machine" is itself the finding.
Test this by cloning your own repository into a fresh folder and following your own README literally, on a machine where you have not been developing. Every missing step shows up in ninety seconds.
A .env that only exists on your laptop. Ship .env.example with every key, and make the app fail loudly with a readable message when one is missing — that is a point in your favour, not an excuse.
Build the stated requirements completely and nothing else. A junior adds features; a senior finishes the brief and writes down what they deliberately left out.
- Do the whole brief. Every bullet in the spec, including the boring one at the bottom. Reviewers score against the list.
- Cut breadth, not quality. Three endpoints done properly beat eight endpoints with no validation.
- Put everything else in "what I would do next". Auth, pagination, caching, rate limits — listing them costs one line each and proves you saw them.
On time: spend roughly what they asked, plus the polish hour. Twenty hours on a four-hour task is not impressive — if it comes out, it reads as someone who cannot estimate, and it makes every future estimate you give suspect.
Adding a feature they did not ask for to show range — a dashboard, a dark mode, a websocket. It reads as an inability to hold scope, and it steals the time your error handling needed.
- What did you leave out and why?
- How long did this actually take?
- What would you do differently with another day?
The README is the highest-leverage file in the submission — it is the only one guaranteed to be read. Six sections, no more than a page:
# Order service
## Run it
docker compose up # postgres + redis
cp .env.example .env
npm install && npm run migrate && npm run seed
npm run dev # http://localhost:3000
npm test
## What it does
Three endpoints from the brief: create order, list orders
by customer, cancel within the 30-minute window.
## Decisions
- **Postgres over Mongo.** Orders are relational and the
cancel rule needs a transaction.
- **Service layer separate from controllers.** Controllers
parse and shape HTTP; nothing else knows about HTTP.
- **Validation at the boundary** with DTOs, unknown keys
stripped — malformed input and mass assignment both die
at the edge.
## Trade-offs I made on purpose
- In-memory idempotency keys. Correct for one instance,
wrong behind a load balancer — Redis in production.
- No pagination on list. Fine for the seeded volume,
not for real.
## What I would do next
Auth, cursor pagination, an outbox for the cancel event,
and a metric on cancel latency.
## Time
About five hours.The "trade-offs I made on purpose" section is the one that separates you. It converts every shortcut from something they find into something you decided — and it is the exact conversation they will open the defence call with.
Nobody expects full coverage on a take-home, and 100% coverage on a four-hour task reads as either padding or a wildly over-spent weekend. What gets scored is whether you tested the right thing.
- The core rule, unit tested, including its edges. If the brief has a thirty-minute cancellation window, test twenty-nine minutes, thirty-one minutes, and exactly thirty.
- One integration test through the real HTTP layer for the happy path. It proves the wiring, which unit tests never do.
- One failure test. Bad payload returns a 400 with a useful body.
Five to ten meaningful tests. Then make sure npm test passes on a clean clone — a failing test suite in a submission is worse than no tests at all.
Tests that assert on mocks you wrote yourself, proving only that your mock returns what you told it to. One real test through the stack outweighs twenty of those.
Reviewers open the commit list. It is the closest thing they have to watching you work, and it takes them ten seconds.
- Eight to fifteen commits, each a coherent step: scaffold, schema, service, controller, validation, tests, README, polish.
- Messages in the imperative, describing intent: "reject cancel after the 30-minute window", not "fix bug".
- No single "initial commit" containing the entire project. It says you developed it somewhere else, and it removes the one artefact that showed your process.
- No commented-out code and no
console.login the final commit. Both are read as how you actually work.
If the repository is private and they ask you to zip it, include the .git folder. Most candidates strip it and lose the signal entirely.
- Input validation at the boundary with unknown properties stripped. Say why in the README: malformed input and mass assignment both stop at the edge.
- Error responses with a shape. A consistent
{ error, message }and correct status codes. Not a stack trace, not a 200 with{ success: false }. - Configuration through env, with
.env.examplecommitted and no secrets in the repository. A committed key is an instant no in a security-aware team. - One migration and one seed script. It is the difference between "I can run it" and "I cannot see your data model".
- A one-command start.
docker compose upfor the dependencies. On a Node take-home this alone puts you above most of the pile.
Give the real figure, rounded honestly, then attach what it bought. Under-reporting to look fast is the trap — commit timestamps are right there, and a claimed three hours against nine hours of commits is a credibility problem in a round that was supposed to be about your code.
About five hours — roughly three on the service and the cancel rule, one on tests, and the last hour on the README and the docker setup. If you want a version with auth and pagination I would want another half day.
- Was that in one sitting?
- Which part took longest?
- What would you cut if you had two hours?
Nearly every take-home is followed by a thirty to forty-five minute call on the code. Two things happen in it, and both are predictable:
- "Walk me through your decisions." Your README already answers this — reread it before the call so the story matches, word for word.
- "Now add X, here, with me watching." Add a field, add an endpoint, change the rule. This is where a well-separated codebase pays: if the change is a new file plus three lines, you pass. If you have to open a 400-line controller and hunt, the structure was decoration.
Reopen the project the morning of the call. A week is long enough to forget your own file names, and hesitating in your own repository is the one thing that reads as "someone else wrote this".
- Why this folder structure?
- Where would this break at a thousand requests a second?
- What is the first thing you would refactor?