The brief was a fully working job posting site, responsive on mobile, LinkedIn-like in shape: employers post roles, candidates apply, both sides see what they need to see. Not a mockup. A real thing, just simple enough to be buildable by one intern.
That ceiling did not last long.
The first few days went as expected. I found a clean template, adapted it, got the home page and a handful of linked pages looking reasonable. I started a TODO.md to track ideas as they came: what features a real version would need, what I wanted to try, what questions I didn’t have answers to yet. How would candidates upload their details, how would employers filter applications, what would the validation flow look like? The notes were supposed to be notes. Instead they became a roadmap I started executing without being asked to.
Toast notifications went in first. Not required, not discussed, but I had been wanting to mess around with the JavaScript for it and the project was a convenient excuse. An admin dashboard followed, with tables and status filters and per-application actions. The HTML was getting complicated enough that I pulled it into proper components. The site had a theme now, a consistent palette across pages, and once that existed it felt wasteful not to carry it through into the email templates too.
So I built an HTML email. Found a logo online that matched the palette, structured it as a proper transactional email with header, body, and footer. Then wired up Twilio for OTP verification so candidates could confirm their contact details before applying. At some point centering that email layout correctly took longer than writing the backend logic for the entire OTP flow. I tried position: absolute, then display: grid. One of them worked. I will let you guess which one.
Twilio’s Verify API handles OTP delivery and expiry. The free tier has a cap on verified numbers that makes multi-account testing awkward, but the basic flow is clean.
The question of where to put uploaded files had an obvious answer: Amazon S3. I set up a bucket, configured the access policy, wired it into the application. Then I deployed the whole thing to AWS, on the free tier, which I assumed meant free.
It mostly was. The S3 bucket was not. I had not read the pricing page carefully enough to understand which parts of free tier stayed free past the first twelve months and which had usage caps the headline number didn’t make obvious. A month later there was a bill. Not large, but a surprise when I had expected nothing.
The senior developers looked at what I had built and liked it. The scope had run well past the original ask, and the AWS bill was entirely my own doing, but the thing worked: candidates could register, upload a CV, apply for a posted role, receive a confirmation email, and an employer could log into a dashboard and review applications. That is a long way from a straightforward job board.
I learned more from building past the brief than I would have from stopping at it. Not a lesson I planned on.
The S3 pricing surprise was my fault for not reading the terms. The free tier documentation is not always clear about what “free” means past the first twelve months, and some services have usage caps that the headline number doesn’t make obvious. Worth reading carefully before you put a bucket in front of anything that uploads files.