Candidates optimise take-homes for completeness. Reviewers grade them for judgement.

That single mismatch explains most rejections. Someone spends fourteen hours on a four-hour task, implements every optional requirement, and loses to a submission that did two-thirds of the work and explained why. The reviewer isn't being perverse — they're hiring someone who will make sensible calls under a deadline, and a submission that ignored the deadline has answered the question badly.

3
Files a reviewer opens, in a predictable order
1
Document that changes the review: the README
0
Credit for finishing everything if the code is unreadable

What gets opened, and in what order

Reviewers are busy and have a pile of these. The sequence is remarkably consistent:

OrderWhatWhat they're deciding
1The READMEWhether you understood the task and can write
2The test fileWhether you test, and what you chose to test
3The hardest part of the domain logicWhether the code is readable by someone who didn't write it
4Everything else, quicklyConsistency

Most candidates put their effort in inverse proportion to that list. The README gets ten minutes at 1am; the tests get skipped because time ran out; the domain logic gets written for an audience of one.

The README that changes the review

This is the highest-leverage hour in the whole exercise. Here's one that works, for a typical brief — build a small service that ingests CSV transactions and exposes a summary endpoint.

Notes

Time spent: about four and a half hours. The brief said four; I went slightly over on the parsing edge cases and I've noted below what I'd have cut if I'd been strict.

What I built: ingestion, validation, the summary endpoint, and tests for the parsing and aggregation logic.

What I deliberately didn't build:

  • Authentication. The brief didn't ask and it would have been an hour of framework configuration that tells you nothing about how I think.
  • A database. Everything is in memory. For a real version I'd use Postgres and the aggregation would move into SQL — that's a ten-line change in SummaryService and the interface is already shaped for it.
  • Pagination on the summary endpoint. It returns a fixed number of buckets, so it can't grow unboundedly. If the buckets were user-defined, this would need it.

Decisions worth mentioning:

  • Rows that fail validation are collected and returned with the response rather than aborting the ingest. The brief was ambiguous here — for a financial import I'd normally argue for rejecting the whole file, because a partial import is hard to reason about. I went the other way because the endpoint is a summary rather than a ledger. I'd want to check that assumption.
  • The parser is deliberately dumb and slow. It's clear, it handles the cases in the sample, and I'd only optimise it against a real file size.

What I'd do next, in order: persistence, then the failure handling above once someone tells me which behaviour they want, then performance if the files are large.

Running it: make test and make run. Tested on Node 20.

Ten minutes to read, and the reviewer now knows how you think about scope, ambiguity and trade-offs — before looking at a single line of code. It also pre-empts the three things they were going to criticise.

The section that does the most work is "what I deliberately didn't build". Without it, every gap in your submission is indistinguishable from something you didn't think of. With it, each gap becomes evidence of a decision. This is the single cheapest improvement available on a take-home and most candidates skip it entirely, usually because admitting to an omission feels like weakness — it reads as the opposite.

How much to build

Take the stated time limit literally, and if there isn't one, assume four hours.

The reasoning isn't moral. A submission that took fourteen hours tells the reviewer nothing about how you work in a week where you have three other things to do — and they can usually tell, because the polish is inconsistent with the scope.

If you go over, say so and say by how much. Nobody has ever been rejected for "I spent five hours rather than four". People are regularly rejected for a submission whose size is implausible against the stated limit and whose README claims it took four.

Where to spend the time, in order:

  1. Make it run from a clean checkout, with one command. A submission that doesn't start is a rejection regardless of what's inside.
  2. Write the README.
  3. Test the part with the actual logic in it.
  4. Make the hardest function readable.
  5. Everything else.

Tests: what to write when you can't write them all

Three or four tests that demonstrate judgement beat twenty that demonstrate diligence.

Test the thing the brief is actually about. If the task is CSV ingestion with edge cases, test the edge cases — the malformed row, the empty file, the duplicate, the one with a trailing comma. Don't test that your framework's router routes.

And if you ran out of time, write it down: "No tests on the HTTP layer. If I'd had another hour that's where it would have gone, because the validation error responses are the part most likely to be wrong." That sentence costs nothing and converts an omission into a demonstrated priority.

What gets rejected quietly

The complete submission

Every optional requirement implemented, no README beyond setup instructions, two hundred lines in one file, and no tests because "the brief didn't ask for them".

The reviewer can't tell which parts were hard for you, why anything is the way it is, or how you'd behave with a deadline. Completeness answered a question nobody asked.

What gets the call

The scoped submission

Two-thirds of the features, a README naming what was cut and why, four tests on the actual domain logic, and one honest sentence about an ambiguity in the brief.

Less code, more signal. Every gap is accounted for, so the reviewer spends their time evaluating your judgement instead of guessing at it.

The follow-up call

Most take-homes are followed by a thirty-minute conversation, and that conversation — not the code — is usually where the decision is made. Three things come up almost every time.

"Walk me through your approach." Don't narrate the code top to bottom. Start with the shape: "Three pieces — parse, validate, aggregate. The interesting one is validation, because the brief was ambiguous about partial failures and I made a call I'd want to revisit." Then let them pick.

"What would you change?" Have three answers ready and make one of them uncomfortable. "The aggregation function is doing two things and I noticed at hour four and left it. I'd split it." Candidates who can't criticise their own submission are usually candidates who can't be reviewed.

"Why didn't you do X?" If your README covered it, say so and repeat the reasoning — consistency between the document and the conversation is itself being checked. If it didn't, the honest answer is better than a reconstructed rationale: "I didn't think of it. Having seen it now, I'd have…"

Expect one question where they suggest a different approach and watch what you do. The strongest response concedes the part that's fair before defending the part that isn't. That's the same instinct the whole loop is testing, and it's covered in more depth here: full-stack developer interview questions.

Four things that lose a submission outright

  • It doesn't run. Test a clean clone into an empty directory before you send it.
  • Committed secrets or a 200MB node_modules. Read your own diff.
  • One commit called "solution". The history is part of the submission; four or five sensible commits tell a story about how you work.
  • Code copied without understanding. It's visible, and the follow-up call finds it in about ninety seconds.

If the brief is unclear

Ask. One short email to the recruiter — "should the summary endpoint handle date ranges, or is a fixed window fine?" — costs nothing and puts you ahead of the candidates who guessed.

If there's no one to ask, make the assumption explicit in the README and pick the simpler interpretation. An assumption stated is a decision; an assumption hidden is a mistake.

Practical target: write the README before you write any code. It takes twenty minutes, it forces you to decide what's in scope before you're attached to something you've already built, and the "what I deliberately didn't build" list stops being an apology at 1am and becomes the plan you worked from.