Read the brief like a spec, not a puzzle
A take-home is a small project with a hidden rubric. Before you write a line of code, read the brief twice and pull out three things: what the reviewer explicitly asks for, what they imply, and what they did not mention. The explicit asks are non-negotiable. The implied asks, like reasonable error handling or a way to run the project, are where many candidates lose points. The unmentioned parts are your time budget protection. If the brief does not ask for authentication, do not build authentication.
Make a short list of the required features and treat it as your acceptance test. If the brief says "the API should return paginated results and handle invalid input gracefully," those are two checkboxes you must be able to tick at the end. Reviewers often grade against the brief almost literally. Missing a stated requirement reads worse than a smaller, cleaner submission.
It helps to physically separate the three categories. Open the brief and tag each sentence. A worked example makes this concrete. Suppose the brief reads:
Build a small service that ingests a CSV of transactions and exposes an endpoint returning each customer's running balance. It should handle malformed rows sensibly. Include a short note on how you would scale it.
From that single paragraph you can extract:
| Category | What you found |
|---|---|
| Explicit | CSV ingestion, an endpoint for running balance per customer, handling malformed rows |
| Implied | A way to run it, a defined response shape, at least one test for the balance maths |
| Asked but not coded | A written scaling note, not a built scaling solution |
| Out of scope | Auth, a UI, a real database, deployment |
That table is your contract. The scaling note is the trap most people miss: the brief asks you to describe scaling, not to build a distributed queue. Building it burns hours and signals weak judgement. A clear paragraph about partitioning by customer id and batching the ingest answers the question in ten minutes.
If anything is genuinely ambiguous, you have two options: email a short, specific question, or state your assumption in the README and move on. Both are fine. A vague "what should I do here?" is not; it reads as someone who cannot make a call. A good clarifying question is narrow: "Should the balance endpoint return all customers or accept a customer id?" That shows you have already understood the rest.
Decide the time budget before you start
Most take-homes suggest a time box, often three to five hours. Honour it, even if you are tempted to keep polishing. Reviewers can tell when someone spent twenty hours, and an overbuilt submission can backfire because it suggests poor judgement about scope.
Split the budget roughly like this:
- Ten percent on reading the brief and planning.
- Sixty percent on the core feature working end to end.
- Fifteen percent on tests for the parts that matter.
- Fifteen percent on the README, cleanup, and a final pass.
Get one thin slice working end to end early, even if it is ugly, before you make any part nice. A submission where the main path runs beats a half-finished elegant abstraction every time. If you run low on time, a working feature with a noted gap is far stronger than a broken feature with beautiful code.
A practical way to enforce this is a hard checkpoint at the halfway mark. If you are not yet running end to end at the midpoint, stop adding scope and start cutting. The instinct under time pressure is to keep building outward; the discipline is to drive a single path all the way through, then widen it. Treat the time box like a sprint commitment: scope is the variable you flex, not quality and not the deadline.
The single most common reason a strong engineer fails a take-home is not a weak algorithm. It is running out of time on the eighty percent that does not matter and submitting a core path that does not run.
What reviewers actually grade
Reviewers are not looking for cleverness. They are imagining what it would be like to have you on the team. They tend to weigh these in roughly this order.
Does it work and is it easy to run
The first thing a reviewer does is clone the repo and try to run it. If the setup is unclear or it does not start, you have already lost ground. A clear README with exact commands matters more than people expect.
## Run
1. Copy `.env.example` to `.env`
2. `npm install`
3. `npm run dev`
4. Open http://localhost:3000
## Test
`npm test`Is the code readable
Naming, structure, and consistency carry more weight than any individual algorithm. Use the conventions of the language and framework. Keep functions small and named after what they do. A reviewer should be able to follow the main path without jumping through ten files.
Did you make sensible decisions
Reviewers value judgement. If you skipped something on purpose, say so. A short note like "I chose an in-memory store to keep setup simple, in production this would be a real database" turns a perceived gap into evidence of good thinking.
Good versus weak, side by side
It is easier to internalise the rubric when you can see the same trait done two ways. The contrasts below are the ones that come up in almost every debrief.
| Trait | Weak submission | Strong submission |
|---|---|---|
| Setup | "Should just work, run the app" | Exact commands, sample env file, stated Node version |
| Structure | One 600 line file, mixed concerns | Clear layers, the core logic isolated from I/O |
| Errors | Crashes on bad input | Validates input, returns a clear error, has a test for it |
| Commits | One commit titled "done" | Small commits that tell the story of the build |
| Scope | Built auth and a UI nobody asked for | Built exactly the brief, noted the rest as future work |
| Tests | Fifty assertions on getters | Three tests on the real risks |
None of these require talent the reviewer cannot already see. They require restraint and a final pass, which is exactly the behaviour a team wants from a colleague.
Tests that signal seriousness
You usually do not need full coverage. You need tests on the parts that would embarrass you if they broke. Test the core business logic, the tricky edge cases, and the input validation the brief asked for. Skip testing framework glue and trivial getters.
test("rejects a negative quantity", () => {
expect(() => addItem({ id: "a", quantity: -1 })).toThrow();
});
test("merges duplicate items by id", () => {
const cart = addItem(addItem(empty, { id: "a", quantity: 1 }), {
id: "a",
quantity: 2,
});
expect(cart.items).toHaveLength(1);
expect(cart.items[0].quantity).toBe(3);
});Two or three meaningful tests that cover the real risks say more than fifty shallow ones. They tell the reviewer you know what is worth protecting.
A useful heuristic: ask which lines, if silently wrong, would produce a believable but incorrect answer. Those are the dangerous ones. A balance calculation that quietly drops a row is far worse than a route handler that throws loudly, because the wrong number reaches the user and nobody notices. Aim your tests at the silent failures. Cover one happy path, one boundary, and one invalid input, and you have shown the instinct reviewers check for without a single redundant assertion.
If you genuinely run out of time for tests, do not leave nothing. Write one test on the most important calculation and add a line in the README: "I tested the core balance logic and would extend coverage to the CSV parser next." That single test plus that sentence reads far better than an untested submission, because it proves you know testing matters and made a deliberate trade rather than forgetting.
Write the README that frames your work
The README is your cover letter for the code. Keep it short and put it in this order: how to run it, what you built, the decisions you made and why, and what you would do with more time. That last section is powerful. It lets you show awareness of the gaps without having to fill them.
A good "with more time" section reads like this:
With more time I would add rate limiting on the write endpoints, move the store to Postgres, and add integration tests for the checkout flow. I left these out to stay within the time box and keep the focus on the core ordering logic.
That paragraph answers the questions a reviewer would otherwise raise in the debrief.
The "decisions and why" section separates senior submissions from junior ones. A junior README lists what was built. A senior README explains the forks in the road. For each meaningful choice, name the alternative you rejected and the reason. "I used a single table rather than normalising customers and transactions, because the dataset is small and the join would add complexity the brief does not need" tells a reviewer you considered the trade and made a call. That sentence is worth more than another hour of code.
One quiet detail reviewers notice: your commit history. A clean sequence of small commits, each with a clear message, lets a reviewer read the story of how you worked. A stream of "wip" and "fix" commits is a missed chance to show how you think.
What shifts as the level rises
One thing about take-homes is genuinely level-dependent, and it is worth naming because it changes where you spend the final hour. The code itself matters less the more senior the role. A junior submission is mostly read for correctness and readability: does it run, is it clean, did you follow the conventions of the language. By the time the same brief lands in front of a senior candidate, the working code is close to assumed, and the reviewer is reading the decisions instead. The scaling note, the "with more time" section, and the trade-offs in the README carry the signal. A senior engineer who gold-plates a toy CSV parser and skips the scaling paragraph has spent their hours in the junior part of the rubric and answered the wrong question.
The practical instruction is the same whoever you are: get the core path running, then move your remaining effort up the rubric toward judgement. The difference is only how far up you are expected to climb.
What does not shift much is the language-specific care reviewers expect. A backend brief is read for a clear request contract, input validation, and what happens on bad data. A frontend brief is read for component structure and how the UI behaves while loading or when a call fails, so an explicit empty state and error state are quietly worth a lot. A full-stack brief is read at the seam: how cleanly the two sides talk and whether the contract between them is written down anywhere. None of that is about seniority; it is about reading the brief for what its domain actually cares about.
The failure modes that sink strong engineers
- Building past the brief. Extra features read as poor scope control, not generosity.
- No way to run it. A missing command or an undeclared dependency means the reviewer never sees your work at its best.
- Leaving the mess in. Dead code, commented-out blocks, stray console logs, and a
node_modulesaccidentally committed all chip away at the impression. - Silent assumptions. If you decided something the brief did not specify, say so. An unstated assumption looks like a misunderstanding.
- One giant commit at the end. It hides your process and looks rushed.
- Premature abstraction. A factory and three interfaces for a one-off script signals someone who reaches for patterns reflexively.
- Ignoring the obvious edge case. Empty input, a single record, a duplicate id. These are the first things a reviewer will try.
On using AI assistants honestly
Many candidates now use AI tools to draft parts of a take-home. The reasonable position is to use them the way you would on the job, then own every line you submit. Problems show up when candidates paste code they cannot explain. Some teams now add a short follow-up call where they ask you to walk through your own submission or extend it live. If you cannot defend a decision in your code, it does not matter who or what wrote it. Treat the assistant as a faster way to your own understanding, not a replacement for it.
A practical test before you submit: pick any non-trivial function and explain out loud why it is written that way, what the alternatives were, and what would break if a key line changed. If you cannot, rewrite it until you can. The live extension call is becoming common precisely because it exposes the gap between code you produced and code you understand, and that gap is the only thing the exercise is really measuring.
Final pass before you submit
Spend the last fifteen minutes as a reviewer would. Clone your own repo into a fresh folder and follow your own README exactly. Run the tests. Read the diff top to bottom and remove dead code, stray console logs, and commented-out blocks. Check that file and function names still match what they do after your last changes. Then write a short submission message that points the reviewer at the README and names one thing you are pleased with. A calm, complete, well-explained small project beats an ambitious unfinished one in almost every loop.
Frequently asked questions
How long should I really spend? Treat the suggested time box as a ceiling, not a target. If the brief says four hours, aim to be done in three and use the last hour for the README and the final pass. Going well over signals you cannot scope.
Should I deploy it? Only if the brief asks. A clear local run is enough almost everywhere. A broken deployment hurts you more than no deployment.
What if I cannot finish? Submit the working slice, be honest about what is missing, and explain what you would do next. A reviewer would far rather see an honest partial than a broken whole.
Do I need a UI if the brief is backend? No. A clear API and a way to call it, such as a curl example or a short test, is plenty. Building an unrequested UI is the classic scope mistake.
Should I ask questions before starting? Yes, if something is genuinely ambiguous and the answer changes your design. Keep it to one or two specific questions, then proceed on stated assumptions for the rest.
Is it fine to use my usual tools and AI assistants? Yes, as long as you understand and can defend every line. Expect a possible follow-up where you walk through or extend the code live.
Where to go next
If the brief came with a suggested time box and you want to sanity check whether it is reasonable for the scope asked, the take-home time calculator gives you a quick second opinion before you commit an evening. For the follow-up call that increasingly accompanies take-homes, how take-homes are being redesigned against AI assistance explains what reviewers now watch for when they ask you to walk through your own code live.
Sources
- GitHub ReadME, "Using code as documentation", on comments, naming, and config as part of how a reviewer reads a repository.
- Martin Fowler, "Code Smell", on the surface signals reviewers react to when judging readability.
- HackerRank, "Testing real-world development skills", on why employers increasingly favour practical project-style assessments over puzzle questions.