Building OurCar: Lessons in UX, State Management, and the Art of Saying No
Sharing a vehicle with family members often seems simple until the gas tank runs dry. For Mendel Greenberg, the friction of splitting fuel costs and coordinating schedules became the catalyst for a technical project: OurCar. What began as a quest to avoid "death by a thousand cuts" regarding gas bills evolved into a deep dive into cross-platform development, user experience (UX) design, and the realities of maintaining a niche application.
The Problem: Coordination Friction
The core issue was a classic coordination failure. While family members tried to refill the tank after significant trips, small errands left the tank slowly depleting. The result was an unfair burden on whoever happened to drive the car when it finally hit empty.
Greenberg's goal was to build something "better for sharing a car than a WhatsApp group." This required three primary capabilities:
- Location Tracking: Knowing where the car was parked when available.
- Status Monitoring: Tracking who had the car and when.
- Fuel Accounting: Monitoring gas usage to determine who owed a refill.
The Technical Stack
To move quickly from prototype to production, Greenberg opted for a lean stack:
- Frontend: Flutter for cross-platform reach.
- Backend: Pocketbase for a simplified backend-as-a-service experience.
- State Management: Riverpod to synchronize data across multiple pages.
- Navigation: Auto Route to handle deep-linking and complex routing.
While agentic development tools like Claude Code were used for troubleshooting and building secondary features (such as an account deletion portal), the core architecture was handled manually to ensure a deep understanding of the system.
The Challenge of "Native" Design
One of the most significant hurdles was achieving a "native feel" on both iOS and Android. Flutter provides widgets for both Material (Android) and Cupertino (iOS) design systems, but it doesn't automatically switch between them.
To solve this, Greenberg utilized the flutter_platform_widgets package, though he noted that layout idioms still differ significantly between platforms, often leading to "spaghetti code" to handle the nuances. He found that the most effective way to design the UI was to reference apps that had already mastered the cross-platform balance, specifically WhatsApp.
"If you follow standard idioms, people suddenly understand how it all works, because they’ve encountered that kind of interaction many times before."
Scope Creep and the Art of Saying No
As the app reached family members, the requests poured in: maintenance schedules, computer vision for odometer scanning, and live GPS tracking. This forced a critical decision-making process regarding "anti-features."
GPS tracking, for example, was rejected for two reasons: battery drain and privacy. While the app needed to know where the car was parked, tracking every movement would reveal private details about the users' lives. By identifying these as anti-features, the developer maintained the app's core utility without compromising user trust or device performance.
Engineering Pitfalls and Iterations
State and Performance
Early versions suffered from "jank" and synchronization issues. The developer discovered that creating "mega-widgets" caused excessive repainting, as a small state change in a parent widget triggered a redraw of all descendants. The solution was a rigorous refactoring into smaller, more manageable widgets with isolated states.
The State-Machine Approach
Initially, fuel logs and trip logs were separate, which confused users. Greenberg transitioned to a single unified timeline acting as a state machine. The logic became binary: a car can be taken if it is returned, and returned if it is taken.
Reflecting on this, he noted that a more "abstract" state machine —one where logic is independent of the data storage method—would have allowed for better unit testing and fewer bugs.
Community Perspectives: Over-Engineering vs. Utility
The project sparked a debate on Hacker News regarding the necessity of such an app. Some critics argued that a pencil and notepad or a simple PWA (Progressive Web App) would have sufficed, calling the project an example of "over-engineering."
However, others countered that the value of a dedicated app isn't just the data record, but the workflow it enforces. As one commenter noted:
"A rough UI everyone actually opens can beat a neat paper log maintained by one person... a purpose-built helper can be the workflow, not only the record of it."
Other technical suggestions included integrating OBD-II Bluetooth dongles to automate fuel and odometer readings, removing the friction of manual data entry entirely.
Conclusion: The Life Cycle of a Niche App
Ultimately, OurCar remained in "forever alpha." As the family members moved away, the immediate need for the app vanished, and the enthusiasm for further development dried up. Despite this, the project served as a comprehensive exercise in the full software development lifecycle: from identifying a pain point and prototyping to navigating the complexities of app store notarization and iterative UX design.