Making your GitHub profile match your resume
35 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Before the interview, an interviewer may open your GitHub link from the resume. A clear profile helps, while a committed password or a pile of empty repositories can end the conversation.
Profile README template
A profile README is a README.md in a repository named exactly like your GitHub username. GitHub shows it at the top of your profile as a short introduction.
A visitor reads your profile README first. Keep it short and factual: who you are, your main projects and how to reach you.
# Hi, I'm AnanyaFinal-year CSE student building Python projects with clean, tested code.**Projects**- [Online Store API](https://github.com/ananya-rao/online-store-api): Python service for products, customers and orders, with MySQL and 45 tests- [Library Management System](https://github.com/ananya-rao/library-management): Python OOP console app for issuing and returning books**Tech:** Python, SQL, MySQL, pytest, Git**Contact:** ananya.rao.dev@gmail.com | linkedin.com/in/ananya-raoExample 1 · Creating the profile README repository
# 1. On GitHub: New repository, name it exactly your username (e.g. ananya-rao),# make it Public and tick "Add a README file".# 2. Then edit it locally:git clone https://github.com/ananya-rao/ananya-rao.gitcd ananya-rao# edit README.md with the template abovegit add README.mdgit commit -m "Add profile introduction and project links"git pushYour profile page now shows README.md above the pinned repositories.
What this code does
- The repository name must match your username exactly; that is how GitHub knows to show it on your profile.
- The commit message says what changed, which is the habit the commit-history section asks for.
Write it in Markdown, a simple text format where # makes a heading and - makes a list. Long lists of badges, animated banners and visitor counters add noise; one clear paragraph and a project list work better.
Pinning the projects listed on your resume
A pinned repository is one of up to six repositories you choose to show at the top of your GitHub profile, above the activity graph.
This program scores repositories to decide which four to pin: resume projects first, then repositories with a README and real commits.
Example 2 · Choosing what to pin
repos = [ {"name": "online-store-api", "on_resume": True, "readme": True, "commits": 64, "practice_only": False}, {"name": "library-management", "on_resume": True, "readme": True, "commits": 23, "practice_only": False}, {"name": "dsa-practice", "on_resume": False, "readme": True, "commits": 140, "practice_only": True}, {"name": "test123", "on_resume": False, "readme": False, "commits": 2, "practice_only": False}, {"name": "python-tutorial-copy", "on_resume": False, "readme": False, "commits": 5, "practice_only": True}, {"name": "hackathon-attendance-app", "on_resume": False, "readme": True, "commits": 31, "practice_only": False},]
def score(repo): points = 100 if repo["on_resume"] else 0 points += 20 if repo["readme"] else 0 points += min(repo["commits"], 50) points -= 30 if repo["practice_only"] else 0 return points
for repo in sorted(repos, key=score, reverse=True)[:4]: print(f"pin: {repo['name']} (score {score(repo)})")pin: online-store-api (score 170) pin: library-management (score 143) pin: hackathon-attendance-app (score 51) pin: dsa-practice (score 40)
What this code does
- Resume projects score highest, so they always come first. A README and some real commit history add points; practice-only repositories lose points.
- The weights are a simple way to think, not a rule. The point: your pinned list should be your resume list plus your next best real work.
Pin the same projects your resume lists, in the same order, so a reviewer who clicks your GitHub link sees them first. Give every pinned repository a clear name (online-store-api, not project1 or test-final), a one-line description and topic tags such as python, pytest, mysql. Pin practice repositories (DSA solutions, course exercises) only if you have space after real projects.
A good project README
A project README is the front page of a repository: what the project does, its tech, how to run it, how to use it, how to test it and what you would improve.
A reviewer should understand a project in one minute. This README answers their questions in the order they ask them.
Example 3 · Project README template
# Online Store APIA Python service for a small online store: products, categories, customers and orders.## Features- 20+ operations with input validation and clear error messages- Customer and admin roles; passwords stored as salted hashes- MySQL schema with stock checks; 45 pytest tests## Tech stackPython 3.12, MySQL 8.4, pytest, GitHub Actions## Run locallyPrerequisites: Python 3.12+, MySQL1. Copy .env.example to .env and set DB_PASSWORD and SECRET_KEY2. python -m venv .venv, activate it, then pip install -r requirements.txt3. python -m store.main## Example usagepython -m store.main list-products --page 1 --size 5## Testspython -m pytest## What I would improveCaching for product reads; tests that run against a real MySQL databaseA reviewer learns in under a minute: what it does, the stack, how to run it with one command, how to try it, how to test it and what you would do next. Secrets appear only as names (DB_PASSWORD, SECRET_KEY), never as values.
What this code does
- Each heading answers one question a reviewer has, in the order they ask it. Screenshots or a short GIF of a sample run make it even clearer.
- Follow your own Run locally steps on a fresh clone before you share the link; broken setup steps are the most common README problem.
Setup steps must actually work on a fresh machine: list the Python version, the database, environment variables and the exact commands. Test them by cloning your own repository into a new folder and following your README word for word. Screenshots or a short GIF of a sample run make it even clearer. Revise: Writing professional README files and using .gitignore (opens in a new tab) and A clean Python project others can run (opens in a new tab).
Parts of a good README:
- Title and one-line summary; then Features (3-6 bullets).
- Tech stack; Setup (prerequisites, configuration, run command); Usage (sample commands, screenshots).
- Tests (how to run); Project structure (optional); What I learned or would improve.
- Never put real passwords in the README; show placeholders like
DB_PASSWORD=your_password.
Clean commit history and no committed secrets
A secret is a password, API key or token. Never commit one: anyone can read it in the Git history, and bots scan public repositories for keys.
The scanner flags lines that look like real passwords or keys and skips placeholders.
Example 4 · Scanning a project for committed secrets
import reimport tempfilefrom pathlib import Path
RULES = [ re.compile(r"(?i)password\s*[=:]\s*(?!your_)\S+"), re.compile(r"AKIA[0-9A-Z]{16}"), re.compile(r"(?i)(api[_-]?key|secret[_-]?key|secret)\s*[=:]\s*(?!your_)\S{8,}"),]
with tempfile.TemporaryDirectory() as folder: repo = Path(folder) (repo / ".env").write_text("DB_PASSWORD=Shop@2026\nSECRET_KEY=k8Hq2LmP9xTz4WvB\n") (repo / ".env.example").write_text("DB_PASSWORD=your_password\nSECRET_KEY=your_secret_here\n") (repo / "README.md").write_text("Set DB_PASSWORD in .env before running.\n")
for path in sorted(repo.iterdir()): for number, line in enumerate(path.read_text().splitlines(), start=1): if any(rule.search(line) for rule in RULES): print(f"POSSIBLE SECRET {path.name}:{number}")
print("Fix: move values to environment variables, add the real file to .gitignore, rotate exposed keys.")POSSIBLE SECRET .env:1 POSSIBLE SECRET .env:2 Fix: move values to environment variables, add the real file to .gitignore, rotate exposed keys.
What this code does
- The scanner flags the real
.env(lines 1 and 2) but not the example file, whose values start withyour_. It prints file names only, and the temporary folder is deleted when thewithblock ends. - Real tools such as GitHub secret scanning and gitleaks know hundreds of key formats; this small version shows the idea. A key found in history must be rotated, not just deleted.
Example 5 · Keeping secrets out with .gitignore
# .gitignore (excerpt)__pycache__/.venv/.idea/.envconfig_local.py# If the real file was already tracked, stop tracking it (keeps your local copy):git rm --cached .envgit commit -m "Stop tracking local config with credentials"git push# Then change the database password and revoke any exposed keys.New commits no longer include the local config file. Old commits still contain the secret, so it must be rotated (changed) anyway.
What this code does
.gitignoreonly affects files that are not tracked yet;git rm --cachedstops tracking a file without deleting it from your disk.- History rewriting tools exist, but rotating the secret is the step that actually protects you, and it must come first.
A commit history shows how the project grew. Small commits with clear messages ("Add order total calculation with tests") show good habits; one giant "final" commit or 40 commits named "update" do not. Keep secrets in environment variables or an uncommitted .env file listed in .gitignore, and commit only an example file. If a secret was committed, rotate it (change the password or revoke the key) first, then clean the code; deleting the line in a new commit does not remove it from history. Revise: Writing professional README files and using .gitignore (opens in a new tab).
Keeping secrets out:
.gitignoreshould cover__pycache__/, the virtual environment folder (.venv/), IDE files (.idea/),.envand local config files.- Commit
.env.examplewith placeholders, not the real.envfile with passwords. - GitHub can block some secret patterns on push (push protection) and alert you; do not rely on it alone.
Archiving half-finished repositories
To archive a repository is to mark it read-only and label it "archived", so viewers know it is no longer active. It keeps old work visible without suggesting it is current.
This program decides what to do with each old repository: keep it public, add a README, archive it, make it private or delete it.
Example 6 · Deciding what to do with each old repository
repos = [ {"name": "online-store-api", "readme": True, "finished": True, "worth_showing": True, "commits": 64}, {"name": "first-website-2023", "readme": False, "finished": True, "worth_showing": True, "commits": 18}, {"name": "college-ml-assignment", "readme": False, "finished": False, "worth_showing": True, "commits": 9}, {"name": "python-tutorial-copy", "readme": False, "finished": False, "worth_showing": False, "commits": 5}, {"name": "test123", "readme": False, "finished": False, "worth_showing": False, "commits": 1},]
def decide(repo): if repo["commits"] <= 2 and not repo["worth_showing"]: return "delete (empty test)" if repo["finished"] and repo["readme"]: return "keep public" if repo["worth_showing"]: return "add README, keep public" if repo["finished"] else "archive (record of past work)" return "make private"
for repo in repos: print(f"{repo['name']:<22} -> {decide(repo)}")online-store-api -> keep public first-website-2023 -> add README, keep public college-ml-assignment -> archive (record of past work) python-tutorial-copy -> make private test123 -> delete (empty test)
What this code does
- Finished work with a README stays public; finished work without one gets a README. Unfinished but meaningful work is archived so it reads as past work, not abandoned work.
- Tutorial copies become private, and empty tests are deleted. The goal is a profile where every public repository is something you can discuss.
Many students have dozens of half-finished tutorial repositories with no README; they make a profile look unfocused. For each old repository, choose: finish it and add a README, archive it if it has some value as a record, make it private if it is unfinished and not worth showing, or delete it if it is an empty test. Keep the public profile to work you are happy to discuss.
Next step
Check your understanding
12 quick questions