As a data engineer, I spend my days designing clean, optimized data structures. But at home, I face a much tougher crowd: my family.
We manage our shared finances together to optimize our budget, and because of where we live and work, we have to do this in several different currencies (like USD, EUR, CZK, and UAH)
Like any developer, I first tried to find a ready-made app to solve this. But I ran into a classic problem: they were either bloated with a million features we didn’t care about, or they were missing the exact features we actually needed.
So, we did what any desperate family does: we opened a Google Sheet. We tracked our money there for a while, not because it was perfect, but because it helped us figure out what we actually needed from a real application. It was our "living schema design" before I wrote a single line of code.
In this article, I want to show you how I looked at this problem from two sides—as a frustrated user who just wants to log expenses, and as a data engineer obsessed with clean database design. Here is the story of how I built our custom home accounting server.
Part 1: Django, a Star Schema, and the Framework Battle
By 2025, I was ready to replace our Google Sheet. My main programming language is Python, so I had three realistic choices: FastAPI, Flask, or Django.
- FastAPI is the cool kid on the block for high-speed APIs, but we didn’t expect millions of requests (unless my family suddenly grew by a factor of a million). We also needed a friendly web UI, which FastAPI isn't naturally built for.
- I had just used Flask for my previous project, but I wanted to challenge myself and learn something new.
- Django felt like an old friend I hadn't seen in years. It has amazing built-in tools (like the admin panel and great translation support), and using it was the perfect way to refresh my skills and grow as a developer.
The Database: Why a "Star Schema" Actually Makes Sense
As a data engineer, I didn’t want a messy database. I decided to use a Star Schema, which is usually used in big data warehouses, but is actually perfect for transaction bookkeeping.
Think of it like this: the central table is the star of the show, and everything else just adds context.
-
The Fact Table (
Transaction): This is where all the action happens . I used a simple sign rule: income is recorded as a positive number, and expenses are recorded as negative numbers. It's simple, mathematically clean, and makes calculations incredibly easy. -
The Dimension Tables: These surround the transaction table and hold details like
User,Family,Currency,Account, andCategory.
By late 2025, I had built the database, a simple web UI, an API, tests, and manuals. I packaged it into two Django apps—members and transactions—and hosted it locally on an Ubuntu server running Gunicorn. It worked beautifully, but I was not done yet.
Part 2: 2026 Evolution — Going Serverless and Banishing "Boring Manuals"
In 2026, I looked at the 10-page text manuals I had written for my family. Let's be honest: nobody—especially your family—wants to read a dry technical guide just to find out how to log a credit card transfer.
I decided to do two things: make the app Serverless and replace the boring manuals with a friendly, conversational AI assistant. To do this, I built a third Django app called assistant.
But I didn't want a generic chatbot that just parroted generic answers. I wanted a smart assistant that knew exactly what the user wanted. So, I designed it to instantly classify user messages into four distinct intents:
- GENERAL: For casual financial chats and friendly greetings.
- DOCUMENTATION (RAG): If a user asks "How do I add a new currency?", the AI runs a search (Retrieval-Augmented Generation) across our markdown manuals to give a step-by-step answer.
- DATA (Text-to-SQL): This is the magic part. If you ask, "How much did I spend on food this month?", the AI translates your natural language question into a clean SQL query, runs it against the database, and gives you the exact answer.
- COMMAND: I wanted to keep the web interface clean and simple. Instead of cluttering the UI with extra buttons, you can just tell the AI, "Clear our chat history," and it triggers the command in the background.
Part 3: Under the Hood of a Low-Latency, Safe AI Pipeline
AI can be slow and expensive if you aren't careful. To make sure my family didn't have to wait 10 seconds for a response, I built a hybrid pipeline using two different cloud providers: Groq and Azure OpenAI.
The Groq + Azure OpenAI Combo
-
Groq is incredibly fast and cheap, but they don't offer text-embedding models. I used Groq's models for all our text tasks:
gpt-oss-20b(for lightning-fast intent classification) andgpt-oss-120b(for generating replies and SQL queries). -
Azure OpenAI handles our embeddings. I deployed their
text-embedding-3-smallmodel and did something sneaky: I configured it to generate embeddings with only 512 dimensions instead of the standard size.
By reducing the dimensions to 512, the embeddings generated much faster, and the size of my vector database shrank dramatically. Best of all, we didn't notice any drop in search quality. The result? The AI assistant responds instantly, without any visible lag.
Easy Documentation Ingestion
To keep the AI's knowledge base updated, I wrote a custom Django management command. Whenever I edit our markdown spec files, I just run this command.
# Ingest general information
python family_acc/manage.py ingest_docs documentation/general_info.md --category general
It automatically parses the markdown layout, slices it into clean chunks, generates 512-dimension vectors, and uploads them to our hosted PostgreSQL database (with the pgvector extension) on neon.tech. The whole process takes only a few seconds.
Chaining the AI's Hands (SQL Security)
Letting an AI write raw SQL queries against your personal database is like letting a toddler run around with a permanent marker—they mean well, but they might delete something important.
To prevent SQL injections or accidental data deletion, the database connection used by the Text-to-SQL engine is strictly restricted to read-only SELECT statements. The AI can read our data to answer questions, but it can never edit, modify, or delete a single row of transactions.
Part 4: Smooth Sailing to Google Cloud Run
After testing everything locally in Docker containers, it was time to put it in the cloud. Thanks to my experience with Azure, AWS, and Google Cloud, deploying the container to Google Cloud Run was surprisingly smooth.
The app immediately connected to our cloud database on neon.tech and the Groq/Azure AI APIs without a single hiccup, working perfectly on the very first try.
Keeping Secrets Safe
In a production deployment, you never want to expose API keys or database passwords. Here is how I set up secure DevOps:
- I stored all environment secrets (like
DATABASE_URLand API keys) in Google Cloud Secret Manager. - I granted the Compute Engine service account secure access rights to read these secrets at runtime .
- I forced Django to enforce secure HTTPS for all connections in production.
My Big Takeaways as a Developer
Building this application taught me that the best coding projects are the ones that solve real-world problems in your own life. Here is what I learned:
- Keep it simple first: Starting with a Google Sheet allowed me to build a schema that matches actual, real-world family habits, rather than guessing what database tables we might need.
- AI must be secure and fast: High-quality AI doesn't have to be slow. By splitting tasks between Groq (for speed) and Azure (for cheap embeddings), and by securing Text-to-SQL with read-only rules, you get an assistant that is both incredibly fast and safe.
- The power of Serverless: Deploying on Cloud Run showed me how easy it is to manage containerized Django apps without worrying about maintaining raw virtual machines.
This project is a perfect proof of concept showing that you don't need a massive team to build a secure, modern, AI-powered system in the cloud. You just need a clear plan, a bit of data engineering, and a family that is tired of manual spreadsheets.
Top comments (0)