Learn / Prove it — practice ladder
Rebuild one old project properly
Lesson 33 of 37 · 7 min read ·
The brief
Take a project you built in college — the one you were proud of, that had no tests, stored passwords in plain text, and ran only on your laptop. Build it again with the same feature set and production standards throughout.
Same idea is deliberate. Removing the design problem leaves only the engineering problem, which is the thing you are actually practising. And you get an honest measure of how much you have improved, because you have the old version to compare against.
Why this beats a new project
A new side project spends its energy on deciding what to build, then dies at 60% when the novelty runs out. A rebuild has a fixed, known, finite scope. You can actually finish it — and finishing is the part that teaches you, because the last 20% is where auth, errors, deployment and edge cases live.
Acceptance criteria
Not done until every one of these is true:
Correctness
- Tests exist and pass in CI, covering the core logic and the auth boundary.
- Input is validated at the API boundary with a schema, and invalid input returns
400/422with a useful body. - Errors are handled explicitly. No unhandled promise rejections, no stack traces returned to users.
Data
- A real database with migrations in version control. No hand-run SQL.
- Indexes on every column you filter or join on, verified with
EXPLAIN. See indexing. - Every list endpoint is paginated with a bounded page size.
Security
- Passwords hashed with argon2 or bcrypt. See encryption basics.
- Every endpoint authorises the resource, not just the session — try accessing another user's record and confirm it fails. See the OWASP Top 10.
- All queries parameterised.
- No secrets in the repository;
.envgitignored from the first commit. See secrets management. - Rate limiting on auth endpoints.
Operations
- Runs in Docker; a new developer can start it with one command.
- CI runs tests on every pull request.
- Deployed somewhere public with HTTPS.
- Structured logs, and a
/healthendpoint that checks the database. - A README with setup steps that actually work on a clean machine.
Sequence it
Do not do it all at once. In order:
- Core feature working end to end, no polish. Prove the shape.
- Real database with migrations.
- Auth, properly.
- Tests around what exists.
- Docker and CI.
- Deploy.
- Logging, health checks, error handling.
- Then the remaining features.
Deploying at step 6 rather than at the end is the important bit. Everything after is deployed continuously, so you never face one enormous scary first deploy.
Write it down
Keep a file: WHAT_I_DID_DIFFERENTLY.md. One line per decision that differs from the original, with the reason. Twenty lines by the end.
That file is the artefact. It is what you talk about in an interview, and it is proof to yourself that the improvement is real and specific rather than a feeling.
Prove you know it
The project is finished when someone else can clone it, run one command, and have it working — and when you can hand them the URL of the deployed version in the same message.