The Produce Problem

I spent five years at Market 32, first as a cashier, then as a supervisor. Long enough to watch the same scene play out on repeat: a customer walks up to self-checkout with a bag of apples or a bundle of kale, and the machine has no idea what to do with them.

Barcoded items are simple. Scan and go. Produce breaks the whole flow. The customer has to find their item in a list of a few hundred things, squint at the tiny PLU sticker to find the code, or just guess. Half the time they get it wrong or give up. The help light comes on, a cashier walks over, and the entire point of self-checkout, moving people through faster, disappears in real time.

I worked at Market 32 throughout college, so I watched that happen hundreds of times before the idea to fix it ever showed up. It wasn’t a hypothetical problem I read about. It was a problem I stood next to, on shift, for years. The idea only clicked later in college, right around the time I was falling in love with problem solving and computer science. Watching the same broken interaction for the hundredth time turned into “I could actually build something better than this,” with no dollar sign attached to the thought yet.

Bringing It to Life

The idea itself was simple to describe even if it wasn’t as simple to build. Put a produce item on a scale, a camera identifies what it is, the weight gets captured, and it’s added to the cart. No PLU codes, no scrolling, no squinting at a small sticker.

I gathered a couple of friends to help build the vision. I brought in Steven Lee to own model training, and Bisher Lpriza to design and 3D print the kiosk enclosure, because neither was something I could do well on my own. I handled everything else myself, the backend, the device software, the machine learning pipeline, and the work of wiring it all together. Steven’s job was making the model stop confusing a pear for an apple. Bisher’s was building something that could hold a camera, a scale, and a display steady enough not to fall apart the first time someone used it. Mine was doing that and making sure all three pieces actually talked to each other.

The build also changed as I changed. By the later half of it, I was a few months into my role as a data engineer for the first time, and I started noticing myself applying the same discipline to Salis that I was learning at work: being more deliberate about data models, thinking about what happens on a retry or a network failure instead of assuming the happy path, writing things so they’d still make sense to me months later. Salis in month two looked like a student project. Salis by the end looked like something written by someone who had started taking the job seriously.

And it worked.

Not as a demo trick, but as a running system, an edge device doing real-time detection, a backend that could actually hold multiple stores and devices, an ML pipeline that kept improving from real usage. That part I’m not going to undersell.

Salis bench prototype: Raspberry Pi, Dymo scale, camera, and the kiosk UI mid-session

Let the Market Decide

Here’s the part that’s less comfortable to write.

I talked to plenty of people while I was building, professors, alumni, other engineers, and I got encouragement and interest. What I didn’t get was signal, because none of them could actually tell me if a grocery store would buy this. I did eventually talk to the people who could. I started cold calling independent grocers in Connecticut, Massachusetts, and New Jersey. I made a couple dozen calls, but only after the prototype was nearly finished. I didn’t get through to many decision makers, just a handful. And the few I did reach mostly said the same thing: they didn’t really feel a pain point around self-checkout and produce. It wasn’t even enough of a pattern to call it a real no. But it wasn’t what I wanted to hear after months of committing to the project and building this vision in my head, and it was the first time I’d ever cold called anyone or sat with that kind of rejection. Instead of trying a different angle, bigger chains, different store sizes, I just stopped. Three months without a single commit.

When I came back, I wasn’t building anymore. I was cleaning up. Writing docs, prepping the whole thing to be open-sourced, eventually sitting down to write this. I came back to close the project out, not to keep building it.

I did validate that the technology could work. I never really validated that anyone wanted to buy it. Calling in after the build was already done isn’t market validation, it’s a formality I got to too late and dropped too soon.

Engineering Decisions I’d Make Again

The business lesson is the one that stuck with me, but it doesn’t mean the engineering wasn’t worthwhile. Building Salis taught me as much about software as it did about startups, and there are a few technical decisions I’d still make exactly the same way today.

The one I’m proudest of is a script I called the invisible labeler. Vision models make mistakes, the camera sees a pear and calls it an apple, and normally fixing that means someone manually reviewing and relabeling thousands of images. Instead, the script watches for one specific pattern: the model auto-adds the wrong item, the customer deletes it, and within 45 seconds manually searches for and adds the right one. That sequence is itself a label. The script catches it automatically and feeds the original image back into training data with the customer’s correction attached, no one labeling anything on purpose. The model gets smarter every time it’s wrong, as a side effect of someone just trying to check out.

Quick, slightly embarrassing aside: Writing this section is when I realized the invisible labeler never actually made it into the main branch. It’s sitting on a branch that never got merged, which is a fitting little metaphor for this whole project. I’m leaving it there instead of quietly fixing history. If you’re curious, here’s the branch.

One of the less obvious problems was figuring out what to do when the kiosk had to do something slow. Capturing a checkout image means writing a file locally, hashing it, saving a database row, and uploading it to S3, and that last step is the slow, unreliable one, a network call to a service that isn’t mine. Instead of making the kiosk sit there waiting on it, that route does everything local and fast, kicks the S3 upload off as a background task with its own retry logic, and returns immediately. The kiosk never notices if S3 is having a bad day. I didn’t reach for that pattern everywhere, most of the backend is small, fast, local database calls where it wouldn’t have changed anything, but it was the right call here, the one place a slow third-party call could have left someone standing at a kiosk waiting on nothing.

The USB scale was its own kind of debugging nightmare. I was using a library called hidapi to talk to it, and on Linux, hidapi has a known bug. Asking it to open a device by vendor and product ID can silently fail. No error, no exception, nothing in the logs. The scale is plugged in, powered on, working fine, and the software just acts like it isn’t there. I spent longer than I’d like to admit assuming the bug was somewhere in my own code before I figured out it wasn’t. The fix was to stop trusting the library and talk to the hardware directly. I opened the raw device file myself, used a lower-level tool to find the right one by matching vendor and product ID against whatever’s actually plugged in, then read the raw bytes straight off it. That meant decoding the scale’s protocol by hand, which byte holds the weight, which bit flips if the reading is negative, whether the unit is grams or tenths of an ounce. Small, unglamorous work, but it’s the difference between a scale that works and one that mysteriously doesn’t, for reasons no error message will ever tell you.

The Lesson

The lesson isn’t that the idea was bad. It might still be a good one. The lesson is that I built it backwards. Talk to the people who’d actually pay first, understand what they need, confirm they’ll buy it, and only then build. I did all of that in reverse.

That’s not a lesson I just know now, it’s one I operate by. Before I wrote a line of code for Bayin, I was on the phone cold calling home care agencies, asking what was actually broken for them. I signed my first client before I built anything custom. Validate first, build second, every time.

Salis isn’t a project I regret. It’s the reason every project I’ve started since has begun with a conversation instead of a commit.


Code: github.com/mirajjara/Salis

P.S. There’s a Steve Jobs clip that captures this philosophy well. Start with the customer experience and work backward to the technology.