The only test that matters
A portfolio is not a gallery and it is not a resume in HTML. It is evidence that you can take something from idea to working thing that other people can use. Most student portfolios fail that test not because the projects are bad but because the evidence is unreachable — a repo with no README, a demo that 404s, a screenshot of something nobody can run.
A live URL beats a repository. A reviewer who can click and immediately see the thing working has already given you more credit than a reviewer who has to clone, install dependencies, and guess at environment variables — which they will not do. Deploy it. Free static hosting and free tiers cover almost every student project, and a deployed toy outranks an undeployed system.
The rest of this guide is a set of decision rules for that one test: how to score which projects earn a slot, what the README has to do in its first four lines, how to write a case study that gets finished, where to host things so they are still up in March, and the audit to run the week before you start applying.
What a reviewer checks in 60 seconds
| Second | What they do | What kills you |
| 0–5 | Load the site or repo | It does not load, or it is a wall of unstyled text |
| 5–15 | Read the top line — what is this? | No one-sentence description of what the project does |
| 15–30 | Click the live demo | Broken link, dead deploy, or no demo at all |
| 30–45 | Skim the README or case study | Setup instructions where the explanation should be |
| 45–60 | Open the code, scan one file | Tutorial code, one giant file, no commits after day one |
Notice that none of those seconds are spent on your design taste or your framework choice. They are spent establishing that the thing is real and that you can explain it. Every rule below is downstream of that observation.
Scoring the projects before you pick three
Most students choose portfolio projects by fondness, which selects for the ones that were fun to build rather than the ones that read well cold. Score them instead. The weights below are opinionated but they are weighted in the direction the 60-second table actually points: reachability and explainability first, novelty and metrics second.
| Criterion | Weight | A score of 0 | A score of 3 |
| Reachable | ×3 | Repository only, or a link that is already dead | A live URL that loads in a couple of seconds on a phone |
| Explainable | ×3 | You would stall on "why did you design it that way?" | You can defend any file in it for ten minutes straight |
| Finished | ×2 | Happy path only — no error handling, no deploy, no README | Errors handled, a test or two, deployed, README written |
| Distinctive | ×2 | A CRUD app or a tutorial clone the reviewer saw twice today | Something specific enough that they ask a question about it |
| Result attached | ×2 | No number, no users, no measured outcome | One honest number: accuracy, latency, throughput, time saved, users |
score = 3·Reachable + 3·Explainable + 2·Finished + 2·Distinctive + 2·Result
max = 3·3 + 3·3 + 2·3 + 2·3 + 2·3 = 36
28-36 front page, with a full case study
18-27 keep it, but fix the lowest-weighted gap first
10-17 one specific fix may rescue it - work out which one
0-9 cut it, and do not feel bad about it
Three worked examples, scored the way a reviewer would score them rather than the way you feel about them:
| Project | Reach | Expl | Fin | Dist | Res | Total |
| A — tutorial to-do app, deployed, one weekend | 3 | 1 | 1 | 0 | 0 | 14 |
| B — course group project, booking system, never deployed | 0 | 2 | 2 | 1 | 1 | 14 |
| C — offline tool built solo over eight months | 3 | 3 | 3 | 3 | 2 | 34 |
A and B tie at 14, and the tie is the entire point of doing this on paper. They are not equally salvageable. B is losing all nine of its Reachable points to a gap you can close in an afternoon: deploying it moves 0 to 3 on a ×3 criterion, which takes it from 14 to 23 and straight into the keep band. A is losing its points to the project itself — it is a tutorial, and no amount of polish converts a tutorial into evidence you can build. B goes on the list after one afternoon. A gets cut.
Run the rubric on everything you have built, including the things you assume are too small. Small projects score surprisingly well on Reachable and Finished, and a small finished thing beats a large abandoned one on every criterion that carries weight.
Three strong projects, not ten weak ones
Ten shallow projects read as ten abandoned weekends. Three projects with depth read as someone who follows through. Depth means the boring parts are there: error handling, a test or two, a deploy, a README, commits spread over more than one day, and some evidence you dealt with a problem you did not anticipate at the start.
- Cut anything from a tutorial unless you extended it substantially, and say what you extended.
- Cut anything you cannot explain in an interview — every project on the page is fair game for a deep question, and the deep question is the whole reason the portfolio exists.
- Cut anything with no users and no result, unless the technical difficulty itself is the point, in which case say what was difficult in the first line.
- Keep the one that is unusual. One weird, specific project is worth more attention than three competent CRUD apps, because it is the one that generates a question.
- Keep the one with a number attached. Accuracy, latency, throughput, time saved, users — any honest metric anchors the whole page.
- Keep the one you are still working on. A commit from this month is a signal nothing else on the page can produce.
The README is the pitch
For most projects the README is the only documentation anyone will read, and for a hiring reviewer it is the case study. Setup instructions matter, but they belong below the explanation, not above it. The single most common README failure is opening with npm install, which answers a question nobody asked before they knew what the project was.
# Project name
One sentence: what it does and who it is for.
[Live demo](url) · 30-second screenshot or GIF
## Why
The problem, in two or three sentences.
## How it works
The architecture in a short paragraph, plus the stack.
## What was hard
The real constraint, and what you decided.
## Results
Numbers if you have them. Scope if you do not.
## Run it locally
The commands, last.
The same skeleton, filled in
Abstract templates are easy to nod at and hard to apply, so here is the shape with words in it. The project is invented purely to make the structure visible — the point is the density of the sentences, not the project.
# Shelf
Scan a barcode, and Shelf tells you what is already in your
kitchen and what expires this week. Runs entirely in the browser.
[Live demo](https://example.invalid/shelf) · [12s GIF](docs/demo.gif)
## Why
I kept buying a second jar of the thing I already had. Every app
I tried wanted an account before it would show me anything, and
none of them worked in a basement with no signal.
## How it works
A barcode is decoded in a web worker, looked up against a local
product index, and written to IndexedDB. There is no server and no
account - the whole app is a static bundle plus a service worker,
so it installs to the home screen and works offline.
Stack: vanilla JS, IndexedDB, a WASM barcode decoder.
## What was hard
The decoder ran at about 4 frames per second on my phone, which is
too slow to feel like scanning. Moving it off the main thread and
downscaling frames to 640px before decoding took it to about 22 fps.
The tradeoff is that very small barcodes now need a second attempt.
## Results
Decodes in 22 fps on a 2021 mid-range Android, 1.1 MB total bundle,
first paint under 1 second on a cold cache. Used daily by me and
three other people, which is the honest number.
## Run it locally
git clone ...
python3 -m http.server 8000
Four things in that example do almost all of the work. The first line says what it does and the unusual constraint. The Why is a specific irritation rather than a market opportunity. The hard part contains a before number, an after number, and a named tradeoff. And the results section says "three other people, which is the honest number" rather than inflating it, which is more persuasive than any invented figure would have been.
A screenshot or a short GIF near the top does an enormous amount of work, because it lets someone understand the project without running anything. It is the cheapest credibility you can buy, and it costs about ten minutes with a screen recorder.
How to write a project case study
A case study is not a feature list. It is a decision narrative — you had a problem, you had constraints, you chose something, and something happened. The structure that reads well:
- Context. What the problem was and why it was worth solving. One paragraph.
- Constraints. Time, cost, data, hardware, skill. Constraints are what make the decisions interesting.
- What you built. The architecture in plain language before any diagram, plus the stack and why.
- The hard part. The one thing that did not work at first. This is the paragraph that separates a builder from a follower of tutorials.
- The result. A number, or honest scope. "78% directional accuracy over 50+ sources per day" is a result; "successfully implemented" is not.
- What you would change. Short, specific, unromantic. It signals you evaluated your own work.
The six moves, written out
Each move is one paragraph and each paragraph has a job. Written end to end, at roughly the length that gets finished by a reader who is skimming:
CONTEXT I wanted to know what was in my kitchen without opening
an account. The job of this paragraph is to make the
reader want the thing to exist. Two to four sentences.
CONSTRAINTS No backend, because I did not want to pay for one or
keep it alive. Offline, because the freezer is in a
basement. One phone to test on. Name the constraints
that later explain a decision - the others are noise.
BUILT Prose first, diagram second. "Decode in a worker, look
up locally, store in IndexedDB, cache the shell in a
service worker." A reviewer who reads only this sentence
should be able to guess the file layout.
HARD PART Before number, what you tried, what you chose, after
number, and the tradeoff you accepted. 4 fps to 22 fps
by downscaling frames, at the cost of small barcodes.
This is the paragraph that gets you asked about it.
RESULT Measured, bounded, honest. If the honest number is small,
say the small number - an inflated one is the single
fastest way to lose a reviewer who checks.
NEXT One specific thing you would change and why, in a sentence
and a half. Not a roadmap. Not an apology.
The hard-part paragraph is the one interviewers actually reuse. If you write nothing else, write that one, because a specific technical decision with a before and an after is the closest thing a portfolio has to a conversation starter. It is also the paragraph you will be asked to defend, which is a reason to make it true rather than impressive.
Write the case study while the project is fresh. Reconstructing why you made a decision six months later produces vague prose, and vague prose is what makes a portfolio forgettable.
Where to put it so it is still up in March
Hosting is not a neutral choice, because the free tiers differ in exactly the dimension that matters to you: whether the thing is still reachable months after you stopped thinking about it. Terms change constantly, so verify current limits before you commit, but the shapes are stable.
| Option | Best for | What the free tier gives you | What expires or sleeps |
| Static hosting from a repository | Anything with no server — HTML, JS, WASM | Effectively unlimited at portfolio scale, custom domain supported | Nothing sleeps. The risk is a stale build, not an expiry |
| Edge or CDN static host wired to the repo | Static sites plus small serverless functions | Automatic deploys on push, generous build minutes | Build-minute caps; functions are often rate limited |
| Serverless app platform free tier | Framework apps with API routes | Enough for demo traffic and a custom domain | Cold starts; usage caps that pause the project |
| Container or web-service free tier | Anything needing a long-running process | One small instance, a database sometimes bundled | Sleeps after inactivity — a 30-60s cold start reads as broken |
| Managed database free tier | Anything with persistence | A small database, no card required | Frequently deleted after a period of inactivity, quietly |
| Model or notebook demo host | ML demos | CPU inference, a public URL | Sleeps; GPU is the paid tier |
| A small VPS at a few dollars a month | Anything at all | Full control, nothing sleeps | You now own patching, certificates, and the bill |
| No server at all | Tools that can run client-side | Free permanently, nothing to rot | You have to design for it up front |
The sleeping free tier is the most common way a portfolio dies without anyone noticing. Your demo works every time you check it, because your visit wakes it — and a reviewer who clicks once and waits 45 seconds concludes the link is dead. If the project can run entirely in the browser, that is the lowest-maintenance answer available, and the tradeoffs are covered in the guides on client-side-only tools and offline PWAs.
What your commit history says about you
Reviewers who open the repo look at the commit list before they look at the code, because it takes four seconds and it is harder to fake than a README. What it tells them:
- A single initial commit containing the whole project reads as imported, not built. It is the most common tell in a student repo.
- Everything in one 14-hour window reads as a hackathon or a tutorial sitting. Fine if you say so; damaging if you imply months of work.
- Messages that say what changed but never why. "fix bug" fifty times is a missed opportunity, since one line of why is the same amount of typing.
- A committed
.env, key, or token anywhere in history. This is the one that gets a candidate rejected rather than downgraded, and it stays in history after you delete the file. Scan your repos before you link them — the student founder security checklist covers the specific checks.
- Reverts, fixes, and a rename or two are good. That is the texture of real work, and its absence is more suspicious than its presence.
- A README edited after the first commit means somebody came back to the project. A branch with a real merge means you know the tool.
The fix for a thin history is not to manufacture one. It is to pick the project that scored highest on the rubric and keep working on it for a few more weeks — the history writes itself, and you get a commit from this month as a side effect.
Performance and accessibility a reviewer notices without knowing they noticed
Nobody runs an audit on your portfolio. They do form an impression in the first two seconds, and these are the cheap things that move it:
- First-view page weight under about 1 MB. A 4 MB hero image is the difference between loading and not loading on conference wifi. Export at the size you actually display.
- Real text, not images of text. Recruiters, screen readers, and search engines all read the page, and none of them read a screenshot of your resume.
- Alt text on every meaningful image, describing the content rather than the file.
- Body text contrast of at least 4.5:1. Grey-on-grey looks refined on your calibrated monitor and is unreadable on a laptop at an airport.
- Visible keyboard focus, and a demo link reachable by tab. Removing the focus outline for aesthetics is the single most common accessibility regression on personal sites.
- One
h1, then h2s in order. Heading structure is free, and it is how assistive technology navigates.
- Something legible with JavaScript blocked. The demo does not have to run; the page does have to say what it is.
- Run the browser's built-in audit once. Treat the output as a smoke test that finds three real problems, not as a score to optimise.
The portfolio site itself
- One page is enough. Who you are, three projects with links, contact. Depth lives on the project pages.
- Every project links to something clickable — live demo first, repository second, paper or write-up third.
- Fast and readable on a phone. A meaningful share of reviews happen on a phone between meetings.
- Real text, not images of text. Recruiters and search engines both read the page.
- Your email visible without a form. Contact forms lose messages and add friction to the exact action you want.
- Dates on things. A portfolio with no dates reads as stale by default.
- The same three projects, named identically, on the resume and the profile. A reviewer cross-referencing your LinkedIn profile against the site should find one story, not two.
Link rot is the actual failure mode
Building the portfolio is the fun part; keeping it reachable is the part that decays. Deploys expire, free tiers sleep, links rot, and dependency installs that worked in September fail in February. A dead demo link on an otherwise strong page is worse than no link, because it converts a positive signal into evidence you do not maintain your work. Put the maintenance on a schedule rather than on your memory:
| Cadence | What you check | Minutes |
| Weekly, during an application season | Click every outbound link on the site, and every link inside the resume PDF | 5 |
| Monthly | Wake anything on a sleeping tier and time the cold start yourself | 10 |
| Quarterly | Clone each repo into a clean folder and run the documented setup exactly as written | 30 |
| Before each application wave | The full pre-application audit below | 45 |
| Annually | Domain renewal, DNS, certificate expiry, and the year in the footer | 10 |
The quarterly clean-clone check is the one people skip and the one that catches the most. Your machine has the API key in a shell profile, the right Python version, and a cached dependency tree; the reviewer's machine has none of that, and neither does yours after a reinstall.
The pre-application audit
Forty-five minutes, once, before you send anything. Work down it in order and fix as you go rather than making a list to fix later:
- Open the portfolio in a private window on a phone. Time to first readable content should be under three seconds.
- Click every demo link. Any that takes more than ten seconds counts as broken.
- Read the first sentence of each project as if you had never seen it. Does it say what the thing does?
- Confirm every repo has a README with a demo link and an image in the first screen of text.
- Search each repo's history for
.env, key, token, and secret.
- Check that the three projects on the site match the three on the resume, by name.
- Confirm your email is on the page as text, not only behind a form.
- Tab through the page once with the keyboard and confirm focus is visible the whole way.
- View it with images disabled and confirm the page still explains itself.
- Ask one person who does not know your work to read it for 60 seconds and tell you what you do.
- Fix whatever they got wrong, because that is the thing the reviewer will get wrong too.
- Note the date of the audit somewhere, so the next one is scoped to what changed.
If step ten produces a wrong answer, nothing else on this page matters yet — the page is failing at its only job, and the fix is almost always in the first sentence rather than in the design.
The application side of this — which roles, which deadlines, what you sent and when — is what Apply OS handles, the document that points at the portfolio is covered in the ATS resume guide, and the interviews the portfolio buys you are the subject of the interview patterns guide. If you need sponsorship, the timeline that constrains all of it is in the internship guide.
Tools referenced in this guide
- Apply OS — free deadline radar and application pipeline, browser-only.
- Resume rebuild — the ATS-safe document your portfolio links from.
- ATS resume guide — how projects should be written on the resume itself.