Four take-home assignments, one workflow
Four take-homes in about five weeks: a classification system, a multi-agent research system, an API service and a schema mapper, all built with a two-track Claude and Cursor workflow. Two led to offers. The designs, the evaluation, and defending AI-assisted code.
Contents
The defense call for my first take-home assignment had one purpose, and the interviewer did not hide it: to find out whether the design decisions were mine or the AI’s. The company expected candidates to use AI tools and to disclose it. What it wanted to know was whether I could explain every choice in the system, including the ones I had rejected, without looking anything up.
That question shaped how I built all four take-homes in this search. AI made the code fast to write. It did not make the decisions, and it could not sit the defense call for me.
This post describes the workflow I used for all four assignments (a planning track with Claude and an implementation track with Cursor or Claude Code, with a gate between every step) and the design decisions in each: a system that classifies employee records into departments and teams, a multi-agent research system, an API service, and a mapper from arbitrary spreadsheet headers to a fixed schema. It is part 12 of the 35 companies, 8 offers series.
The four were for Vieu, ThirdAI, Aiseo-ai and Winmore, between early April and mid-May 2026. I describe each at the level of the problem and my own design, not the companies’ materials.
The headline results:
- Take-homes: 4 in about five weeks
- Offers that followed: 2 (one accepted, one I did not take)
- Schema mapper: all 27 columns in the provided examples mapped correctly, 24 of them by the cheapest stage alone
- Classification system: two gaps flagged by the reviewer after submission, both closed before the defense call
Background: what a take-home tests now#
When AI can write most of the code, a take-home for a senior role stops being a test of typing speed. In my experience it tests four other things.
Scoping. Every brief was underspecified. Deciding what to build, what to leave out and what to document as future work was most of the judgment.
Evaluation. A system without a way to measure its own quality is a demo. The reviewers who gave me the most useful feedback asked how I knew it worked.
Restraint. Take-homes invite over-engineering: frameworks, plugin systems, abstractions for requirements nobody stated. Reviewers read the code, and complexity they did not ask for reads as poor judgment.
Ownership. If the reviewer suspects the decisions came from a model, the code quality stops mattering. Every decision has to be one you can defend in your own words.
The two-track workflow#
Every assignment ran on the same split. Claude, in a planning conversation, handled reading the brief, analyzing the data, architecture, decisions and the sequence of implementation prompts. Cursor (and, for the last assignment, Claude Code) wrote the code, one prompt at a time. I sat between them.
graph LR
A["Brief and data"] --> B["Planning track:<br/>analysis and design"]
B --> C[("Architecture doc<br/>with rejected options")]
C --> D["One implementation<br/>prompt"]
D --> E["Build track:<br/>Cursor or Claude Code"]
E --> F["I relay the output"]
F --> G{"Validate"}
G -->|"pass"| D
G -->|"fail"| B
G -->|"done"| H["Submit with<br/>AI disclosure"]
class A gray
class B,C amber
class D,E violet
class F,G gray
class H greenThree rules made this work.
Decisions before code. Every assignment started with an architecture document that recorded each decision, the alternatives considered and why they were rejected. That document became the script for the defense call.
One prompt, one validation. The build track never got a multi-step prompt. Each prompt produced one piece; I ran it, relayed the output to the planning track, and only then wrote the next prompt. Bugs surfaced one step after they were introduced instead of at the end.
I correct the planner. The planning track was wrong often enough to matter. On the research system, its first design decomposed questions around a fixed topic domain, when the brief needed any arbitrary research question to work. On the classification system, it described parts of my own code imprecisely when preparing me for the defense call, and I had to correct it against the actual files. Owning the decisions meant checking them.
A classification system with a defensible taxonomy#
The first assignment was to classify about 1,000 employee records (job title plus description) into departments and named teams, with commands to classify new records and look records up.
The design went through three versions. Pure LLM classification was simple but expensive and impossible to audit. Embeddings with clustering produced groupings the reviewers would not recognize as departments. The final design was a hybrid: priority-ordered keyword rules with word-boundary matching for clear cases, and a small LLM for ambiguous ones.
graph TD
A["Employee record"] --> B["Rules: ordered<br/>keyword matching"]
B -->|"clear match"| D["Merge by<br/>match source"]
B -->|"ambiguous"| C["Small LLM with<br/>enum-locked output"]
C --> D
D --> E["Department plus<br/>team or null"]
E --> F["Confidence tier:<br/>high, medium, low"]
F -->|"low"| G["Human review queue"]
E -.->|"evaluate command"| H["LLM judge on a<br/>stratified sample"]
class A gray
class B,C amber
class D violet
class E,F blue
class G red
class H greenThree decisions carried the design.
A standard taxonomy, not a discovered one. I used an industry-standard set of 15 departments rather than clustering, because the reviewers were domain experts who would immediately recognize the standard categories and immediately distrust invented ones.
Make hallucination structurally impossible. The LLM’s output schema locked the department to an enumerated list. OpenAI’s Structured Outputs guarantees that responses adhere to the supplied JSON Schema, which means the model cannot invent a department. A trimmed version of the schema, with fewer departments than the real one:
{
"type": "object",
"properties": {
"department": {
"type": "string",
"enum": ["Engineering", "Sales", "Marketing", "Finance", "Legal", "People"]
},
"team": { "type": ["string", "null"] },
"confidence": { "type": "string", "enum": ["high", "medium", "low"] }
},
"required": ["department", "team", "confidence"],
"additionalProperties": false
}Prefer null to a guess. The team field was nullable, and the instructions preferred no team over an invented one. A wrong team is worse than a missing one, because it looks like data.
The reviewer’s feedback on the first submission named two gaps: no way to measure accuracy, and teams that were predefined rather than discovered from the data. I closed both. An evaluate command used a larger model as a judge on a stratified sample, with agreement between models as a check on the judge. It showed a large gap between lenient and strict accuracy, which was the honest finding. A --discover-teams flag embedded records, reduced dimensions, and ran HDBSCAN, a density-based clustering method that finds clusters of varying density and marks sparse points as noise, within each department, then had the LLM name each cluster. Discovered teams covered somewhat fewer records than the predefined ones but were far more specific.
The defense call was the real test. I prepared a document of every decision with the alternatives I rejected and why, and practiced against an adversarial version of the reviewer. The offer followed.
A multi-agent research system with no framework#
The second assignment was a multi-agent system that takes a complex research question and produces a sourced report.
I built it on the raw model API with no agent framework, so every orchestration decision was visible in the code. It had two layers. A planning loop understood the question, decomposed it into subtasks and validated the decomposition, with a hard cap of two rounds before moving on. An execution layer ran the subtasks as a dependency graph with four typed agents (retrieval, domain reasoning, critique and synthesis) dispatched through a registry. Failed calls retried once. The system wrote both a JSON trace for observability and a Markdown report for the reader, and logged every decision to the console so a reviewer could follow the reasoning without opening a file.
Two bugs surfaced in the smoke test, one in how responses were read and one in the executor’s bookkeeping, and both were fixed before the full run. I scored the three test questions against my own 15-point rubric: two scored 12, and the third scored 7 because the synthesis step produced an incoherent report. I submitted it with that result documented rather than hidden.
A schema mapper that stayed linear#
The fourth assignment, with a two-day deadline, was to map arbitrary column headers from shippers’ spreadsheets onto a fixed internal schema of seven fields, with confidence scores and as little manual work as possible.
I wrote the design document first and put it through two rounds of review by an outside reviewer. I accepted most of the feedback and defended two points with reasons. The final design was a linear cascade of five stages, cheapest first: normalize the header, look it up in an alias dictionary (with an explicit list of ambiguous short aliases like “from” and “to”), try fuzzy matching, try embedding similarity, and only then fall back to an LLM.
Restraint was a stated requirement of mine. The pipeline was five explicit function calls in sequence: no base classes, no pipeline framework, no dependency injection. Confidence scores were documented as heuristic indicators, not calibrated probabilities, because claiming calibration I had not measured would have been the first thing a reviewer attacked. A header that genuinely covered two fields returned both candidates and an ambiguity flag rather than a forced guess.
All 27 columns across the provided examples mapped correctly, 24 of them at the alias stage, and the test suite passed on a fresh clone. The design document opened with a summary readable in under a minute (results, the cascade, key decisions, known failure modes), followed by the full technical write-up. An offer followed, which I did not take.
The API service, and keeping assignments private#
The third assignment was a small API service with two endpoints, one that scored content and one that generated related queries and analyzed gaps. I planned it in phases (scaffold and schemas, then each feature, then tests and documentation) with a prompt log recording how AI was used. I did not take that process further.
One assignment raised a question I had not expected: whether to keep the solution in a public repository. I did not want an assignment the company had not intended to be public sitting on a GitHub profile that gets real traffic. I offered to share it as a zip file or by email instead, and said I would withdraw if neither worked. A private repository with the reviewers added as collaborators is a cleaner third option, and the one I would offer first next time.
Results#
The four assignments and what came of them:
| Assignment | Core design decision | Outcome |
|---|---|---|
| Employee classification (Vieu) | Rules plus enum-locked LLM, standard taxonomy, evaluation command | Offer, accepted |
| Multi-agent research (ThirdAI) | No framework, planning loop plus typed agents in a dependency graph | Follow-up calls; I stopped the process |
| API service (Aiseo-ai) | Phased build with a prompt log | I stopped the process |
| Schema mapper (Winmore) | Linear five-stage cascade, cheapest first | Offer, not taken |
The workflow produced working, documented systems on short deadlines, two to three days where the brief stated one. More importantly, it produced systems I could defend, because every decision had been made and written down in the planning track before any code existed.
What I’d do differently#
Build evaluation into the first submission. The classification reviewer’s first comment was that there was no accuracy analysis. Every take-home should ship with a way to measure itself, even a rough one.
Settle the sharing question before building. I raised the public-repository concern after the work was done. Asking at the start avoids an awkward exchange at the end.
Timebox scope explicitly. The research system’s third question scored poorly because I ran out of time to fix synthesis. A written cut list at the start would have made that trade-off on purpose rather than by accident.
Keep the planner honest. The planning track was wrong several times. Reading the actual code before every defense, rather than trusting the planner’s summary of it, was the habit that saved the defense call.
Conclusion#
AI changed what a take-home measures. Code is cheap now; decisions, evaluation and restraint are not. The workflow that worked for me split the two: a planning track that owned every decision in writing, a build track that executed one validated step at a time, and me in between, correcting both.
Use AI to write the code. Make the decisions yourself, write them down with the alternatives you rejected, and be ready to defend every one without the model in the room.
Part 12 of the 35 companies, 8 offers series. I join Google as a Software Engineer III, AI/ML, in October 2026.
Responses
Select any passage to highlight it or comment on that line. Highlights stay private, in this browser.