Resume, Portfolio & GitHub Profile Module 3 – Writing content that proves your work
Finding numbers for your resume bullets
Where the numbers for resume bullets already are, how to measure a before-and-after, and how to write estimates and confidential figures you can defend.
What you will learn
- Find honest measures in records you already have, such as repositories, trackers and reports
- Measure a before-and-after when no record holds a number yet
- Write estimates and ranges that you can defend in an interview
- Protect confidential figures while still showing the scale of your work
Before you start
What you’ll learn · Before you start
What you will learn
- Find honest measures in records you already have, such as repositories, trackers and reports
- Measure a before-and-after when no record holds a number yet
- Write estimates and ranges that you can defend in an interview
- Protect confidential figures while still showing the scale of your work
Before you start
On this page
Asha’s first draft had nine bullets and one number in them. Her records held more than twenty: a version history, a ticket tracker, build logs, test reports and a feedback form, each measuring her work in time, volume, quality or scope. If you think you have no numbers, you may well have several, in records you have not opened yet.
This lesson shows where those numbers are and how to read them out honestly: one scope line from a real Git history, and a change written against its baseline: The value of a measure before your change, such as a build time or a wait. A result is written against it, from the baseline to the new value, so that a reader can judge its size.. Then come the harder cases, each with its own method: measuring when no record holds a number, estimates you can defend in an interview, and figures you are not allowed to share.
Five kinds of measure, and the records that hold them
Canada’s Job Bank asks for firm numbers an employer will understand, such as how many people you supervised or by what percentage you increased sales. MIT’s careers office groups them as money, increases or reductions in percent, and the quantity of work: the size of a department, an event, a budget or a data set. For technical work, five kinds cover almost everything:
| Kind | An example from this track | Where it is recorded |
|---|---|---|
| Time | p95 latency from 420 ms to 180 ms | Monitoring, build logs, a timed run |
| Volume | 2 million shipment events a day | Logs, analytics, database counts |
| Quality | Line coverage from 48 % to 81 % | Test reports, incident and bug trackers |
| Scope | 14 services moved to Kubernetes | Version history, release notes, tickets |
| Money | Hours or costs saved | Finance reports, only with permission |
Students have the same records, smaller: a project’s history, a contest’s results page, a feedback form, a usage log. Follow one student project through its records, and note which number each one holds:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The work. Meera's part of a three-student canteen queue app was the token queue, its tests and an export of serving times. Before writing a bullet, she asks which records already measure that work.
- Version history. The project's version history records who changed what. It gives a measure of scope: 14 of the 23 commits are hers. Scope shows how much she did, not what it achieved.
- A baseline by hand. No record held the wait before the app existed, so the canteen committee timed 20 lunch-hour waits by hand in the week before launch: 14 minutes on average. That is the baseline.
- The serving-time log. From launch, the app logged when each token was issued and served. Over four weeks of lunch hours the average wait was 6 minutes. Against the baseline, that is the result: a measure of time.
- The feedback form. The weekly feedback form has one question about queues. Complaints fell from 41 in the week before launch to 9 in the fourth week: a measure of quality, from the people the work was for.
- The bullet. The strongest number becomes the bullet: Built a token queue for the college canteen in Flask and SQLite, cutting the average lunch-hour wait from 14 to 6 minutes. The commits and complaints stay ready for the interview.
The strongest number was a result, the wait in minutes, and it needed two records: a log of the wait after the app and a baseline timed by hand before it. The commit count was the weakest: it says how much Meera did, not what changed.
Checkpoint
Check: Kinds of measure and the records that hold them
A number needs its baseline, its unit and its source
Version history is the record most developers forget. Two Git commands, git shortlog -sn and git log --shortstat,
count commits by person and the files and lines each commit changed. The program builds the canteen app’s history with
real Git and adds up Meera’s share:
# Reads one honest scope line out of a project's version history. It builds an invented repository, the canteen queue
# app of three students, with real Git commits (fixed names and times, so every run makes the same history), then runs
# the two commands you would run on your own project and adds up what they print for one person.
#
# Git runs in a new folder below this one, which is also its home folder, so your own Git settings change nothing and
# GIT_CONFIG_NOSYSTEM=1 skips the computer's system-wide settings. Every person and file is invented.
import os
import re
import shutil
import subprocess
from pathlib import Path
HOME = Path("learner").resolve()
REPO = HOME / "canteen-queue"
START = 1767240000 # one fixed moment, in seconds since 1970; commits follow one day apart (dates are never shown)
PEOPLE = {
"Meera": ("Meera Iyer", "meera@example.com"),
"Rohan": ("Rohan Das", "rohan@example.com"),
"Imran": ("Imran Khan", "imran@example.com"),
}
# Each commit: who, the message, and what it does to files: add n lines, change the last n, or drop the last n.
COMMITS = [
("Meera", "Start the canteen queue app", [("app.py", "add", 20), ("README.md", "add", 8), ("requirements.txt", "add", 2)]),
("Meera", "Add the token table", [("schema.sql", "add", 14), ("app.py", "add", 10)]),
("Rohan", "Add the counter page", [("templates/counter.html", "add", 30), ("static/style.css", "add", 25)]),
("Meera", "Issue tokens in order", [("queue.py", "add", 40), ("app.py", "add", 6)]),
("Meera", "Test the token order", [("tests/test_queue.py", "add", 35)]),
("Imran", "Add a menu page", [("templates/menu.html", "add", 28)]),
("Meera", "Call the next token", [("queue.py", "add", 22), ("templates/counter.html", "add", 6)]),
("Rohan", "Style the token display", [("static/style.css", "add", 30)]),
("Meera", "Log the time each token is served", [("queue.py", "add", 18), ("schema.sql", "add", 4)]),
("Meera", "Test serving times", [("tests/test_queue.py", "add", 30)]),
("Rohan", "Show how many are waiting", [("templates/counter.html", "add", 12)]),
("Meera", "Skip tokens nobody collects in 5 minutes", [("queue.py", "add", 26)]),
("Imran", "Add prices to the menu", [("templates/menu.html", "add", 14)]),
("Meera", "Test skipped tokens", [("tests/test_queue.py", "add", 28)]),
("Meera", "Export the day's serving times as CSV", [("export.py", "add", 32)]),
("Rohan", "Fit the counter page on phones", [("static/style.css", "add", 18)]),
("Meera", "Fix the token number reset at midnight", [("queue.py", "change", 4), ("queue.py", "add", 2)]),
("Meera", "Test the midnight reset", [("tests/test_queue.py", "add", 16)]),
("Imran", "Fix menu typos", [("templates/menu.html", "change", 2)]),
("Rohan", "Add a large-print mode", [("static/style.css", "add", 12)]),
("Meera", "Document how to run it", [("README.md", "add", 24)]),
("Rohan", "Tidy the templates", [("templates/counter.html", "change", 4), ("templates/counter.html", "drop", 2)]),
("Meera", "Add the setup script", [("setup.sh", "add", 15)]),
]
def git_env(when):
env = {
"PATH": os.environ.get("PATH", "/usr/bin:/bin"), "HOME": str(HOME), "XDG_CONFIG_HOME": str(HOME / ".config"),
"GIT_CONFIG_NOSYSTEM": "1", "GIT_CEILING_DIRECTORIES": str(HOME.parent), "LANG": "C", "LC_ALL": "C",
"TZ": "UTC", "GIT_AUTHOR_DATE": f"@{when} +0530", "GIT_COMMITTER_DATE": f"@{when} +0530",
}
if "TMPDIR" in os.environ:
env["TMPDIR"] = os.environ["TMPDIR"]
return env
def git(*args, when=START):
done = subprocess.run(["git", *args], cwd=REPO, env=git_env(when), capture_output=True, text=True, check=True)
return done.stdout
def build():
shutil.rmtree(HOME, ignore_errors=True)
REPO.mkdir(parents=True)
git("init", "-q", "-b", "main")
files, made = {}, 0
for n, (who, message, edits) in enumerate(COMMITS):
for path, how, count in edits:
lines = files.setdefault(path, [])
if how == "add":
lines += [f"{path}: line {made + i}" for i in range(count)]
elif how == "change":
lines[-count:] = [f"{path}: line {made + i}, changed" for i in range(count)]
else:
del lines[-count:]
made += count
target = REPO / path
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text("\n".join(lines) + "\n")
name, email = PEOPLE[who]
git("add", "-A")
git("-c", f"user.name={name}", "-c", f"user.email={email}", "commit", "-q", "-m", message, when=START + n * 86400)
build()
print("An invented project: the canteen queue app of three students, with 23 commits.")
print()
print("$ git shortlog -sn HEAD")
print(git("shortlog", "-sn", "HEAD"), end="")
print()
print('$ git log --author="Meera Iyer" --shortstat --format=')
stats = git("log", "--author=Meera Iyer", "--shortstat", "--format=")
print("\n".join(line for line in stats.splitlines() if line.strip()))
print()
added = sum(int(x) for x in re.findall(r"(\d+) insertions?", stats))
deleted = sum(int(x) for x in re.findall(r"(\d+) deletions?", stats))
mine = int(git("rev-list", "--count", "--author=Meera Iyer", "HEAD"))
total = int(git("rev-list", "--count", "HEAD"))
touched = set(git("log", "--author=Meera Iyer", "--name-only", "--format=").split())
every = set(git("ls-files").split())
print("Meera's share, added up:")
print(f" commits: {mine} of {total} ({mine * 100 // total} %)")
print(f" files she changed: {len(touched)} of the {len(every)} in the project")
print(f" lines: {added} added, {deleted} deleted")
print()
print("A scope line these records support:")
print(f" Wrote {mine} of the {total} commits of a three-person canteen queue app in Flask and SQLite,")
print(" including the token queue, its tests and the CSV export of serving times.") Output
An invented project: the canteen queue app of three students, with 23 commits.
$ git shortlog -sn HEAD
14 Meera Iyer
6 Rohan Das
3 Imran Khan
$ git log --author="Meera Iyer" --shortstat --format=
1 file changed, 15 insertions(+)
1 file changed, 24 insertions(+)
1 file changed, 16 insertions(+)
1 file changed, 6 insertions(+), 4 deletions(-)
1 file changed, 32 insertions(+)
1 file changed, 28 insertions(+)
1 file changed, 26 insertions(+)
1 file changed, 30 insertions(+)
2 files changed, 22 insertions(+)
2 files changed, 28 insertions(+)
1 file changed, 35 insertions(+)
2 files changed, 46 insertions(+)
2 files changed, 24 insertions(+)
3 files changed, 30 insertions(+)
Meera's share, added up:
commits: 14 of 23 (60 %)
files she changed: 9 of the 11 in the project
lines: 362 added, 4 deleted
A scope line these records support:
Wrote 14 of the 23 commits of a three-person canteen queue app in Flask and SQLite,
including the token queue, its tests and the CSV export of serving times.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 git_scope.py
Fourteen of 23 commits and 9 of 11 files support an honest scope line, and every number in it can be shown. Results need more than a count. A result names its baseline and its unit, as in “from 14 to 6 minutes”, so a reader can judge the size, and its size is measured against the baseline: from 420 ms to 180 ms is 57 % lower. When the measure is already a percentage, the change is in percentage point: The unit for the difference between two percentages. Coverage that rises from 48 % to 81 % rises by 33 percentage points, which is not the same as rising by 33 %., so coverage rising from 48 % to 81 % rose by 33 points.
Percentage Calculator Work out a change against its baseline, or in percentage points, with the working shown.Three cases need more care. When no record holds a number, measure one: run the work before and after your change on the same input, and keep the output. When a number is an estimate, label it, name its source and round it down. When a figure is confidential, show its scale with a range, a percentage or a relative change.
Checkpoint
Check: A baseline, a unit and a source for every number
Measuring a before-and-after, with a timed run Pro: opens with a Pro, Premium or Ultimate pass
When no record holds the number, you make one. This part times a report before and after a change, the way a resume number should be measured, and turns the result into a bullet with its conditions.
In this part:
- Five rules for a measurement you can defend, with a timed run
- Why the median of several runs, and not the best or the first
1 runnable example (Python) · about 4 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Estimates and ranges you can defend in an interview Pro: opens with a Pro, Premium or Ultimate pass
Some numbers can only be estimated, and an estimate is honest when it says so. This part counts one record three ways, rewrites five inflated claims, and gives three questions that every estimate has to survive.
In this part:
- Counting people, not events, in one token log, worked
- Five inflated claims, rewritten as estimates and ranges
- Three questions every estimate has to survive
1 runnable example (Python) · about 4 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Confidential figures: the size of the work without the figure Pro: opens with a Pro, Premium or Ultimate pass
Many of the best numbers belong to your employer. This part shows how to keep the size of your work on the page while the figure itself stays private, and which numbers to leave off even when they flatter you.
In this part:
- Ranges, percentages and relative change, side by side
- Numbers that look strong and prove little
about 4 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Practise: write a change against its baseline
Write the function that turns two measurements into the phrase a bullet can use, with the baseline, the unit and the size of the change measured against the baseline:
Exercise · Easy · Python
Write a change against its baseline
A number on a resume needs its baseline and its unit, and its size is measured against the baseline. Write result_phrase(before, after, unit) in phrase.py. It returns the phrase a bullet can use, or None:
- With no baseline (
beforeisNone) it returnsNone: there is nothing to compare with, so measure first. - Otherwise the phrase starts
"from <before> <unit> to <after> <unit>", each number with commas between thousands:from 1,200 ms to 300 ms. - Then comes the size of the change, after a comma:
57 % lower,150 % higher, orunchangedwhen the two are equal. The percentage is the change divided by the baseline, rounded down to a whole number, so a claim is never bigger than its record: 57.14 becomes 57, and 66.67 becomes 66. - When the unit is
"%", the measure is already a percentage, so the change is in percentage points: from 48 % to 81 % is33 percentage points higher, not 68 % higher. - A baseline of 0 has no percentage: the phrase is only
from 0 tests to 140 tests.
The sample tests use numbers from this lesson and the last one.
Starter code · phrase.py
def result_phrase(before, after, unit):
"""The change from before to after, as a resume bullet can say it, or None without a baseline."""
# Replace this line with the rules of the prompt.
return f"{after} {unit}" The sample tests · test_phrase.py
from phrase import result_phrase
def test_a_cut_against_its_baseline():
"""measures a cut against the baseline: 420 ms to 180 ms is 57 % lower"""
assert result_phrase(420, 180, "ms") == "from 420 ms to 180 ms, 57 % lower"
def test_a_rise_against_its_baseline():
"""measures a rise against the baseline: 40 to 100 users a day is 150 % higher"""
assert result_phrase(40, 100, "users a day") == "from 40 users a day to 100 users a day, 150 % higher"
def test_percentages_change_in_points():
"""gives the change of a percentage in percentage points"""
assert result_phrase(48, 81, "%") == "from 48 % to 81 %, 33 percentage points higher"
assert result_phrase(31, 12, "%") == "from 31 % to 12 %, 19 percentage points lower"
def test_rounds_down_never_up():
"""rounds the percentage down, so the claim is never bigger than the record"""
assert result_phrase(14, 6, "minutes") == "from 14 minutes to 6 minutes, 57 % lower"
assert result_phrase(3, 1, "s") == "from 3 s to 1 s, 66 % lower"
def test_thousands_and_no_change():
"""writes thousands with commas, and says unchanged when nothing changed"""
assert result_phrase(1200, 300, "ms") == "from 1,200 ms to 300 ms, 75 % lower"
assert result_phrase(5, 5, "s") == "from 5 s to 5 s, unchanged"
def test_no_baseline_or_a_zero_one():
"""refuses a missing baseline and gives no percentage from zero"""
assert result_phrase(None, 180, "ms") is None
assert result_phrase(0, 140, "tests") == "from 0 tests to 140 tests" A hint
Build the phrase in two parts. The first, f"from {before:,} {unit} to {after:,} {unit}", is the same in every case but the first. For the second, work out the difference after - before, and use int(...) on a positive number to round it down.
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 answer questions on the kinds of measure and on baselines; the idea you have answered worst so far comes first:
Practice that adapts to you
Practise this lesson
6 questions about 5 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): Where do you find numbers for your resume when you think you have none?
In the records the work left behind. A repository’s history gives scope, such as how many commits or services were mine. A tracker gives counts of bugs or tickets closed, a build log gives times, a test report gives coverage, and a feedback form or usage log gives results for the people the work was for. When nothing recorded the result, I measure it before and after a change on the same input and keep the output. Every number I use comes with its baseline and its source, so I can explain it if asked.
Three questions of this module’s bank go further:
-
Fresher to senior
Last year you sped up a slow report, but nobody measured it before your change, and the old version no longer runs in production. How do you write the bullet, and what do you say when an interviewer asks how much faster it got?
-
Mid to senior
Your work cut the fraud losses of a listed company's payments service, but the amounts are confidential. How do you show the size of the result on your resume, and how do you answer when an interviewer presses for the figure?
-
Fresher to mid
Your side project's analytics show 10,000 page views last month, and a friend tells you to write "used by 10,000 people". What do you write instead, and how would you defend it in an interview?
Flashcards · 7 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
Which five kinds of measure cover almost all technical work?
Time, such as latency; volume, such as events a day; quality, such as test coverage; scope, such as services moved; and money, such as hours or costs saved, which you share only with permission.
-
Where do the numbers for a resume usually come from?
From records the work left behind: version history, ticket trackers, build logs, monitoring, test reports, feedback forms and usage logs. A student's projects and contests leave the same kinds of record.
-
Which two Git commands give scope numbers for a project?
git shortlog -sn counts the commits of each person, and git log --shortstat reports the files and lines each commit changed.
-
Why is a commit count a weak number on a resume?
It shows how much you did, not what changed for anyone. A result, such as a canteen wait cut from 14 to 6 minutes, beats it whenever you have one.
-
What does a result need before a reader can judge its size?
Its baseline, its unit and its source, as in “from 420 ms to 180 ms”. The size of the change is taken against the baseline, which makes that drop 57 %.
-
Test coverage rose from 48 % to 81 %. How do you state the rise?
As 33 percentage points. When the measure is itself a percentage, the difference is given in points, which is not the same as a 33 % increase.
-
What do you do when no record holds a number, when a number is an estimate, or when it is confidential?
Measure it by running the work before and after your change on the same input, and keep the output. Label an estimate, name its source and round it down. Show a confidential figure's scale as a range, a percentage or a relative change.
Key takeaways
- Most work leaves records that measure it in time, volume, quality, scope or money: histories, trackers, logs, reports and feedback forms.
- A result needs its baseline, its unit and its source, and its size is measured against the baseline; a change of a percentage is given in percentage points.
- Scope numbers such as commits show how much you did, not what changed, so a result beats them when you have one.
Terms in this lesson
- baseline
- The value of a measure before your change, such as a build time or a wait. A result is written against it, from the baseline to the new value, so that a reader can judge its size.
- median
- The middle value of a set of numbers once they are sorted, with half the values below it and half above. Of five timed runs, it is the third fastest.
- percentage point
- The unit for the difference between two percentages. Coverage that rises from 48 % to 81 % rises by 33 percentage points, which is not the same as rising by 33 %.
References
- How to write a good resume (Job Bank, Government of Canada)
- Resumes: Writing about your skills (MIT Career Advising and Professional Development)
- git-log documentation (Git project)
- git-shortlog documentation (Git project)
- time: perf_counter (Python documentation) (Python Software Foundation)
- timeit: Measure execution time of small code snippets (Python Software Foundation)
- GitHub Acceptable Use Policies (GitHub Docs)
- SEBI (Prohibition of Insider Trading) Regulations, 2015, regulations 2(1)(g), 2(1)(n), 3(1) and 3(2A) (Securities and Exchange Board of India)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress