Ali Gharehgozloo

Case study 02

PriceMate

Role

Team of four
Research & product design
PWA front-end
Backend & functions

Timeframe

2025 — 2026
Graduation project

Status

Shipped, academic
Outstanding Project
distinction, EMU

Stack

PWA · service worker
AppWrite · 11 collections
4 serverless functions
OSRM · OpenFoodFacts

An app that tells you which supermarket in your area sells a product cheapest, and whether you can eat it — with the safety answer decided by code, not by a language model.

PriceMate home screen and AI assistant screen

Context

Grocery prices in Northern Cyprus vary sharply between shops on the same street, and nothing publishes them. There is no open price feed, no chain API, and no incentive for supermarkets to make comparison easy. What exists instead is shoppers who already know that one shop is cheaper for bread and another for dairy.

So the data has to come from the people using the app. That decision cascades: crowdsourced data is sparse, uneven, and sometimes stale, and the interface has to be honest about all three without becoming useless.

My role

A team project — four of us. Between us: user research and the formal requirements work, the product design, the PWA front-end, the AppWrite backend across eleven collections and four serverless functions, the offline architecture, and the write-up.

This was a graduation project, which means it was assessed on method as much as output: QFD and House of Quality for requirements, Ishikawa diagrams for the problem analysis. Constraints none of us would have chosen, and they made the requirements sharper than instinct would have.

The problem

Two problems, and the second one is the serious one.

The first is coverage: a comparison app with thin data is worse than no app, because a wrong “cheapest” answer sends someone across town for nothing. The second is safety. Shoppers with allergies do not want a price; they want to know whether a product will hurt them, and that question has a right and a wrong answer with real consequences.

A language model reading an ingredients list will answer that question fluently every single time, including when it is wrong. That is not an edge case to handle later. It is the reason the architecture looks the way it does.

Decisions

The first one is the reason this project got a distinction, and the one I would talk about in an interview if I only had time for one.

Decision 01

The threshold check runs server-side before the model is allowed to write anything

The allergen feature has two jobs that look like one: decide whether a product is safe for this user, and explain that to them clearly. I split them, and the split is the whole design.

The decision is made server-side by deterministic code. The user's declared allergens and thresholds are evaluated against the product's ingredient and nutrition data by ordinary logic — comparisons, not inference. That produces a verdict: safe, unsafe, or unknown because the data is incomplete. Only then is a language model invoked, and it receives the verdict as a fact it must explain. It writes the sentence a person reads. It does not get a vote on the outcome.

“Unknown” is a first-class result, not a failure state. If the ingredient data is missing a field the threshold depends on, the app says so plainly rather than reasoning around the gap. An honest “I cannot tell you” is a usable answer; a confident guess is not.

The practical consequence: the safety logic is testable. I can write a case, assert a verdict, and know it holds. No prompt phrasing can change it.

Decision 02

Two caching layers, because a supermarket is a place with no signal

People use this app inside shops, and shops have concrete walls and no reliable mobile data. An app that needs a connection to compare prices does not work at the moment it is needed.

So there are two independent layers. A service worker caches the application shell, so the app opens instantly and offline. A local database cache holds the product and price data the user has actually touched, so their real shopping list is available with no network at all. The two are separate deliberately: a stale interface and stale data are different problems with different refresh rules.

OpenFoodFacts sits behind both as an online-only fallback for products nobody in the community has entered yet. It is a fallback, not a source — its coverage of local products is thin, and treating it as the primary would have made the app look authoritative in exactly the places it is weakest.

Decision 03

Scanning as the primary entry point, and a route rather than a list

Typing a product name into a search field is a bad interaction while holding a jar in one hand. Barcode scanning is the fast path in and doubles as the contribution mechanism: the same gesture that looks a product up is the one that adds a price for it, which is how a crowdsourced catalog grows without asking anyone to do data entry as a favour.

Comparison then ends in something actionable. Rather than reporting that four shops carry an item at four prices, the app routes to them with OSRM, because “cheapest” is not useful if it is twenty minutes away. The whole interface is bilingual English and Turkish, decided at wireframe stage rather than retrofitted.

What shipped

A working PWA: scan, compare, route, and an assistant that answers ingredient questions inside the safety architecture above.

PriceMate product detail and price history
Product detail and price history
PriceMate product detail and price history, second view
Price timeline across shops
Walkthrough — scanning a product, comparing prices across shops, and the allergen check returning a verdict the model did not decide.

Outcome

The project received an Outstanding Project distinction from the EMU Department of Computer Engineering. The safety architecture is what the assessment singled out, and it is the part I would defend hardest — not because it is elaborate, but because it is the correct shape for the problem and most products in this category get it wrong.

I do not have adoption numbers. It was built and assessed as a graduation project, not launched commercially, and I would rather say that than dress up a test group as users.

What I would change

I designed a system for crowdsourced data without designing for the moment there is none. The first user of a price comparison app opens it, finds nothing, and does not come back. A seeded neighbourhood — one district, prices I collected myself — would have been worth more than two more features.

I also never tested the allergen flow with someone who actually has a severe allergy. I reasoned about their needs and I reasoned carefully, but the one group whose judgement mattered most is the group I did not put in front of it.

And eleven collections is more than this product needed. Some of that structure is modelling for features that had not earned their place yet.

Next case study

Biliyo

A marketplace that changed what it sold halfway through, and an information architecture that absorbed it.