Verdict
Yes
Yes, write one or two tests of the core logic of a side project that other people use, run them on every push, and leave the pages and glue code untested.
Why
- Blast radiususers
- Change frequencyregularly
- Detectabilityeventually
- Reversibilitytrivial
- Test costmoderate
Yes, write a few tests for a side project that other people use, aimed at the logic the project exists for. For a small web app with a few dozen users, Blast radius is users, and Change frequency is regularly, because you work on it some weekends and leave it for weeks between. Detectability is eventually: nothing monitors the app, and free users who hit a bug leave instead of writing to you. Reversibility is trivial, since you redeploy the previous commit and nothing stays behind. Test cost is moderate, because installing a test runner and writing the first test takes about an hour, and rule R12 gives Test minimally.
| When | Decision | Why |
|---|---|---|
| Only you use the side project, on your own machine | Do not test; look at the result after each change | Blast radius falls to none: a failure costs nobody but you |
| The side project is mostly pages on a hosted backend, and you reload each page after an edit | Do not test; look at each page after the change | Detectability becomes immediately: the broken page is on your screen, and a redeploy undoes it |
| Users keep records in the side project that they cannot rebuild, such as a training log | Test the code that saves each record, with a test that reads back every saved field, in CI | Reversibility rises to with-effort: wrong records need a repair script and a message to each affected user |
| The side project charges users money | Test mandatory: cover the charge amount and the webhook that grants access | Blast radius rises to money and Reversibility to costly: a wrong charge ends in refunds |
| Users log in to the side project, and each user must see only their own records | Test mandatory: one test that a user cannot read the records of another user | Blast radius rises to safety-or-legal: a missing check exposes personal data |
| The side project is a library that other people install from npm or PyPI | Test the public API and its likeliest edge cases in CI on every pull request | Reversibility rises to costly: a broken release stays in the lockfiles of other people |
| The side project scrapes a site that changes without notice | Test it differently: a scheduled job that alerts you when the scraper returns zero items | Test cost rises to heavy: a saved copy of the page goes stale whenever the site changes |
What breaks if you don't test
The costly failure comes in the core logic after a long break. You return after four months, change a function you no longer remember, and the output stays plausible. No alert fires; a side project seldom has one. Users who see a wrong result stop using the app instead of filing a bug report.
What you lose if you over-test
Your time on a side project is a few evenings a month. Tests pinned to markup or to mocked HTTP calls break when you swap a UI library, and trying new libraries is one reason side projects exist. A coverage target of 80% adds hours of tests for settings screens few users open.
How to test
Write unit tests for the core logic first:
- Pick the function whose wrong output would look right: the totals, the ranking, the parser.
- Write one test with an input small enough to work out by hand, and compare with your own result.
- Run the tests on every push. The GitHub Actions guide for Node.js shows the workflow file, and public repositories run it for free.
- When a bug appears, add a regression test with the input that caused it.
For JavaScript, Vitest needs one install command. For Python, use pytest.
When the answer changes
- Other people pay you through the app: the payment path needs mandatory tests.
- Users log in and store their own data: test that one account cannot read another.
Real incident + Code example
The leaderboard that grew by 61%
In my practice, the clearest case was my own side project: a web app that ranks about 40 members of my running club by weekly distance. Four months after the last commit, I added a miles option that converted every distance to kilometres, including distances already in kilometres, so every total grew by 61%. The leaderboard still looked plausible. Eleven days later a member asked why her 10 km run showed as 16.1 km. The fix took one line, but three members had stopped logging by then. The test I added afterwards fails on the broken version:
import { expect, it } from "vitest";
import { weeklyTotals } from "./totals";
it("converts miles and leaves kilometres alone", () => {
const totals = weeklyTotals([
{ runner: "ana", distance: 10, unit: "km" },
{ runner: "ana", distance: 5, unit: "mi" },
{ runner: "ben", distance: 12, unit: "km" },
]);
// Worked out by hand: 10 + 5 x 1.609344 = 18.05 km
expect(totals.ana).toBeCloseTo(18.05, 2);
expect(totals.ben).toBe(12);
});
With the bug, ana gets 24.14 km and ben 19.31 km, so both assertions fail.
Related questions
FAQ
- Should I write tests for personal projects?
Yes, write one or two tests of the core logic of a personal project that other people use, and run them on every push. If only you use the project, skip the tests and check the result after each change.
- Is TDD worth it for a side project?
Test-driven development on a side project pays off for the core logic, where you know the expected output before you write the code. For pages that change every weekend, a test written first adds work on code you may delete. If the project exists to learn TDD, write the tests anyway, because the framework does not count the value of learning.
- Should an open source side project have tests?
Yes, an open source library that other people install needs tests of its public API, run in CI on every pull request. A broken release stays in the lockfiles of other people after you publish a fix.