Resume, Portfolio & GitHub Profile Module 3 – Writing content that proves your work
Resume bullet points that show impact
How to write resume bullets that show impact: what changed, by how much and how, results instead of duties, precise verbs, and how many bullets each job gets.
What you will learn
- Write bullets that follow the accomplished X, measured by Y, by doing Z pattern
- Rewrite duty statements as results, with the method you used
- Choose precise action verbs and keep tense, voice and pronouns consistent
- Limit each bullet to two lines and each role to three to five bullets
- Order each role's bullets by their relevance to the posting
Before you start
What you’ll learn · Before you start
What you will learn
- Write bullets that follow the accomplished X, measured by Y, by doing Z pattern
- Rewrite duty statements as results, with the method you used
- Choose precise action verbs and keep tense, voice and pronouns consistent
- Limit each bullet to two lines and each role to three to five bullets
- Order each role's bullets by their relevance to the posting
Before you start
On this page
“Worked on caching” tells a reader that a cache existed. “Cut p95 API latency from 420 ms to 180 ms by adding PostgreSQL indexes and a Redis cache for route lookups” tells them what changed, by how much and how, in 21 words. Google’s hiring page asks for that shape in a formula: “accomplished [X] as measured by [Y], by doing [Z]”.
This lesson builds bullets of that kind. You will take one apart, turn duties into results, choose verbs that say what you did, and decide how many bullets each job gets and which goes first. A checker that runs in your browser tests twenty bullets against five rules, and a lab has you write a check for invented numbers and rewrite an intern’s eight bullets from his records.
A strong bullet says what changed, by how much, and how
Asha, the invented backend engineer this track follows, wrote “Worked on caching” under her current job. It is true, and it says nothing a reader can use: no change, no size, no sign of which part was hers. Three official guides ask for the same repair in different words. Google’s formula wants what you accomplished, how it was measured and what you did. MIT’s careers office describes PAR statements: a strong action verb, the project, then the result, answering “What did you do?”, “Why did you do it?” and “What was the outcome?”. The UK’s National Careers Service suggests 2 to 3 lines per job using the STAR method: A way to describe one example of your work in four parts: the situation, the task, the action you took and the result. The UK's National Careers Service suggests it for CVs, application forms and interviews.: situation, task, action and result.
Each guide ends in a result you can point to. Step through Asha’s draft becoming a bullet, one part at a time:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The draft. Asha's first line for her current job reads 'Worked on caching'. It tells a reader that a cache existed. It names no change, no number and no part of the work that was hers.
- A verb first. Ask what she did to the system. She made something faster, so the bullet starts with Cut: an action verb that already promises a before and an after.
- What changed. Name the thing that changed, the X of the formula: the p95 latency of the API, the time within which 95 of every 100 requests were answered.
- By how much. Give the measure, the Y: from 420 ms to 180 ms. A number with its baseline and its unit lets a reader judge the size of the change, and an interviewer ask how it was measured.
- How. Say how she did it, the Z: by adding PostgreSQL indexes and a Redis cache. The method is what an interviewer asks about first, and it puts the skills inside the evidence.
- Where, if it helps. Add the scope only when it adds something. For route lookups tells the reader which part of the system got faster; the size of a team or a volume of work can go here too.
- The bullet. Read top to bottom, the parts make one line of 21 words: Cut p95 API latency from 420 ms to 180 ms by adding PostgreSQL indexes and a Redis cache for route lookups.
The parts land in a steady order: the verb first, then what changed and by how much, then the method. The order can bend. When a posting asks for a technology, “by adding a Redis cache” may come before the numbers. What cannot go is the change with its measure, because a bullet without them is a duty again. Google also asks about scope, such as how big a team was or what the work covered; add it when it tells the reader something, as “for route lookups” does here.
Key idea
A bullet earns its line when it says what changed, by how much and how you did it. The verb, the measure and the method are the parts a reader scans for and an interviewer asks about.
Checkpoint
Check: What a strong bullet holds
Duties describe the job; results describe what you did in it
Canada’s Job Bank puts the problem plainly: a job title already tells an employer what your duties were, so a resume should highlight achievements instead. It also warns against vague statements, quoting “Responsible for improving efficiencies and making cost savings” as a line that gives an employer no information. MIT’s careers office says the same of experience descriptions: they should not read like job descriptions.
A duty becomes a result when you answer three questions about it, which is MySmartCoPilot’s method: what was different after your work, how do you know, and what did you do to make it so? The answers are the X, the Y and the Z. Four rewrites from invented resumes show the questions at work:
| Before: a duty | After: a result | What the rewrite added |
|---|---|---|
| Responsible for the hostel mess feedback form. | Designed a two-question mess feedback form that collected 1,860 responses in one term; the committee changed three dishes. | What changed, and its size |
| We made the fest website. | Built the registration and payment pages of the college fest website, used by 1,140 students in its first week. | Your own part |
| Was tasked with writing unit tests. | Wrote 140 unit tests for the billing module, raising its line coverage from 48 % to 81 %. | A baseline for the number |
| Contributed to open source. | Fixed a date-parsing bug in an open-source CSV library; the maintainers merged the pull request. | An outcome someone can check |
The program below holds ten weak bullets of this kind and their rewrites. It checks each one against five rules of
form: an action verb first, no personal pronoun, no duty phrase, a measure or a named outcome, and about 200
characters at most, which is two printed lines. Press Edit, put your own bullets in MINE, and run it again:
# Checks resume bullets against five rules of form (MySmartCoPilot's checker; the rules follow the guides this lesson
# cites): it starts with an action verb, it has no personal pronoun, it has no duty phrase, it has a measure or a named
# outcome, and it fits in two printed lines (about 200 characters). It runs on ten weak bullets from invented resumes
# and their rewrites, then on one rewrite with a false number. Press Edit, add your own bullets to MINE, and run it.
import re
VERBS = {
"added", "automated", "built", "cut", "designed", "fixed", "led", "migrated", "moved", "ran",
"raised", "reduced", "replaced", "tested", "trained", "wrote",
}
PRONOUNS = {"i", "me", "my", "we", "our", "us"}
DUTY_PHRASES = ("responsible for", "worked on", "helped with", "involved in", "tasked with", "duties included")
OUTCOME_WORDS = ("merged", "adopted", "published", "won", "replaced")
MAX_CHARACTERS = 200
PAIRS = [
("Canteen queue app", "a verb and what changed",
"Worked on the canteen queue app.",
"Built a token queue for the college canteen in Flask and SQLite, cutting the average lunch-hour wait "
"from 14 to 6 minutes."),
("Hostel mess feedback", "a result, not a duty",
"Responsible for the hostel mess feedback form.",
"Designed a two-question mess feedback form that collected 1,860 responses in one term; the committee "
"changed three dishes."),
("College fest website", "your part, not the team's",
"We made the fest website.",
"Built the registration and payment pages of the college fest website, used by 1,140 students in its "
"first week."),
("Route lookups at Example Logistics", "a measure and the method",
"Helped with performance improvements on the API.",
"Cut p95 API latency from 420 ms to 180 ms by adding PostgreSQL indexes and a Redis cache for route lookups."),
("Shipment events at Example Logistics", "the scope",
"Involved in building a Kafka consumer.",
"Built a Kafka consumer for 2 million shipment events a day, with idempotent writes and Grafana alerts."),
("Billing tests in an internship", "a baseline for the number",
"Was tasked with writing unit tests.",
"Wrote 140 unit tests for the billing module, raising its line coverage from 48 % to 81 %."),
("Invoice export", "no pronouns, and an honest estimate",
"I automated some reports for the finance team.",
"Automated the weekly invoice export with a Python script, saving the finance team about 3 hours a week."),
("Library seat booking", "a result in place of upkeep",
"Duties included maintaining the library seat booking site.",
"Added e-mail reminders to the library seat booking site, reducing no-shows from 31 % to 12 % in one "
"semester."),
("Python workshops", "what changed for other people",
"Helped juniors learn Python.",
"Ran six weekend Python workshops for 45 first-year students; 38 of them finished every exercise."),
("Open-source fix", "a named outcome, when there is no number",
"Contributed to an open-source CSV library over the summer, where I read the code base, joined the "
"discussions on several issues, worked on a bug about dates and learned a lot about testing and code review.",
"Fixed a date-parsing bug in an open-source CSV library; the maintainers merged the pull request."),
]
FALSE_NUMBER = "Cut p95 API latency from 420 ms to 18 ms by adding PostgreSQL indexes and a Redis cache for route lookups."
MINE = [] # your own bullets, one string each
def problems(bullet):
"""The rules a bullet breaks, in words (an empty list when it passes all five)."""
found = []
words = re.findall(r"[A-Za-z][A-Za-z'-]*", bullet)
first = words[0].lower() if words else ""
if first not in VERBS:
found.append(f'starts with "{words[0] if words else bullet}", not an action verb')
pronouns = sorted({w for w in words if w.lower() in PRONOUNS})
if pronouns:
found.append("a pronoun: " + ", ".join(pronouns))
lower = bullet.lower()
for phrase in DUTY_PHRASES:
if phrase in lower:
found.append(f'a duty phrase: "{phrase}"')
if not re.search(r"\d", bullet) and not any(w in lower for w in OUTCOME_WORDS):
found.append("no measure and no named outcome")
if len(bullet) > MAX_CHARACTERS:
found.append(f"{len(bullet)} characters, more than two lines")
return found
def verdict(bullet):
found = problems(bullet)
if not found:
return f"passes ({len(bullet)} characters)"
return f"breaks {len(found)}: " + "; ".join(found)
failed_before = passed_after = broken = 0
for n, (where, rule, before, after) in enumerate(PAIRS, start=1):
print(f"{n:>2}. {where} (rule: {rule})")
print(f" before {before}")
print(f" {verdict(before)}")
print(f" after {after}")
print(f" {verdict(after)}")
broken += len(problems(before))
failed_before += bool(problems(before))
passed_after += not problems(after)
print()
print(f"Before: {failed_before} of {len(PAIRS)} bullets break at least one rule, {broken} problems in all.")
print(f"After: {passed_after} of {len(PAIRS)} bullets pass all five rules.")
print()
print("One rewrite with a false number:")
print(f" {FALSE_NUMBER}")
print(f" {verdict(FALSE_NUMBER)}: the form is right, and no rule can tell that 18 ms never happened.")
for bullet in MINE:
print()
print(f" yours {bullet}")
print(f" {verdict(bullet)}") Output
1. Canteen queue app (rule: a verb and what changed)
before Worked on the canteen queue app.
breaks 3: starts with "Worked", not an action verb; a duty phrase: "worked on"; no measure and no named outcome
after Built a token queue for the college canteen in Flask and SQLite, cutting the average lunch-hour wait from 14 to 6 minutes.
passes (122 characters)
2. Hostel mess feedback (rule: a result, not a duty)
before Responsible for the hostel mess feedback form.
breaks 3: starts with "Responsible", not an action verb; a duty phrase: "responsible for"; no measure and no named outcome
after Designed a two-question mess feedback form that collected 1,860 responses in one term; the committee changed three dishes.
passes (122 characters)
3. College fest website (rule: your part, not the team's)
before We made the fest website.
breaks 3: starts with "We", not an action verb; a pronoun: We; no measure and no named outcome
after Built the registration and payment pages of the college fest website, used by 1,140 students in its first week.
passes (111 characters)
4. Route lookups at Example Logistics (rule: a measure and the method)
before Helped with performance improvements on the API.
breaks 3: starts with "Helped", not an action verb; a duty phrase: "helped with"; no measure and no named outcome
after Cut p95 API latency from 420 ms to 180 ms by adding PostgreSQL indexes and a Redis cache for route lookups.
passes (107 characters)
5. Shipment events at Example Logistics (rule: the scope)
before Involved in building a Kafka consumer.
breaks 3: starts with "Involved", not an action verb; a duty phrase: "involved in"; no measure and no named outcome
after Built a Kafka consumer for 2 million shipment events a day, with idempotent writes and Grafana alerts.
passes (102 characters)
6. Billing tests in an internship (rule: a baseline for the number)
before Was tasked with writing unit tests.
breaks 3: starts with "Was", not an action verb; a duty phrase: "tasked with"; no measure and no named outcome
after Wrote 140 unit tests for the billing module, raising its line coverage from 48 % to 81 %.
passes (89 characters)
7. Invoice export (rule: no pronouns, and an honest estimate)
before I automated some reports for the finance team.
breaks 3: starts with "I", not an action verb; a pronoun: I; no measure and no named outcome
after Automated the weekly invoice export with a Python script, saving the finance team about 3 hours a week.
passes (103 characters)
8. Library seat booking (rule: a result in place of upkeep)
before Duties included maintaining the library seat booking site.
breaks 3: starts with "Duties", not an action verb; a duty phrase: "duties included"; no measure and no named outcome
after Added e-mail reminders to the library seat booking site, reducing no-shows from 31 % to 12 % in one semester.
passes (109 characters)
9. Python workshops (rule: what changed for other people)
before Helped juniors learn Python.
breaks 2: starts with "Helped", not an action verb; no measure and no named outcome
after Ran six weekend Python workshops for 45 first-year students; 38 of them finished every exercise.
passes (96 characters)
10. Open-source fix (rule: a named outcome, when there is no number)
before Contributed to an open-source CSV library over the summer, where I read the code base, joined the discussions on several issues, worked on a bug about dates and learned a lot about testing and code review.
breaks 5: starts with "Contributed", not an action verb; a pronoun: I; a duty phrase: "worked on"; no measure and no named outcome; 205 characters, more than two lines
Text longer than 4,000 characters is cut here; run the example yourself to see all of it.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bullet_check.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
All ten weak bullets break at least one rule, 31 problems in all, and all ten rewrites pass. Two rewrites show what to do without a hard number: the open-source fix names an outcome a reader can check, a merged pull request, and the invoice export gives an honest estimate, about 3 hours a week. The last line is the warning. Asha’s latency bullet with 18 ms in place of 180 ms passes too, because a checker reads the form of a bullet, never its truth. Every number has to come from your own records, and the next lesson is about finding them.
Starting with a phrase about your job
Wrong: “Responsible for the release pipeline.” Right: “Cut release time from 3 hours to 25 minutes by scripting the release checklist.” The first describes what the job asked of you; the second says what you made happen, and an interviewer can ask how.
Checkpoint
Check: Results instead of duties
Precise verbs, one tense and no pronouns
“Worked on”, “helped with” and “involved in” have one thing in common: they hide what you did. The official lists of action verbs point the other way. Job Bank suggests handled, managed, led, developed, increased, accomplished and leveraged; Europass recommends strong verbs such as managed, developed and increased; MIT suggests built, managed, developed and wrote; and Harvard’s guide sorts its verbs by kind of work, from technical to teaching. The best verb is the one that names your work exactly:
| Instead of | It hides | Say what you did |
|---|---|---|
| Worked on | what you did | Built, migrated, tested, profiled |
| Helped with | how big your part was | Wrote, reviewed, fixed, ran |
| Responsible for | whether anything happened | Cut, raised, launched, automated |
| Involved in | whether you acted at all | Designed, led, presented |
Three habits keep a list of bullets readable. Pronouns: Job Bank says not to use “I”, “my” or “me”, MIT says to avoid the first person, and Harvard adds “We”. Tense: use the past tense for finished work, including results you have already reached in your current job, and the present tense only for what you still do: “Run the release checklist for 14 services” next to “Moved 14 services to Kubernetes”. That convention is MySmartCoPilot guidance; whichever you choose, use it all the way down. Voice: Harvard asks for active rather than passive language, because “The cache was added” hides who added it.
Dropping “We” is where the real work starts. “We built the fest website” credits nobody; “Built the registration and payment pages of the college fest website” names your part. Google asks about leadership in the same spirit: how big was the team, and what was the scope of your work? If you led the four people who built it, say “Led a team of four”.
Passive Voice & Writing Style Checker Paste your bullets to find passive sentences that hide who did the work.Checkpoint
Check: Verbs, tense, pronouns and your own part
Short bullets, a few per role, the strongest first
The guides agree on short. MIT advises bullet points of 1 to 2 lines each, the National Careers Service 2 to 3 lines per job, and Job Bank at most 5 to 7 bullet points in any section of a resume. For a tech resume, MySmartCoPilot’s default is three to five bullets for a recent, relevant role, fewer for older or less relevant ones, and at most two printed lines per bullet, about the 200 characters the checker allows.
Order matters as much as length. Within a role, put bullets in order of relevance to the posting, not in the order the work happened, because the first bullet under each job is the one most likely to be read; a first screen, as the first lesson of this track worked out, reaches only the top of page one. The posting’s signals decide what relevant means: its must-have terms and its stress on scale, ownership or on-call work.
The habit that makes this fast is a bullet bank: A private list of every true bullet you could write for a job or a project, each with the record that proves it, from which you choose the few that fit one posting.: every true bullet you could write for a job, each with the record that proves it, kept in one private file. For each posting you choose and order from the bank instead of writing from scratch. Step through Asha choosing four bullets for one posting:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The bank. Asha's bullet bank holds nine true lines for her current job, each with the record that proves it: a Kafka consumer, a latency cut, a move to Kubernetes, on-call incidents, an invoice export, a Python upgrade, a test suite, a tech talk and a setup guide.
- Match the posting. She reads them against the Example Freight posting. Five answer its terms or signals: Kafka at scale, PostgreSQL and the API, Docker and Kubernetes, on-call work, and Python in the invoice export.
- Order by relevance. The posting stresses scale and on-call, so the Kafka consumer leads: 2 million shipment events a day answers the first signal and its alerts the second. The latency cut, with PostgreSQL and the API, comes next.
- Keep three to five. Four fit her one page: the consumer, the latency cut, Kubernetes and the 23 incidents. The invoice export is true but the weakest match, so it stays out this time.
- The rest stay banked. Nothing is thrown away. The five lines left out wait in the bank, and a posting for a data platform would pick a different four, with the invoice export moving up.
- Two lines at most. Each kept bullet fits two printed lines, about 200 characters. The longest of the four, the on-call bullet, has 116 characters, so none of the four needs a third line.
Nothing she left out was deleted: the five lines that waited in the bank are the ones a different posting will pick, and each still has the record that proves it.
Checkpoint
Check: How many bullets, and in what order
Practise: honest bullets from a facts sheet
The lab has two steps. First write the check the form checker lacked, the one that finds numbers your records do not hold. Then rewrite the eight bullets of Kabir, an invented intern, from his facts sheet, so that every bullet passes that check and the five rules of form:
Code lab · 2 steps
Honest bullets from a facts sheet
Write a check that finds the numbers a facts sheet does not hold, then rewrite an intern's eight bullets so that they pass it and the five rules of form.
- Find the numbers your records do not hold To do
- Rewrite an intern's eight bullets from the facts To do
Exercise · Easy · Python
Find the numbers your records do not hold
A checker can test the form of a bullet but not its truth, except in one way: it can check that every number in the bullet comes from your records. Write unsupported(bullet, facts) in facts_check.py. facts is a list of lines from a facts sheet; the function returns the numbers of bullet that appear in none of those lines, each written as in the bullet, once, in the order they first appear.
- A number is a run of digits that may have commas between groups of three and one decimal point:
81,1,860,3.5. A full stop after it ends a sentence and is not part of it. - A number joined to letters, as in
p95,Python3or4th, is part of a name, not a measure: ignore it. - Commas do not change a number, so
1,860in a bullet is supported by1860in the facts, and the other way round.
The sample tests use bullets and facts from this lesson, a false number like the one the checker could not catch, and the edge cases above.
Starter code · facts_check.py
import re
def unsupported(bullet, facts):
"""The numbers of bullet that no line of facts holds, each once, in the order they first appear."""
# Replace this comment and the line below with the rules of the prompt.
return [] The sample tests · test_facts_check.py
from facts_check import unsupported
ASHA = [
"p95 latency of the route-lookup API: 420 ms before, 180 ms after the PostgreSQL indexes and the Redis cache",
"Kafka consumer: 2 million shipment events a day",
]
MESS = ["Feedback form: 1860 responses in one term; the committee changed 3 dishes"]
def test_every_number_from_the_facts():
"""finds nothing to report when every number is in the facts"""
bullet = "Cut p95 API latency from 420 ms to 180 ms by adding PostgreSQL indexes and a Redis cache for route lookups."
assert unsupported(bullet, ASHA) == []
def test_a_false_number():
"""reports the 18 ms that no record holds, the number the form checker passed"""
bullet = "Cut p95 API latency from 420 ms to 18 ms by adding PostgreSQL indexes and a Redis cache for route lookups."
assert unsupported(bullet, ASHA) == ["18"]
def test_commas_do_not_change_a_number():
"""matches 1,860 in the bullet with 1860 in the facts, and reports an invented 1,900"""
assert unsupported("Collected 1,860 responses in one term.", MESS) == []
assert unsupported("Collected 1,900 responses in one term.", MESS) == ["1,900"]
def test_names_are_not_numbers():
"""ignores p95, Python3 and 4th, which are parts of names"""
assert unsupported("Ported the p95 report to Python3 for the 4th team.", MESS) == []
def test_decimals_and_full_stops():
"""keeps the decimal point inside a number and the full stop outside it"""
facts = ["Saved about 3.5 hours a week; coverage rose to 81"]
assert unsupported("Saved about 3.5 hours a week, raising coverage to 81.", facts) == []
assert unsupported("Saved about 3.6 hours a week.", facts) == ["3.6"]
def test_each_number_once_in_order():
"""lists each unsupported number once, in the order it first appears"""
bullet = "Cut the wait from 15 to 5, then to 5 again, for 300 students."
assert unsupported(bullet, MESS) == ["15", "5", "300"] A hint
Write one regular expression for a number and use it twice: once on every line of facts, to build a set of the numbers they hold (with the commas removed), and once on the bullet. A lookbehind such as (?<![\w.,]) and a lookahead such as (?!\w) keep p95 and 4th out.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Exercise · Easy · Python
Rewrite an intern's eight bullets from the facts
Kabir, an invented intern, spent ten weeks in the IT team of an invented pharmacy chain. His draft resume has eight weak bullets, in BULLETS in bullets.py, and his facts sheet records what really happened, one line per bullet:
- A Python script checks expiry dates in the stock database every night; it flagged 312 expired items in its first month. Before, staff checked the shelves by hand.
- The stock report moved from a spreadsheet to a Flask page; it loads in 2 s instead of 40 s.
- 64 new unit tests for the billing module; its line coverage rose from 35 % to 72 %.
- 17 bugs from the team's tracker fixed in 10 weeks.
- Barcode scanning on the receiving screen: receiving a delivery of 200 items took about 50 minutes before and about 20 after, timed on three deliveries.
- A setup guide for new interns: the next 2 interns set the project up in 1 day instead of 3.
- A talk on the expiry check for 25 pharmacists and store staff at the monthly meeting.
- Hand-sent SMS reminders for prescription refills replaced by a scheduled job that sends about 150 a day.
Rewrite the eight strings so that bullet n tells the story of fact n. Each bullet must:
- start with an action verb in the past tense: a word ending in "ed", or one of the irregular verbs in the tests'
IRREGULARlist, but none of the weak openers inWEAK; - hold no personal pronoun (I, me, my, we, our, us) and no duty phrase such as "responsible for" or "worked on";
- hold at least one number, and only numbers that its fact holds: a number the facts do not hold fails, as in the check of the lab's first step;
- name its own subject (the expiry check, the report, the tests, the bugs, the barcodes, the guide, the talk, the reminders), and fit in 200 characters.
The sample tests check all eight bullets against these rules.
Starter code · bullets.py
# Rewrite each bullet from the fact with the same number in the prompt.
BULLETS = [
"Responsible for checking expiry dates.",
"Worked on the stock report.",
"Helped with testing the billing module.",
"Fixed bugs.",
"I added barcode scanning.",
"Wrote documentation for interns.",
"Gave a presentation.",
"Involved in the SMS reminder system.",
] The sample tests · test_bullets.py
import re
from bullets import BULLETS
FACTS = [
"A Python script checks expiry dates in the stock database every night; it flagged 312 expired items in its first "
"month. Before, staff checked the shelves by hand.",
"The stock report moved from a spreadsheet to a Flask page; it loads in 2 s instead of 40 s.",
"64 new unit tests for the billing module; its line coverage rose from 35 % to 72 %.",
"17 bugs from the team's tracker fixed in 10 weeks.",
"Barcode scanning on the receiving screen: receiving a delivery of 200 items took about 50 minutes before and about "
"20 after, timed on three deliveries.",
"A setup guide for new interns: the next 2 interns set the project up in 1 day instead of 3.",
"A talk on the expiry check for 25 pharmacists and store staff at the monthly meeting.",
"Hand-sent SMS reminders for prescription refills replaced by a scheduled job that sends about 150 a day.",
]
SUBJECTS = [("expir",), ("report",), ("test",), ("bug",), ("barcode",), ("guide",), ("talk", "present"), ("remind",)]
IRREGULAR = {"built", "cut", "drew", "gave", "led", "made", "ran", "rewrote", "set", "spoke", "taught", "won", "wrote"}
WEAK = {"worked", "helped", "assisted", "participated", "involved", "tasked", "responsible"}
PRONOUNS = {"i", "me", "my", "we", "our", "us"}
DUTY = ("responsible for", "worked on", "helped with", "involved in", "tasked with", "duties included")
NUMBER = re.compile(r"(?<![\w.,])(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?(?!\w)")
def words(bullet):
return re.findall(r"[A-Za-z][A-Za-z'-]*", bullet)
def numbers(text):
return [n.replace(",", "") for n in NUMBER.findall(text)]
def test_eight_different_bullets():
"""keeps eight bullets, all different"""
assert len(BULLETS) == 8
assert len(set(BULLETS)) == 8
def test_each_starts_with_an_action_verb():
"""starts every bullet with a past-tense action verb that is not a weak opener"""
for n, bullet in enumerate(BULLETS, start=1):
first = words(bullet)[0].lower()
assert first.endswith("ed") or first in IRREGULAR, f"bullet {n} starts with {first!r}"
assert first not in WEAK, f"bullet {n} starts with the weak opener {first!r}"
def test_no_pronouns_and_no_duty_phrases():
"""leaves out personal pronouns and duty phrases"""
for n, bullet in enumerate(BULLETS, start=1):
assert not {w.lower() for w in words(bullet)} & PRONOUNS, f"bullet {n} has a personal pronoun"
assert not any(p in bullet.lower() for p in DUTY), f"bullet {n} has a duty phrase"
def test_numbers_come_from_the_facts():
"""gives every bullet a number, and only numbers that its own fact holds"""
for n, (bullet, fact) in enumerate(zip(BULLETS, FACTS), start=1):
found = numbers(bullet)
assert found, f"bullet {n} has no number"
extra = [x for x in found if x not in numbers(fact)]
assert not extra, f"bullet {n} has numbers that fact {n} does not hold: {extra}"
def test_each_names_its_subject_and_fits_two_lines():
"""names the subject of its fact and stays within 200 characters"""
for n, (bullet, subject) in enumerate(zip(BULLETS, SUBJECTS), start=1):
assert any(s in bullet.lower() for s in subject), f"bullet {n} does not name its subject ({subject[0]}…)"
assert len(bullet) <= 200, f"bullet {n} has {len(bullet)} characters" A hint
Take each fact in turn and answer three questions in one line: what did Kabir do (the verb), what changed (with the numbers of that fact and no other), and how. Fact 6 becomes, for example, a line that starts "Wrote a setup guide …" and ends with the 1 day and the 3 days.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then work through questions on the four ideas of this lesson; the one you have answered worst so far comes first:
Practice that adapts to you
Practise this lesson
12 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to mid-level): What makes a resume bullet strong?
It says what changed, by how much and how I did it, in one or two lines. “Cut p95 API latency from 420 ms to 180 ms by adding PostgreSQL indexes and a Redis cache” starts with a verb for my action, gives the change with its baseline and unit, and names the method, which is what an interviewer asks about first. A duty such as “Responsible for the API” has none of the three. Two more things make a bullet strong: it is about my part, not the team’s, and every number in it comes from a record I could show. When there is no number, I name an outcome someone can check, such as a merged pull request.
An interviewer may press on that answer. Pick the reply you would give; each choice says what it gets right or misses:
Checkpoint
Interview round: two follow-up questions
Two questions of this module’s bank put the rules to work on harder cases:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
Your team of five built a college fest website that 1,140 students used, and you wrote its registration and payment pages. How do you write the bullet so that it credits your part without hiding the team, and what do you say when the interviewer asks who did what?
-
Mid to senior
You have nine true bullets for your current job and room for four. The posting stresses scale and on-call work. How do you choose and order the four, what happens to the other five, and how would the choice change for another posting?
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
What shape does Google's hiring page ask a resume accomplishment to take?
Accomplished X, as measured by Y, by doing Z: the result, the measure that shows it, and the work that produced it.
-
Which parts must a strong bullet keep, whatever their order?
A verb for your action, the change with its measure, and the method. The order may bend, for instance to put a technology the posting asks for before the numbers, but without the change and its measure the bullet is a duty again.
-
Which three questions turn a duty into a result?
What was different after your work, how do you know, and what did you do to make it so? The answers supply the result, the measure and the method.
-
What can a bullet offer when there is no hard number?
An outcome someone can check, such as a pull request the maintainers merged, or an honest estimate labelled as one, such as about 3 hours a week saved.
-
What can a bullet checker never tell you?
Whether the bullet is true. It reads form only: a verb first, no pronoun, a measure, a sensible length. A bullet with a wrong figure passes just as easily, so every number has to come from your own records.
-
Which habits keep a list of bullets readable?
A precise action verb instead of “worked on” or “helped with”; no “I”, “my”, “me” or “we”; past tense for finished work and present tense only for what you still do, used consistently; and active voice, which shows who did the work.
-
How many bullets does a recent, relevant role get, and in what order?
Three to five, none longer than two printed lines, and fewer for older or less relevant roles. Order them by relevance to the posting, because the first bullet under a job is the one most likely to be read.
-
What is a bullet bank, and why keep one?
A private file holding every true bullet you could write for a job, each next to the record behind it. For each posting you pick and order from it; the lines you leave out wait for another posting.
Key takeaways
- A strong bullet says what changed, by how much and how you did it: Google’s accomplished X, measured by Y, by doing Z.
- A duty describes the job; it becomes a result when you answer what was different after your work, how you know, and what you did.
- Start with a precise action verb, keep one tense convention and no personal pronouns, and name your own part of team work.
- Give a recent, relevant role three to five bullets of at most two lines, ordered by relevance to the posting, and keep the rest in a bullet bank.
- A checker can test the form of a bullet, never its truth, so every number must come from your own records.
Terms in this lesson
- bullet bank
- A private list of every true bullet you could write for a job or a project, each with the record that proves it, from which you choose the few that fit one posting.
- STAR method
- A way to describe one example of your work in four parts: the situation, the task, the action you took and the result. The UK's National Careers Service suggests it for CVs, application forms and interviews.
References
- Our hiring process (How we hire) (Google Careers)
- How to write a good resume (Job Bank, Government of Canada)
- How to write a CV (National Careers Service (UK))
- The STAR method (National Careers Service (UK))
- Create your Europass CV (Europass, European Union)
- Resumes (MIT Career Advising and Professional Development)
- Resumes: Writing about your skills (MIT Career Advising and Professional Development)
- Harvard College Guide to Creating a Strong Resume (Harvard FAS Mignone Center for Career Success)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress