Skip to content
HN On Hacker News ↗

OurCar: What I Learned Making an App for my Family | Mendel Greenberg

▲ 117 points 79 comments by chabad360 3w ago HN discussion ↗

Pangram verdict · v3.3

We believe that this document is fully human-written

0 %

AI likelihood · overall

Human
100% human-written 0% AI-generated
SEGMENTS · HUMAN 5 of 5
SEGMENTS · AI 0 of 5
WORD COUNT 1,995
PEAK AI % 1% · §4
Analyzed
May 7
backend: pangram/v3.3
Segments scanned
5 windows
avg 399 words each
Distribution
100 / 0%
human / AI fraction
Verdict
Human
Pangram v3.3

Article text · 1,995 words · 5 segments analyzed

Human AI-generated
§1 Human · 0%

TL;DR: I made an app to share a car with my family using flutter. It was a very interesting experience.Necessity is the Mother of InventionWe were sitting around the kitchen table trying to make a plan for splitting the gas bill. This was an ongoing problem, and me joining the family had only made it worse. There needed to be a way to determine who needed to pay for filling the car, because otherwise, whoever got it when the tank was empty got left with the bill, and that was just plain unfair. To be clear, we all tried to make sure to fill up when we used a significant amount of fuel. But if you drive the car to the store and back, it doesn’t use a lot of gas, and you don’t feel like it needs a fill, but eventually the tank is empty. It’s a death by a thousand cuts.The first suggestion thrown around was from my sister-in-law. One would refill however many kilometers you drove. One of the views on the car’s display says how many Km are remaining until the tank is empty, which is based on the average efficiency of the current trip. The obvious difficulty with using this information, is that you can’t go the gas station and ask them to fill up 100Km of petrol in your tank, you can only ask for liters, so that plan was a bit problematic.I then realized that you could take the trip odometer reading combined with the trip efficiency reading to calculate exactly how much gas was used on that trip. However, no one else was quite as excited at the prospect of doing that math every time, so a different solution was needed. We settled on everyone filling up the car each time they used it, which —while it kinda worked— was just very annoying if you just took the car for a small errand.I’d been itching to build an app for a while, and just needed the right idea to get on it. Suddenly, here is a problem waiting to be solved, on a silver platter! There were other issues we were having with sharing the car: finding out who had it; where they parked; scheduling conflicts; so the empty gas tank was the straw that broke the camel’s back. I figured I could put together something that could solve most of the issues we faced without adding friction, and began immediately working on a prototype.

§2 Human · 0%

The Birth of a ProductI defined the scope for this project as “An app that’s better for sharing a car than a WhatsApp group”. This meant it needed to be able to: Know the car’s location (only when it was available, it was no one else’s business where I took it). Know the status of the car, so we could know who had it and when (very helpful when chasing down tickets). Keep track of the gas tank and who used how much (so we could know how much any of us needed to fill up). There are obviously some things needed beyond this, but all of them are in service to these goals, so I didn’t see the need to write them down. I looked to see if there were any other apps that could do this kind of thing, but at the time, I found nothing. There were some apps that do something similar for businesses, allowing employees to easily track usage of a company car, or allows companies to manage a fleet. But there was nothing I could find that was all that good for our use case, a few friends or family that want to share a car.The stack I picked for this project was very simple, Pocketbase and Flutter (I later added Riverpod for state-management and Auto Route for navigation and routing, but more on that later). I chose these because I had tried both of them in previous projects, and they worked perfectly fine for what I wanted to do, so I figured they would work just fine for this as well. This was correct.NoteAt the time that I started the project (around a year ago, at the time of writing), agentic development was in its infancy, so I didn’t see it worthwhile to rely on those tools to do building, but they were indispensable for troubleshooting and research. Later, once Claude Code was a much more useful tool, I started trying out agentic development, and found it mildly useful (in recent months I’ve started using it a lot more).I worked on the initial prototype over the course of a week (pretty much full-time I think, I was between jobs at the time), and had gotten together something that was a few basic pages: A main page that showed basic details about the car: current location, odometer, estimated tank level, and a button to mark the car as taken.

§3 Human · 0%

A list of all the trips the car had taken A form to track returning the car (gets the location, odometer, and trip efficiency) A list of the fuel fill ups A form to fill out when filling gas (gets the amount and price) A form to notify other users if someone wished to take out the car at a certain time. Those last three got built a little later. This core stayed pretty much unchanged. A few pages were added to cover some other things: User management, car settings, some other small menus, and later, the list of fill-ups and the trips was combined into a unified timeline (more on that later). But overall, it remains this simple.On the backend side, I added a hook to Pocketbase to allow sending the aforementioned push notifications. Later down the line, I would take advantage of this capability to add a bunch of other small features. Suffice to say, going with Pocketbase was a very good decision for this project.There was the obvious number of bugs and warts that a prototype application has, but it was cool, promising, and had product-market fit (the holy trinity as far as I’m concerned), so now I just needed to get it onto their phones. I decided to upload it to the various app stores, and use the beta testing features to distribute the app. I opted for this route, because (I thought) it was much simpler than going around and messing with everyone’s phones. This was not exactly an enjoyable process.Needing to figure out the mess that is Apple’s iOS notarization was incredibly annoying, and later I gave in and paired my wife’s phone to my developer account just to simplify this process. Publishing to Google’s Play Store is simpler, but not significantly. Fortunately, most of that was a one-time setup issue, and deployment tends to be much more straight-forward now (I still don’t understand Apple’s need to review beta applications). Ultimately, once I had uploaded it, I just needed to ask everyone for their email, and then they could download the app.During this process I needed to actually decide a name for the project, because I didn’t want to be stuck with an appbundle name (com.mendelgreenberg.<something goes here>) that sucked.

§4 Human · 1%

When I created the folder I named it “gastrax” which is perhaps a bit a clever, but not a very good name, and my wife rightfully insisted that we pick something better. She sat with ChatGPT for a bit and after throwing around way too many names (some of which already existed), we came up with OurCar, which is undoubtedly much cuter. So now that I had a suitable name, I could actually upload and share it with everyone.I immediately started getting feedback.Defining Scope, Or: Saying NoI got ideas and complaints: “Can it track the maintenance schedule?” “It’s too annoying to type in. Can I just take a picture of the dashboard, and it’ll scan it?” “It should do GPS tracking so you don’t have to put in the numbers” “How do we split gas?” “You should be able to make scheduled trips” “How do I use it?” Etc. I found myself needing to decide between what I wanted to work on and what needed improvement. Worse, I needed to choose what not to do. I had enough of a vision of what needed to be built that most of the feedback was already on my list. But still, there were ideas that I needed to say no to, either because it didn’t fit within the scope, or because I felt it was an anti-feature: something that —while it seemed useful— actually made the app worse.A good example of this was GPS tracking. The idea is that the app tracks your location throughout the trip, and can use that (combined with the car’s advertised efficiency) to know how many miles were driven, and how much gas was used. Plus, you get a nice visualization in the app after the fact. The problem with this idea was two-fold: I couldn’t find a way to implement this that wouldn’t use a ton of battery (live location tracking is very battery-heavy), and worse, it would violate everyone’s privacy. All the details of the car and its trips are shared: who took the vehicle, when, and for how long. If I added where they took it, that would instantly allow everyone to know when my brother-in-law went on a date. All we actually needed to know is where they parked the car when it was finally available (we can use GPS for that). So that was a feature that was going to stay out.

§5 Human · 0%

On the other hand, there were complaints and requests that were totally valid (like taking a picture and using computer vision to read the odometer and gas meter), but I realized that they usually stemmed from the user experience (UX) being too clunky. Most of the time, this was because despite the user interface (UI) using the right widgets on iOS or on Android, the app wasn’t natural to use on either platform, making it somewhat confusing. I figured that if I could fix this up, everyone would find it easier to use, and would stop asking for workarounds.The Design of Everyday AppsWhen it comes to look and feel, the philosophy that I quickly adopted was to attempt to be as “native” as possible. Now, Flutter is not the native GUI framework on either platform, but the framework provides widgets that are very close to, if not exactly, matching the native toolkits. In practice, this philosophy meant that my app was not going to have its own style and feel, but would instead get out of your way, and feel like it was integrated into the platform. Additionally, I wanted the app to be very quick, intuitive, and easy to use.There were various challenges I had in achieving this goal, first and foremost: I am not a designer by any means. I would like to imagine that I have some taste in this area, but certainly when starting from scratch I’m out of my depth. So often, I build a UI that is dense, hard to navigate, and very clunky, but still I’m scratching my head about what to improve. Admittedly, it seems practice is the key here, as over time I’ve found I’m slowly starting to get a better sense for what are good ways to do things.One of the first issues I encountered (I know I’m not the only one) is that Flutter provides no built-in method to use the correct widgets for each platform automatically. It seems the accepted method is to build to separate views for each page: one for Material, one for Cupertino (Material is the name of the design system for Android, Cupertino is the name of Flutter’s implementation of Apple’s design). I found this really not ideal, and in fact it would have been a significantly larger pain-point if not for Flutter Platform Widgets: a package that provides adaptive versions of (almost) all widgets.