Case Study: Mess Monitor — Serverless on a Student Budget

Mess Monitor started as a fix for my own shared student mess: splitting meal and grocery costs always ended in arguments and a messy spreadsheet. Today it has 10K+ downloads on Google Play, 700+ daily active users, a 4.7★ rating — and a backend that costs almost nothing to run. It also won the bdapps National Hackathon in 2022. This case study covers the architecture and the decisions that let one engineer build, ship, and keep operating a consumer app alone.

The constraint that chose the architecture

I was a student. There was no budget for an always-on server, and no time for patching one. That constraint made the decision: Flutter on the client (one codebase, Android first), and a fully serverless backend — API Gateway → Lambda (Node.js) → DynamoDB. Nothing runs when nobody is using it; cost scales from zero, and there is no server to babysit during exam week.

The domain, in one sentence

A mess (shared household) has members; members log meals and grocery expenses through the month; at month-end everyone needs one number — who owes whom how much. Reads and writes are almost always scoped to a single mess, which maps perfectly onto DynamoDB's partition model.

Single-table DynamoDB design

The whole application lives in one table, partitioned by mess:

The trade-off is the usual one: queries you didn't design keys for are painful. That's the discipline single-table design demands — model the access patterns first, then the data.

The settlement algorithm

Month-end settlement computes each member's meal rate share (total grocery spend ÷ total meals × member's meals) against what they actually paid, then reduces the debt graph to a minimal set of transfers — so five housemates get three payment instructions, not ten. It runs in a Lambda on demand; the result is stored back as a settlement item so history survives membership changes.

Living with cold starts

Lambda cold starts are real but manageable at this scale: handlers are small, dependencies pruned, and connection setup is reused across warm invocations. DynamoDB needs no connection pool — one of the quiet reasons it pairs better with Lambda than a relational database does for an app like this. The p95 the user feels is dominated by mobile network latency, not the backend.

What it costs

With 700+ daily actives, the backend sits almost entirely inside the AWS free tier: Lambda invocations and DynamoDB on-demand capacity for a meal-logging workload are tiny. The practical bill is effectively the price of a coffee — which is the whole reason the app could survive its student-project phase and keep running. The same workload on the smallest always-on EC2 instance would have cost more every single month than this does in a year.

What 10K+ downloads taught me

Frequently asked questions

What architecture does Mess Monitor use?

A Flutter app talking to a fully serverless AWS backend: API Gateway in front of Node.js Lambda functions with DynamoDB for storage. Nothing runs when nobody is using it, so cost scales from zero and there is no server to maintain.

How is the DynamoDB single-table design structured?

One table partitioned by mess (household) ID, with typed sort-key prefixes for members, meals, and expenses (like MEAL#date#member). A month's dashboard is a single partition query with a sort-key range, and running totals are kept with atomic counter updates on write.

How much does it cost to run a serverless app with 700+ daily users?

Almost nothing for this workload: Lambda invocations and on-demand DynamoDB capacity for meal logging sit largely within the AWS free tier. The smallest always-on server would cost more per month than this backend does per year.

← All posts