Resume & portfolio
The filter before the interview: a resume that survives the 6-second scan and the ATS, and a portfolio that proves you can build — with runnable checkers for your own materials.
Before interviews there's a filter: a resume a recruiter scans in ~6 seconds and an ATS (applicant tracking system) that keyword-matches you to the role, plus a portfolio (your GitHub + projects from this course) that proves you can actually build. This chapter makes both strong — with a runnable checker you can point at your own resume.
The words you'll hear (in plain terms):
| Term | What it actually means |
|---|---|
| ATS | software that scans resumes for keywords before a human sees them. |
| portfolio | public proof of skill: GitHub repos, deployed projects, writeups. |
| impact bullet | a resume line showing result + how ("cut latency 40% by adding caching"). |
| keyword match | how well your resume's terms overlap the job description. |
| signal | evidence an employer reads as "can do the job". |
What you need before starting:
- Having built a few projects here (the capstones are portfolio-ready).
- A draft resume + your GitHub to apply this to.
- The Python checker runs offline — paste your own text in.
New to the topic? Read this box, then take the chapters in order — each section is tagged essential → expert so you always know the depth you're at.
Learning objectives
- Write impact-driven resume bullets that survive the 6-second scan.
- Pass the ATS keyword filter without stuffing.
- Turn course projects into portfolio proof.
- Curate a portfolio like a hiring manager reads it.
code/cr1-resume-portfolio/ — run them against your own resume, stories, and offers.1 · The 6-second scan & impact bullets essential
Picture the moment your resume actually lives or dies. A recruiter has forty PDFs open and a role to fill by Friday. Yours gets somewhere between six and ten seconds of a human eye before they decide "phone screen" or "next." They are not reading — they are pattern-matching for evidence that you'll do the job. In that window, a resume full of "Responsible for the testing pipeline" and "Worked on backend services" reads as a list of chores someone assigned you. It answers "what were you allowed near?" — not the only question a hiring manager cares about: "what changed because you were there?"
Here's the reframe that fixes almost every weak bullet: a job description is a list of duties, but you are not applying to have duties — you're applying because you produced outcomes. So think of each bullet as a tiny before/after story. Before you: deploys failed one in three. After you: one in twenty. The bullet's whole job is to make that delta visible in one line. The analogy that sticks is a news headline: a headline never says "reporter was assigned to cover the fire" — it says "fire destroyed 40 homes; cause under investigation." Result first, mechanism second. Your bullets are headlines about your own work.
That gives us the shape the scorer below enforces — strong action verb → what you did → measurable impact → how. Each piece earns its place. The verb ("Cut," "Shipped," "Automated") signals ownership; "Responsible for" and "Helped with" are passive hedges that quietly tell the reader you were adjacent to the work, not driving it. The number is the single highest-leverage upgrade you can make: "improved performance" is a claim anyone can type, but "cut p95 latency from 800ms to 210ms" is a claim you can only make if it happened. Numbers convert assertions into evidence, and evidence is what survives the skim. The "how" (the "by adding a CI test gate with pytest" clause) is where your actual skills live — it's the part a technical interviewer will grab onto, so it doubles as bait for the conversation you want.
The common mistake isn't laziness — it's modesty combined with vagueness. Engineers undersell because the metric feels fuzzy ("I don't know it was exactly 30%"). The fix is not to invent a number but to estimate honestly and defensibly: "roughly halved our on-call pages" is fine if you can explain the reasoning when asked. A number you can defend beats a precise number you made up, and both beat no number at all. If you truly can't quantify impact, quantify scope instead — "across 4 microservices," "for a 12-person team," "handling 2M requests/day." Scale is itself a signal.
A recruiter skims; your bullets must show result + how, not duties. Formula: action verb → what → measurable impact → how. "Responsible for testing" is a duty; "Cut prod incidents 30% by adding a CI test gate" is impact.
Read top to bottom: the red bar at the top is the weak "before," the four coloured boxes are the parts you assemble, and the green bar at the bottom is the "after."
- The red top bar is a duty statement — "Responsible for the test suite." It scores 0/3 because it has no ownership verb, no number, and no method; it says what you were near, not what changed.
- The four middle boxes, read left to right along the blue arrows, are the parts of a strong bullet: a green action verb (Cut), the what (deploy failures), an amber number (30%), and the blue "how" (by adding a CI gate). Each earns exactly one point.
- The green bottom bar is those parts stitched into one line — the payoff. It scores 3/3: the same underlying work, now legible as impact in the six-second scan.
In short: a duty says "I was allowed near this"; adding a verb, a number, and a "how" turns it into "this changed because I was here."
bullet_score.pyimport re
def score_bullet(bullet):
score, notes = 0, []
if re.match(r"^(Built|Led|Cut|Shipped|Designed|Automated|Reduced|Improved|Launched)", bullet):
score += 1
else: notes.append("start with a strong action verb")
if re.search(r"\d", bullet): # has a number/metric
score += 1
else: notes.append("add a measurable result (%, time, count)")
if re.search(r"\bby\b|using|with", bullet): # explains HOW
score += 1
else: notes.append("say HOW you achieved it")
return score, notes
for b in ["Responsible for the test suite",
"Cut deploy failures 30% by adding a CI test gate with pytest"]:
s, n = score_bullet(b)
print(f"[{s}/3] {b}")
for note in n: print(" -", note)
[0/3] Responsible for the test suite
- start with a strong action verb
- add a measurable result (%, time, count)
- say HOW you achieved it
[3/3] Cut deploy failures 30% by adding a CI test gate with pytest
This little program grades one resume bullet (a single line on your resume) out of 3, and tells you what's missing. It captures the whole rule of a good bullet: it should start with a strong action verb, contain a number, and explain how you did it. You paste your own bullets in at the bottom.
import rebrings in Python's regular-expression tool — a way to search text for patterns.score_bullet(bullet)is the reusable checker; it starts thescoreat 0 and an emptynoteslist for advice.- Check 1 (verb):
re.match(r"^(Built|Led|Cut|...)", bullet)asks "does the bullet start with one of these power verbs?" The^means "at the very beginning". If yes,score += 1; if not, we append the advice "start with a strong action verb". - Check 2 (number):
re.search(r"\d", bullet)looks anywhere in the line for a digit (\d= any 0–9). A number is what turns a claim into evidence ("30%", "5 services"). - Check 3 (how): it searches for the words
by,usingorwith— the tell-tale that you explained the method, not just the result. - The
for b in [...]loop runs two example bullets through the checker and prints the score plus each note, so you can see a 0/3 and a 3/3 side by side.
What the output means: A weak line ("Responsible for the test suite") scores [0/3] and lists all three fixes; a strong line scores [3/3] with no notes. The number in brackets is how many of the three rules the bullet passed.
Try this: Paste one of your own resume lines into the list and run it. If it scores below 3, rewrite it using the missing pieces — add a verb, a number, or a "by …" — until it hits 3/3.
2 · Passing the ATS essential
You wrote a beautiful resume, applied to a role you're clearly qualified for, and heard nothing. No rejection, no screen — silence. It's demoralizing, and the instinct is to assume a person read it and passed. Often, no person ever did. Your resume hit an Applicant Tracking System (ATS) first: software that ingests, parses, and ranks applications so a recruiter can start from a shortlist instead of a mountain. Large companies use one because a single popular role can draw hundreds of applicants; a human simply cannot open them all. So before your content can impress anyone, your resume has to survive being read by a machine that has no judgment and no benefit of the doubt.
The mental model that demystifies this: think of the ATS as a very literal-minded intern with a highlighter and a checklist. It doesn't understand that "K8s" and "Kubernetes" are the same thing, or that your "container orchestration" experience obviously covers the "Kubernetes" the posting wants. It matches tokens — the actual words on the page against the actual words in the job description. That literalness cuts two ways, and understanding both is the whole skill. It means your words must echo the posting's words when the underlying skill is genuinely the same; and it means the machine can be gamed, which is precisely why the honest strategy matters.
The dishonest move — dumping the entire job description into white 1px text at the bottom of the page, or stuffing a keyword salad no human would write — is a trap for two reasons. First, modern ATS platforms flag suspiciously dense keyword repetition, and the score can go down. Second and more important: if the keyword gets you past the filter but you can't back it up, you've just spent a recruiter's time and a bad first impression to reach an interview you'll fail. The goal is not "beat the robot" — it's "make sure the robot doesn't hide a qualified human from the recruiter." Those are different aims, and only the second one survives contact with the actual interview.
So the discipline is mirror, don't manufacture. Read the posting, and where it names a skill you truly have, use its phrasing rather than your favorite synonym: if it says "CI/CD," don't rely on the reader inferring it from "Jenkins pipelines" — say both. The checker below turns this into a concrete gap list: it tells you which must-haves your resume mentions and which it's silent on. Treat the "missing" list as a two-column decision — for each gap, either it's something you genuinely have and forgot to name (add it, easy win), or it's a real skill gap (a signal to learn it, which is exactly what the rest of this course is for). What you never do is add the third option: claim it falsely.
Many resumes are auto-filtered before a human reads them. The ATS matches your resume's terms against the job description. Don't stuff — but do mirror the real skills you have using the words the posting uses.
Follow the flow left to right: a stack of resumes enters on the left, passes through the ATS in the middle, and comes out one of two ways on the right.
- The white box on the left is the applicant pile — hundreds of resumes, yours among them. No human has opened any of them yet.
- The blue middle box is the ATS. It compares the literal tokens on your page against the words in the job description — it does not know "K8s" and "Kubernetes" mean the same thing, so it is unforgiving about phrasing.
- The two outputs are the payoff: the solid "match" arrow to the green shortlist is where a resume whose words echo the posting lands; the dashed "miss" arrow to the red box is the silent rejection — a real skill that was present but never named in the posting's words.
In short: the filter matches words, not meaning — so for every skill you genuinely have, use the posting's own phrasing or risk being filtered before a person ever reads you.
ats_match.pydef keyword_match(resume, jd, must_haves):
r = resume.lower()
present = [k for k in must_haves if k.lower() in r]
missing = [k for k in must_haves if k.lower() not in r]
pct = round(100 * len(present) / len(must_haves))
return pct, present, missing
resume = "Built LLM apps with Python and FastAPI. Wrote pytest suites. Deployed with Docker."
must = ["Python", "Docker", "pytest", "Kubernetes", "CI/CD", "FastAPI"]
pct, have, miss = keyword_match(resume, "", must)
print(f"match: {pct}%")
print("have:", have)
print("missing (add if TRUE for you):", miss)
match: 67%
have: ['Python', 'Docker', 'pytest', 'FastAPI']
missing (add if TRUE for you): ['Kubernetes', 'CI/CD']
This checks how well your resume matches a job posting — the same idea an ATS (the software that filters resumes before a human reads them) uses. You give it your resume text, the job description, and a list of must-have skills; it tells you your match percentage and which skills are missing.
r = resume.lower()makes a lower-case copy of your resume so the comparison ignores capitalisation ("Python" and "python" count the same).present = [k for k in must_haves if k.lower() in r]is a list comprehension: it walks through every required skill and keeps the ones whose text appears in your resume.missingkeeps the ones that do not appear.pct = round(100 * len(present) / len(must_haves))turns "4 of 6 found" into a percentage (67%).len(...)just counts how many items are in a list.- The bottom lines feed in a sample resume and a list of six required skills, then print the match percentage, the skills you have, and the ones you're missing.
What the output means: match: 67% means the resume mentioned 4 of the 6 required skills; the missing list (Kubernetes, CI/CD) is what you'd add — only if you genuinely have it.
Try this: Replace resume with your own resume text and must with the real must-haves from a job posting. Aim to close the gaps by learning the skill, not by faking the keyword.
3 · Turn course projects into portfolio proof intermediate
A resume makes claims; a portfolio settles them. Every candidate for an AI role writes "built RAG pipelines" on their resume — the words are free, and the interviewer knows it. What separates you from the stack of identical bullets is a link they can click that proves the claim: real code, running, in a repo that looks like an engineer built it and not a student who followed a tutorial once. This is why the portfolio matters more in AI/ML hiring than in almost any other field — the tooling is young, credentials are noisy, and "show me" is the fastest way for a manager to trust you.
Here's the distinction that trips people up: a portfolio is not "the projects you built," it's the projects packaged so a stranger can judge them in three minutes without asking you a single question. The analogy is a restaurant. You can be a brilliant cook, but if a diner walks in to a dark room, no menu, and food that takes an hour with no explanation, they leave. The dish (your code) is necessary but not sufficient — the lighting, the menu, the plating (README, demo, tests, clean history) are what let someone evaluate the dish at all. Most engineers pour 95% of their effort into the code and 5% into the packaging, then wonder why nobody's impressed. The ratio that actually converts is closer to 70/30.
What are the packaging signals a manager scans for? A clear README that answers three questions fast — what problem this solves, why you built it this way, and how to run it in one command — because a manager who can't run it in under five minutes won't try. A live demo or screenshots, because "it works on my machine" is exactly the phrase that ended a thousand projects; a deploy link (even a free-tier one) turns a claim into a fact. And the engineering signals that say "this person ships production code, not homework": tests, a CI badge, and a commit history that reads like incremental work rather than one 4,000-line "final commit." Those last signals are precisely why the DF, TQ, and CD tracks in this course exist — they're not busywork, they're the difference between a repo that reads as amateur and one that reads as hireable.
The common mistake is quantity over legibility: forty half-finished tutorial clones on a GitHub profile actively hurt you, because a manager who sees clutter assumes clutter in your thinking. Three finished, documented, deployed projects beat thirty abandoned ones every time. Your capstones from this course are already at portfolio grade in substance — the work in this section is dragging them up to portfolio grade in presentation, which is the part that's actually seen.
Start at the blue box at the top — that's your project code — then follow the two arrows down to the two ways it can be received.
- The dashed grey arrow leads to the left red "bare repo" box: the same code with no README, no demo, no tests. A manager can't evaluate it in the time they'll give it, so it reads as invisible and is passed over.
- The solid blue arrow leads to the green "packaged" box: the identical code wrapped in a README, a live demo/deploy, tests, CI, and a clean history — the ~70% code / 30% packaging ratio that actually converts.
- The packaged path's arrow lands on the right box, "judged hireable" — the payoff. Note both red and green start from the same code: the difference is entirely presentation, the part that's actually seen.
In short: great code in a bare repo is a dish served in a dark room — the README, demo, and tests are the lighting and menu that let someone judge it at all.
Your capstones here (the doc-intelligence pipeline, RAG platform, tested/deployed app) are portfolio-grade. What makes them count: a clear README (what/why/how-to-run), a live demo or screenshots, and the engineering signals — tests, CI, a clean commit history (all the DF/TQ/CD sections).
| Weak portfolio | Strong portfolio |
|---|---|
| tutorial clones | projects that solve a real problem |
| no README / setup | clear README + one-command run |
| "it works locally" | deployed / demo link + tests + CI |
| one giant commit | readable history + a real PR or two |
4 · Advanced — a portfolio a hiring manager reads advanced
A hiring manager clicks your GitHub, sees 47 public repos, and closes the tab. Not because the work was bad — because they have no idea where to look, and a busy person facing an unsorted pile defaults to "next candidate." This is the failure mode of the eager engineer who thinks more visible work = more impressive. It's the opposite. An unfiltered profile forces the manager to do the curation you should have done for them, and they won't. The skill this section teaches is editing — deciding what the manager sees first, because whatever's on top is your portfolio in practice.
The mechanism GitHub gives you is pinned repositories — up to six slots at the top of your profile that you control. Treat them like the front window of a shop: you don't put your entire inventory in the window, you put the three things that make someone walk in. And the choice of which three isn't random — it should tell a story of range, because a manager isn't just asking "can this person code?" but "what kind of engineer are they, and would they be stuck on my team?" A profile of three near-identical projects answers the first question and fails the second.
The pattern that reads best is depth, breadth, initiative. One project that proves you can go deep — a RAG platform with real retrieval evaluation, not a weekend toy — says "I can own something hard end to end." One that proves breadth — a full app with tests, CI, and a live deploy — says "I understand the whole lifecycle, not just the fun middle part." And one that shows initiative — something you chose to build because it interested you or scratched your own itch — says "I'll bring ideas, not just execute tickets." Together those three sketch a complete engineer; any one alone leaves a doubt the manager has to resolve, and doubts resolve against you.
Each pinned repo then needs its README to lead with impact, the same headline discipline from §1, because the manager reads the README the way they read your resume — top-down, skimming for the payoff. Burying "this cut retrieval latency 60%" in paragraph four is like putting the punchline last in a joke told to someone already reaching for the door. The common mistake here is letting the portfolio grow instead of curating it: every finished project feels worth showing, so the profile bloats over time and the signal drowns. Great portfolios are pruned as deliberately as they're built — the readiness checker below is how you decide which projects have earned a pin.
Managers skim your GitHub like your resume. Pin 3 projects that show range: one that proves depth (the RAG platform), one that proves breadth (an end-to-end app with tests+deploy), one that shows initiative (something you chose). Each with a README that leads with impact.
Read the three coloured boxes left to right — they are the three repos you pin — then follow the arrows down to the single box they combine into.
- The purple "Depth" box is the project that goes hard end-to-end (a RAG platform with real retrieval evaluation, not a weekend toy) — it answers "can this person own something difficult?"
- The blue "Breadth" box is a full app with tests, CI, and a live deploy — it answers "do they understand the whole lifecycle, not just the fun middle part?"
- The green "Initiative" box is something you chose to build — it answers "will they bring ideas, or only execute tickets?"
- The arrows converging on the bottom box are the payoff: the three together read as a complete engineer. That is why range beats three near-identical projects, which answer only the first question.
In short: pin for range, not repetition — depth, breadth, and initiative each close a doubt a manager would otherwise resolve against you.
portfolio_check.pydef portfolio_ready(project):
checks = {
"has_readme": project.get("readme", False),
"has_tests": project.get("tests", False),
"deployed_or_demo": project.get("demo", False),
"clear_commits": project.get("clean_history", False),
}
ready = sum(checks.values())
verdict = "portfolio-ready" if ready >= 3 else "needs work"
return verdict, [k for k, v in checks.items() if not v]
v, gaps = portfolio_ready({"readme": True, "tests": True, "demo": False, "clean_history": True})
print(v, "| improve:", gaps)
portfolio-ready | improve: ['deployed_or_demo']
This rates whether one of your projects is portfolio-ready — good enough to show a hiring manager. It scores four signals employers look for and calls a project ready once it passes at least three of them.
projectis a small dictionary describing one project — a set of yes/no facts likereadme,tests,demo,clean_history.project.get("readme", False)reads one fact, defaulting toFalse("no") if it wasn't listed.- The
checksdictionary gathers the four True/False answers: has a README, has tests, is deployed or has a demo, and has a clean commit history. ready = sum(checks.values())counts how many are True (in Python,Truecounts as 1).verdict = "portfolio-ready" if ready >= 3 else "needs work"gives the pass/fail using that count.- The
returnhands back the verdict plus the list of failed checks ([k for k, v in checks.items() if not v]) so you know exactly what to improve.
What the output means: portfolio-ready | improve: ['deployed_or_demo'] — the project passed 3 of 4 signals, so it's ready, and the one thing that would make it stronger is a live demo.
Try this: Fill the dictionary in with the true state of one of your own repos. If the verdict is "needs work", tackle the items in the improve list one at a time.
5 · Professional — tailor per role professional
You've got one polished resume and you're firing it at fifty postings, and the callback rate is quietly killing your motivation. The instinct feels efficient — write once, apply everywhere — but it's the resume equivalent of a cover letter that starts "Dear Sir or Madam." A generic resume is optimized for no role, which means it's slightly wrong for every role. The recruiter for an MLOps position and the recruiter for an applied-research position are scanning for different top-line signals, and the same fixed document can only lead with one of them.
The professional move is not to write fifty resumes — that's unsustainable and you'd burn out by application ten. It's to keep one master resume: a long, comprehensive document with every bullet, project, and metric you've ever earned, far more than fits on a page. The master is your source of truth; you never send it. For each real application you produce a tailored cut from it — a fifteen-minute edit, not a rewrite. Think of it like a photographer's contact sheet versus the three prints they hang: the master holds everything, and each role gets the selection that makes that role's manager lean in.
Three edits do most of the work. First, reorder so the bullets matching the posting's stated priorities sit at the top of each section — the six-second scan lands there, so that's where your most relevant evidence must be. Second, rewrite the summary line to name the role you're applying for and the one or two things that role most values; a summary that says "AI engineer specializing in production RAG systems" for a RAG role beats a generic "software engineer" every time. Third, mirror the must-have keywords the posting uses (the §2 discipline), so both the ATS and the human see their own language reflected back.
The common objection is "fifteen minutes times forty applications is ten hours" — and the answer is that it's the wrong forty. Tailoring forces you to actually read each posting, which means you self-select out of roles that aren't a fit and pour effort into the ten that are. A smaller number of tailored applications reliably out-converts a flood of generic ones, so the tailoring isn't extra work — it's the filter that stops you wasting effort on roles you were never getting anyway.
One generic resume underperforms. Keep a master resume, then tailor a version per role: reorder bullets to match the JD's priorities, adjust the summary, mirror the must-have keywords. 15 minutes per application dramatically lifts callback rates.
Read left to right: the single box on the left is the one document you maintain; the three boxes on the right are what you actually send, one per application.
- The white box on the left is the master resume — a long, comprehensive record of every bullet, project, and metric you've earned. It's deliberately too long for one page, and you never send it.
- The label over the arrows names the three edits that produce each cut: reorder bullets to the posting's priorities, rewrite the summary line, and mirror the must-have keywords (the §2 discipline).
- The three coloured boxes on the right are tailored cuts for different role types — each foregrounds the facts that role scores highest. The note beneath is the rule: it's the same true work, just a different selection and emphasis — never invented facts.
In short: write once as a master, then spend fifteen minutes selecting from it per role — fewer, sharper applications out-convert a flood of generic ones.
6 · Tech-lead — your narrative & brand tech-lead
Here's what surprises engineers the first time they interview for a senior or lead role: the skills-checklist stops working. At junior levels, hiring is largely a competence question — can you code, do you know the stack, can you pass the coding screen. You optimize your resume by adding — more skills, more projects, more keywords. Then you reach the senior tier and the same additive strategy stalls, because the question quietly changed. Nobody doubts you can code by now. What they're trying to figure out is: what are you known for, what's your judgment like, and what happens to a team and a system when you're in the room?
That question can't be answered by a longer skills list — it's answered by a narrative. A narrative is a coherent through-line: "I'm the person who makes flaky ML systems reliable," or "I take research prototypes and get them to production scale." It's not a title and it's not a technology; it's the pattern a manager can summarize in one sentence after meeting you. The analogy is the difference between an actor's résumé and their reputation. A résumé lists roles; a reputation is "the actor you cast when you need X." Senior hiring runs on reputation, and your public footprint is how a stranger perceives that reputation before you've said a word.
This is where the additive instinct actively hurts you. A senior engineer with 47 unfocused repos and a skills section listing every framework they've touched reads as unfocused — the opposite of the clear point of view a lead is supposed to have. The move is subtraction and depth: a focused GitHub that circles one or two themes, a handful of writeups (a blog post, a README deep-dive, a conference talk, a meaningful OSS contribution) that show your thinking, not just your output. One well-argued post about a hard tradeoff you navigated does more for a lead-level candidacy than ten more repos, because it demonstrates the thing the role is actually hiring for: judgment you can articulate.
The common trap at this level is confusing activity with brand — posting constantly, chasing followers, collecting stars. Brand isn't reach; it's legibility of judgment. The senior candidates who stand out aren't the loudest, they're the ones whose small public body of work makes a hiring manager think "this person has a considered point of view about how systems should be built." Everything below — from the way you frame decisions to the artifacts you choose to make public — is in service of making that judgment visible to someone who has thirty minutes and a job to fill.
At senior/lead level, hiring is about a narrative: what you're known for and where you're going. Beyond the resume — a focused GitHub, a few writeups (blog/README deep-dives), talks or OSS contributions. You're not listing skills; you're demonstrating judgment and impact at scale.
The dashed line down the middle splits two strategies: the junior "add more" instinct on the left, the senior "subtract to a theme" move on the right.
- The left cluster of amber chips is the additive strategy — Python, Docker, FastAPI, AWS, RAG, 47 repos. More is piled on, but with no through-line it reads as unfocused, the opposite of what a lead is meant to project.
- The right green banner is a single narrative theme ("makes ML systems reliable") — the one sentence a manager can repeat after meeting you.
- The three white boxes beneath it are the few deep artifacts that back the theme — a blog post, one deep repo, an OSS contribution or talk — that show your thinking. Together they read as clear judgment, which is the thing senior hiring is actually buying.
In short: below senior, you optimize by adding; at senior, you optimize by subtracting to one legible theme plus a few artifacts that prove your judgment.
Exercise CR1.1 — Make your materials pass
Context: The chapter's three tools only help if you run them on your own materials and act on the scores — rewriting bullets, closing real gaps, and getting projects to a hiring bar.
Your task: Run the bullet scorer over every line of your resume and rewrite any scoring below 3; run the ATS matcher against a real posting's must-haves and close true gaps; pick 3 course projects, run the portfolio check, and get each to portfolio-ready.
Requirements:
- Rewrite each sub-3 bullet with a strong verb, a number, and a "by..." clause
- Close ATS gaps only with keywords that are genuinely true (defensible in interview)
- Get each of three projects to at least 3 of 4 signals: README, tests, demo/deploy, clean history
- Iterate — re-run each tool after editing
- Non-code: the deliverable is improved materials, not a program
💡 Hint: Treat the scores as a to-do list, not a grade — the value is in the rewrite each low score forces.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: A resume gets a six-second scan, and "Responsible for" lines say nothing about impact. The impact-bullet shape — action verb + what + measurable result — is what survives that scan.
Your task: Rewrite this weak resume line into an impact bullet using the action verb + what + measurable result shape: "Responsible for building a chatbot for the support team."
Requirements:
- Open with a strong action verb, not "Responsible for"
- Name the concrete artifact and the tech stack that built it
- Include a measurable result (a percentage, a before/after, a time)
- Quantify scope where it adds credibility (e.g. "across 12k monthly tickets")
- Never invent metrics — use only numbers you could defend
💡 Hint: The formula is verb → what → number; if a line has no number, it's still a duty statement, not an impact bullet.
Show solution
Weak: starts with “Responsible for”, names a duty, has no result.
Rewritten:
Built a retrieval-augmented support chatbot (Python, FastAPI, Claude API) that deflected 38% of tier-1 tickets, cutting mean first-response time from 6h to under 2m.
Why it works: strong verb (Built) → concrete artifact + stack → a measurable business result (38% deflection, 6h→2m). If you lack a real metric, quantify scope instead: “… across 12k monthly tickets.” Never invent numbers you can’t defend in an interview.
Context: Before a human reads your resume, an ATS matches it literally against the posting's phrases — but human readers discount keyword lists. A good bullet satisfies both by using the keywords in context.
Your task: You're applying to a role whose posting stresses "RAG pipelines, vector databases, evaluation". Re-target your generic bullet so an ATS keyword scan and a 6-second human scan both succeed — without keyword stuffing.
Requirements:
- Map your real work to the posting's exact keyword phrases
- Place each keyword in concrete context, not a bare Skills list
- Show the pipeline/result so a human reads substance, not stuffing
- Include a measurable outcome (e.g. a regression caught before release)
- Only claim keywords that are genuinely true of your work
💡 Hint: Weave the posting's phrases into a sentence that also carries a result — the ATS sees the words, the human sees the work.
Show solution
Generic: “Worked on an AI search feature.”
ATS-aligned:
Designed a RAG pipeline (chunking → embeddings → vector database retrieval → Claude synthesis) and built an offline evaluation harness (faithfulness + citation metrics) that caught a 9% regression before release.
The three posting keywords appear in context, mapped to real work — that beats a “Skills: RAG, vector DB, eval” list an ATS can read but a human discounts. Use the exact noun phrases from the posting; ATS matching is literal.
Context: A hiring manager spends about 90 seconds on a project. The top of the README has to convey the problem, your role, the result, and how to run it inside that window.
Your task: Draft the top-of-README block for a course project so a hiring manager grasps the problem, your role, the result, and how to run it in ~90 seconds.
Requirements:
- Lead with a one-line pitch and why the problem matters
- State what you built, honestly bounding any shared/borrowed work
- Give quantified results (deflection %, eval results, latency)
- Provide a one-command way to run it (e.g.
docker compose up) - Name the tech stack and link an architecture reference
- Order it problem → your role → result → how to run
💡 Hint: Explicitly naming what was shared reads as senior, not diminishing — it signals you know the difference between your work and the team's.
Show solution
# Support Triage Agent
**One line:** classifies incoming support tickets and drafts grounded replies,
escalating low-confidence cases to a human.
**Why it matters:** tier-1 volume was drowning a 4-person team; this deflects
routine questions and routes the rest.
**What I built:** the classifier + confidence gate + RAG answer path + the eval
harness. (Retrieval infra was a shared library.)
**Results:** 38% deflection on a 500-ticket eval set; 0 hallucinated policy claims
(citations enforced); p95 latency 2.1s.
**Run it:** `docker compose up` then open localhost:8000 — seeded demo data included.
**Stack:** Python, FastAPI, Claude API, pgvector. **Architecture:** see diagram below.
The order matters: problem → your role → result → how to run. “What I built” is scoped honestly (you name what was shared), which reads as senior, not as diminishing your work.
Context: Before you publish, grade your flagship project on the dimensions a hiring manager actually weighs — because the weakest dimension is exactly where an interviewer will probe.
Your task: Fill a rubric for one project across the dimensions that matter and identify the single weakest dimension to fix first.
Requirements:
- Score dimensions like problem framing, runs-in-under-5-minutes, evaluation, scope honesty, and failure handling
- Grade each 0 (missing) / 1 (meets bar) / 2 (above bar)
- Meets-bar examples: README states the problem, setup works, some tests exist, happy path runs
- Above-bar examples: named tradeoffs, one-command demo with no secrets, an offline eval catching regressions, failure/bad-input handling
- Fix the lowest-scoring dimension first (often evaluation or failure handling)
💡 Hint: Evaluation and failure handling are where most projects only show the happy path — and that's precisely where interviewers push.
Show solution
| Dimension | Meets the bar | Above the bar |
|---|---|---|
| Problem framing | README states problem + who it’s for | Names the tradeoff and why this approach |
| Runs in <5 min | Clear setup, seeded data | One command (docker compose up), no secrets needed for demo |
| Evaluation | Some tests exist | Offline eval set + a metric that would catch a regression |
| Scope honesty | Says what you built | Distinguishes your work from shared/borrowed code |
| Failure handling | Happy path works | Shows what happens on bad input / model error |
Score each 0 (missing) / 1 (meets) / 2 (above). Fix the lowest first — usually Evaluation or Failure handling, since most course projects only show the happy path, and that is exactly where an interviewer probes.
Context: One project can be told two ways. Tailoring the same work to a product-focused role and an infrastructure-focused role — by selection and emphasis, never fabrication — is a professional skill.
Your task: Same project, two postings: (A) a product-focused "AI Engineer" role and (B) an infrastructure-focused "ML Platform" role. Write the same project as two bullets, each emphasising what that role cares about.
Requirements:
- Version A emphasises user outcome and quality (deflection, citation enforcement, hallucinations held at zero)
- Version B emphasises systems, reliability and cost (async serving, cache reuse cutting spend, p95 latency under load)
- Both describe the same project truthfully
- The difference is selection and emphasis, not new facts
- Each bullet still carries a measurable result
💡 Hint: One truth, two lenses — you're choosing which real facts to foreground, never inventing role-specific ones.
Show solution
Shared project: the support triage agent.
Bullet for (A) AI Engineer — emphasize user outcome + quality:
Shipped a support-triage agent that deflected 38% of tickets; designed the confidence gate and citation enforcement that kept hallucinated policy claims at zero on a 500-case eval set.
Bullet for (B) ML Platform — emphasize systems + reliability + cost:
Built the serving path for a support-triage agent: async FastAPI, prompt-cache reuse cutting token spend ~45%, and p95 latency held at 2.1s under load with a rate-limit backoff + retry layer.
Notice: one truth, two lenses. You are not inventing new facts — you are foregrounding the facet each reader scores. Tailoring is selection and emphasis, never fabrication.
Context: Senior candidates are remembered by a narrative, not a skills list. A 2–3 sentence positioning statement gives interviewers a hook and steers the conversation to your strongest ground.
Your task: Craft a 2–3 sentence positioning statement (your "tell me about yourself" and LinkedIn headline) with a theme, evidence, and a direction — then critique a weak version.
Requirements:
- Name a clear theme (what you're known for), not a tech list
- Back it with one concrete piece of evidence (a specific result)
- State a direction (what you want to own next)
- Critique a weak version that is just a skills list with no theme or evidence
- Keep it to 2–3 sentences and memorable
💡 Hint: Theme → evidence → direction: the skills-list version is forgettable precisely because it has none of the three.
Show solution
Weak (a list): “I’m a Python developer with experience in AI, RAG, FastAPI, Docker, and AWS looking for a new opportunity.” — no theme, no evidence, forgettable.
Strong (a narrative):
I build LLM systems that teams can actually trust in production — my work centers on the un-sexy parts: evaluation, grounding, and graceful failure. Most recently I built a support-triage agent whose citation-enforcement and confidence gate held hallucinated claims at zero on a 500-case eval. I’m looking to own the reliability side of a customer-facing AI product.
The pattern is theme (trustworthy LLM systems) → evidence (the eval result) → direction (what you want next). It gives the interviewer a hook and tells them what to ask about — you steer the conversation onto your strongest ground.
✓ Checkpoint — you can move on when you can…
- Write impact bullets that pass the 6-second scan.
- Match a resume to a JD without keyword stuffing.
- Turn course projects into portfolio proof.
- Curate a portfolio and craft a senior narrative.
Knowledge check check yourself
What three elements make a strong impact bullet, versus a duty statement like "Responsible for testing"?
Show answer
Why should you mirror a job description's keywords on your resume, and what's the rule that keeps this honest?