DEV Community

Bishnu Saha
Bishnu Saha

Posted on

I built a personal expense tracker because I kept lying to myself about money

I'm bad with money. Not "forgot to save" bad — "spent ₹4,000 on food delivery in
one week and convinced myself it was fine" bad.

I tried spreadsheets. I tried apps. Nothing stuck. Either too complex, too ugly,
or needed a subscription for features that should be free.

So I built Spendly. A simple, fast expense tracker that actually fits how I think
about money.

What it does

  • Log expenses in under 10 seconds
  • Organise by category with custom icons and colours
  • Track a monthly budget and see exactly where you're over
  • Set savings goals with a visual progress ring
  • Recurring expenses that auto-log themselves daily
  • Income sources (freelance, salary, side projects)
  • Dark mode, because obviously

It's live at spendly.it.com — free, no credit card.

The stack

  • Frontend: React + Vite, deployed on Vercel
  • Backend: Express + Prisma, deployed on Render (free tier)
  • Database: Supabase (PostgreSQL)

Boring and correct. The free tier combo of Render + Vercel + Supabase means
$0/month to run.

The bugs that taught me things

The timezone nightmare

I built a "salary cycle" feature — expenses reset on whatever day your salary lands,
not on the 1st of the month. Smart idea. Terrible execution the first time.

My getCycleRange() helper was mixing local time and UTC. The client (IST, UTC+5:30)
would compute cycleStart and send it as an ISO string. The server, running UTC,
would parse it and quietly shift into the previous month. Result: empty expense lists
in production, everything working perfectly in local dev.

Fix: use UTC methods everywhere, consistently.

Top comments (0)