TLAHuynh Van Gioi Em

Chili by TLA

Personal finance app, in beta with 10 users: wallets, budgets, credit-card statements, installments and loans, with an AI assistant that turns a sentence or a screenshot into a transaction.

Role
Solo full-stack developer · product, code and operations
Period
Jun 2026 – Present
Stack
React 19Express 5Prisma 7PostgreSQLTanStack QueryReact Router 7ZodVitePlaywrightDocker
Links
chili.tlait.online ↗

At a glance

  • 3months from empty repository to production
  • 31releases in the first three weeks
  • ~3,300automated tests, unit and end-to-end
  • Took the product from an empty repository to production in about three months, then shipped 31 releases in its first three weeks, working from a written roadmap of small end-to-end slices.
  • Designed it around one rule: anything that changes money goes through the service layer. The web app, the AI assistant and scheduled jobs are all just callers, so none of them can skip a business rule or reach another user's data.
  • Made the AI assistant safe to trust with money: it can only call the app's own services, never chooses whose account it acts on, runs each action once even when retried, and models are compared on a 76-case test set rather than by feel.
  • Modelled the parts of personal finance most apps skip: credit-card statement cycles, installment plans, loans with repayments and a monthly cash-flow forecast, with exact decimal money math and product decisions written down with their reasoning.
  • Run it like a team product: about 3,300 automated tests including end-to-end journeys on a real database, versioned releases deployed to my own server with backups, and Claude Code working inside architecture rules I set and review.

The problem

Most finance apps stop at "income minus expense". Real money in Vietnam is messier: a salary in a bank account, daily spending in cash and e-wallets, a credit card on its own statement cycle, a phone on 0% installments, a loan paid every month. Chili keeps all of it in one place, with every balance exact.

It is in beta at chili.tlait.online with a Vietnamese UI, and 10 beta users already use it for their own money. I built it alone: from an empty repository to production in about three months, then 31 releases in the first three weeks.

Chili dashboard showing total balance across four wallets, monthly income and expense, this month's cash-flow forecast, and budget progress
Dashboard: wallet balances, the month's cash-flow forecast and budget progress.

Scope

  • Wallets of every kind, including credit cards with statement cycles and installments.
  • Transactions, monthly budgets, saving goals, and debts and loans with repayment schedules.
  • A cash-flow forecast for the month, and monthly and yearly reports.
  • An AI assistant that turns a sentence or a screenshot into a transaction.

Architecture

A pnpm + Turborepo monorepo. web and api both import shared, never each other.

packages/shared   Zod schemas · types · enums · error codes   (imports nobody)
       ▲                         ▲
apps/web                    apps/api
React 19 + Vite             Express 5
React Router 7              Controller → Service → Repository
TanStack Query              Prisma 7 → PostgreSQL

Each API area (wallets, transactions, budgets, …) has a controller for HTTP, a service for the business rules, and a repository, the only layer that touches Prisma. Production is three Docker containers on one VPS: Caddy, the API and PostgreSQL.

Chili transactions list with filters by type, wallet, category and month, and totals for the filtered range
Transactions: filter by type, wallet, category and period, with totals for the filter.

Key technical decisions

The service layer is the center of the app

The web app, the AI assistant and scheduled jobs all call the same service methods, and every method checks that the data belongs to the user. So the AI gets no special path to the database: it is just another caller.

AI as a translator, not a back door

The AI's tools are generated from the shared Zod schemas, so its input passes the same checks as the forms. The user id comes from the session, never from the model, and each action runs once even if a request is retried. A screenshot is only read into a draft; nothing is saved until the user confirms.

The model is an open one I host myself with Ollama, so no user's finances go to a third-party AI service. I picked it by its score on 76 Vietnamese test inputs, such as "ăn trưa 50k tiền mặt" (lunch, 50k, cash), not by a few chats that went well.

One schema, checked on both sides

The same Zod schemas validate on the client and again on the server, so forms, API and AI tools cannot drift apart. Errors share one shape too, so the UI can show exactly which rule was broken.

Money and time are never approximate

Money is stored as Decimal(19, 4) and sent as strings, never as JavaScript floats. Dates are resolved in Asia/Ho_Chi_Minh, so a card statement or a budget month never shifts because the server runs in UTC.

Chili budgets page for October 2026 with three budgets, their spent amounts, remaining amounts and progress bars
Budgets: one month at a time, each covering several categories.

Each rule is tested once, at the lowest level that proves it

Unit tests cover business rules and components. Integration tests run services against a real PostgreSQL for rollbacks, races and locks. Playwright covers only what needs the browser, the API and the database together, such as balances, transfers and a full month of use.

Chili reports page comparing income and expense by month with a donut chart of spending by category
Reports: income and expense by month, against the previous period, and spending by category.

Shipping it like a team product

Working alone, nobody else catches what I forget, so the process is written down:

  • Small, finished slices. Each feature ships complete, with a changelog entry.
  • Decisions on paper. Choices worth remembering are written down with the reasoning.
  • Checked releases. CI runs types, lint, more than 3,000 unit tests, the build and end-to-end tests on a real database. The database is backed up every night.
  • AI with rules. I work with Claude Code inside architecture and testing rules I wrote, and I review every change.

From Pocketly to Chili

Chili is a rewrite. The first version, Pocketly (Next.js, Server Actions, Prisma, early 2026), proved the finance logic, but its business rules were mixed into the request handling, so nothing besides the UI could safely change money. Chili moved the rules into a separate API built around the service layer, with room for the AI assistant planned from the start.

← All projects