This is a submission for the Hacktoberfest Weekend Challenge: Build for a Friend
What I Built
My friend Vini travels constantly — a work trip one week, a long weekend somewhere the next, flights and hotels and eating-out-while-away stacked back to back. Her words: "I never know if I'm actually overspending or if it just looks scary because it's all bunched up." She's not bad with money. Travel spending is just impossible to feel in control of — it comes in spikes, and every budgeting app she's tried flashes those spikes red like an alarm, which is exactly the opposite of what she needs when she gets home tired from a trip.
So I built her Budget Buddy — a tiny web app with one job: let her log what she spends in ten seconds, and once a week, give her a warm, honest, judgment-free check-in on how it went, written by an AI that's been explicitly told never to shame her.
It's not a budgeting app in the traditional sense. There are no limits, no red alerts, no "you're over budget" banners. You log an expense, you pick a category, you're done. Then when you're ready — not on a forced schedule — you ask for a check-in, and Gemma reads your last 7 days of spending and responds like a supportive friend would: acknowledging the hard stuff (a travel week is just... expensive, and that's okay), calling out one or two categories gently worth a second look, and giving three concrete, kind, doable tips. No jargon. No guilt.
I also built it so more than one friend can use the same deployed app — there's a lightweight profile switcher, so onboarding another friend onto the same instance is a click, not a new deploy. I ended up adding my own spending to it too (solo-builder chaos: too much food delivery, too many dev-tool subscriptions), partly to stress-test it with real data before handing it to Vini, and partly because once it existed I genuinely wanted my own weekly check-in. That's why the live app has two profiles you can toggle between: Vini (the friend this was built for) and Yash (me, the builder, who ended up living in it too).
Demo
Live app: https://budget-buddy-a2ng.onrender.com
Two seeded profiles are waiting for you — no sign-up, nothing to type. Pick Vini (the friend this was built for — a frequent traveler, so her weeks are spiky: flights, hotels, eating out on the road) or Yash (me, the builder — heavy on food delivery and dev-tool subscriptions), and use the friend-switcher in the top-right to toggle between them. Each has ~5 weeks of realistic history and four real weekly check-ins already generated, so there's something to look at immediately instead of a blank slate. The screenshots below are from my own (Yash) profile — it felt more honest to show the tool actually being lived-in by the person who built it.
1. Onboarding — no password, just a name:
A friend's name is the only "auth" this needs. It's saved locally and scopes everything to them.
2. Logging a spend — ten seconds, no judgment:
Pick a category, type an amount, optionally a note, done. The newly added entry shows up immediately in a "Just added" feed so there's instant confirmation — small thing, but it's the difference between an app that feels responsive and one that feels like it ate your input.
3. The dashboard — spending as information, not a verdict:
A donut chart broken down by category, with totals and percentages, so you can see the shape of your week without a single color-coded warning.
4. The check-in — the actual point of the app:
This is a real Gemma response from my own seeded data, not a canned one:
"It looks like your big rent payment took up most of the space this week, which is totally normal for a monthly cycle! I'm really proud of you for staying on top of your tracking, because that's how we build confidence."
Worth watching: Food, Subscriptions
Tips:
- Take a quick peek at your apps to see if there are any subscriptions you don't use as much as you used to.
- Maybe try making your favorite coffee or tea at home a few extra times a week to keep some extra cash in your pocket.
- Try setting a small "fun budget" for the week so you can enjoy your snacks and treats without any worry at all.
5. History — watching the tone evolve, not just the numbers:
Every past check-in is kept, so instead of a spreadsheet of regret, you get a running log of a friend checking in on you, week over week.
Code
Repo: https://github.com/yash872/budget-buddy
The two files I'd point a reviewer to first:
-
lib/gemma.ts— the prompt and the parser. I deliberately did not ask Gemma for JSON. For a tone-sensitive, empathetic response, forcing structured output tends to flatten the writing into something robotic. Instead I asked for a plain-text format withSUMMARY:/WATCH:/TIPS:markers, and parse those with regex. If the model ever drifts from the format, the parser falls back to storing the raw text as the summary rather than throwing — a demo (or a real user) should never see a 500 because a model answered in slightly the wrong shape. -
app/api/checkin/route.ts— wraps the actual Gemma call in a named Sentry span (gemma.generateCheckin,op: "ai.generate"), so the AI call shows up as its own traced unit of work in Sentry rather than disappearing into a generic request trace.
How I Built It
Stack: Next.js 16 (App Router, Turbopack) + TypeScript, Tailwind CSS v4, MongoDB Atlas (plain mongodb driver — two flat collections, no ORM needed for this), Gemma via Google AI Studio (@google/genai), Sentry for error + AI-call tracing, deployed on Render's free tier via a render.yaml blueprint.
Data model is deliberately minimal:
-
expenses:{ profileName, category, amount, date, note?, createdAt } -
checkins:{ profileName, periodStart, periodEnd, totalSpend, categoryBreakdown, aiSummary, topCategories, tips, model, generatedAt }
No auth system at all — a friend's name is saved in localStorage and sent with every API call to scope their data. For a weekend build meant to serve one real friend (and a demo audience), real auth would have been effort spent in the wrong place. The lib/aggregate.ts "group spending by category" logic is shared between the chart and the Gemma prompt builder, so the dashboard and the AI are always looking at the same numbers.
Three things I'd call out as the actual craft of this build, beyond "wire up an API":
- The prompt is written for a feeling, not a feature. The system instruction explicitly says "you are never judgmental, never use shame, guilt, or scare tactics, and you never use financial jargon. Speak like a supportive friend, not an advisor." That line is doing the real work of the whole app. I rewrote it probably eight times during testing because Gemma's default financial-advice instincts (even unprompted) kept creeping toward "you spent $X on Y, consider reducing this" — which is exactly the tone Vini needed not to hear after a travel week.
-
A UI bug that mattered more than it looked like it should. Early on, the "What should we call you?" input text was nearly invisible — a
prefers-color-scheme: darkmedia query was flipping the text color without flipping the card background underneath it, so dark-mode users got white-on-white text. It's a one-line CSS bug, but on an app whose entire pitch is "feel welcomed, not interrogated," an input you can't even read the moment you open the app undercuts the whole premise. Fixed by pinningcolor-scheme: lightand giving inputs explicit, deliberate colors instead of trusting the cascade. -
The friend-switcher. Supporting multiple people on one deployed instance (instead of one deploy per friend) meant building a real "known profiles" list in
localStorage, not just a single active name — so switching back to a friend you've already set up on this device is one click, with the active profile clearly highlighted and an inline "add a new friend" flow for onboarding the next one.
I seeded ~5 weeks of realistic expense history for the two live profiles — Vini, whose spending comes in travel spikes (flights, hotels, taxis, and eating out while away, with one calm week at home where rent lands), and myself, a solo builder with heavy food delivery and dev-tool subscriptions — and generated every weekly check-in through the real Gemma API, not mocked, so what you see live is actually representative of what the model produces. That included riding out the occasional transient 500 from the API, which is exactly why the Gemma call is wrapped in retry-with-backoff: one of Vini's four check-ins only succeeded on a retry while I was seeding.
Why Does Open Innovation Matter?
A friend's spending history is one of the most personal data sets they have — more honest than a diary, probably (I know, because mine is in there too). The idea that a budgeting coach could be built on a model whose weights, architecture, and training approach are published and inspectable — rather than a black box behind someone else's API that could change behavior, pricing, or availability overnight — matters a lot for something this intimate.
Using an open model like Gemma means the exact prompt and the exact behavior it produces are something I can audit, version, and reason about, instead of trusting an opaque system to "do the right thing" with a friend's financial life. If I wanted to, I could self-host this model entirely, run it against a different provider with the same weights, or hand the whole prompt to someone else to inspect and improve — none of that is possible with a closed system. For a tool whose entire value proposition is trust ("tell me the truth about my spending, kindly"), being able to point at exactly how that trust is implemented, rather than asking someone to take it on faith, feels like the right foundation to build on.
Prize Categories
Entering:
- Gemma (featured) — the core AI, generating every weekly check-in
-
Render (featured) — hosting, free tier, via
render.yaml -
MongoDB Atlas (partner) —
expensesandcheckinscollections -
Sentry Agent Tracing (partner) — the Gemma call is wrapped in a named Sentry span (
gemma.generateCheckin) so it shows up as its own traced AI operation, not a generic request
Built solo over a weekend for a friend who deserved a kinder way to look at her own spending — and, turns out, so did I. 🌱



Top comments (0)