App // Scope without collapse

Garaje

The operating system for car culture

The operating system for car culture: six front ends on one shared backend, built in eight months by one person on the technical side.

Year:
2025–present
Role:
Frontend Software Engineer, Founding Engineering Team
Org:
Garaje LLC
Garaje on three screens at sunset: the web app’s car services on a desktop, the consumer site on a laptop, and the business site on a tablet, with the mobile app on the phones in front
Front ends on one shared backend
6

Garaje monorepo, August 2026

Backend tables behind all of them
53

Garaje monorepo, August 2026

Test files, weighted to pure business logic
621

Garaje monorepo, August 2026

Reviewed pull requests in eight months
~360

Garaje monorepo, August 2026

First vendor approved
23 Jun 2026

Medium, “First in”, 23 June 2026

One product, six front doors

01

Garaje is where car culture happens online. You keep a garage of your vehicles and document builds on them, join clubs, find and run meets, book parking and services from verified shops, and buy and sell cars and parts. Businesses get a workspace to manage listings, bookings, events and a subscription.

The breadth is the product bet, not scope creep. We are not trying to win feature-for-feature against the best events tool or the best marketplace. We are betting that one place holding all of an enthusiast's activity beats five best-in-class tools they have to stitch together themselves. Your vehicle is the same object whether you are showing it at a meet, listing it for sale, or logging a build on it.

That amounted to six front ends over one shared backend of 53 tables. I own design and engineering. My co-founders own business and operations.

How one person holds it

02

One shared backend library, consumed by every front end including React Native. The conventional alternative, an API layer with each client owning its own data access, would have killed this. Every feature gets built twice and the business rules drift apart between them. With one person on the technical side, that is not a tax, it is a wall.

So ticket pricing, capacity math and validation were written and tested once. Porting events to mobile became purely a UI exercise. The schema generates its own types, so a field I add to a table is type-checked inside a native screen with no hand-maintained contract to fall out of sync.

The release process is the other half. Every flagged surface ships staff-enabled in production and publicly invisible, so I develop against real data and real edge cases while the public sees nothing. The marketplace lived in that state for months; when it launched, the launch was a toggle in an admin console rather than a deploy. More to the point, turning something off at 11pm is also a toggle, not a revert and a build and a deploy.

The cost is real. Install is slow, and the main app is pinned a major version behind the marketing sites because upgrading the largest surface is a project rather than a chore. I would make the same call again.

Legal to show, illegal to book

03

The naive marketplace listing has one switch: live or not. That model is wrong here, and the reason is specific. To advertise a business location you need a local business permit. To take payment you need a tax registration certificate. Different documents, different agencies, obtained at different times.

Collapsing both into one boolean forces a bad choice. Block legitimate businesses until tax registration completes, and the marketplace is empty during exactly the period you are trying to fill it. Let anyone take payments, and you put both the shop and the platform in a bad position.

So bookability became its own computed decision, and it returns a reason rather than a boolean. “Booking unavailable” with no explanation is a dead end for the buyer and a support ticket for us. A free listing says contact the host directly. An unverified one says the host is not accepting online bookings yet. There is always a next action.

The first version checked whether a tax registration document existed on the workspace. That is spoofable: a partner uploads the file and tags its own type, so any PDF can claim to be a tax certificate. Taking money on the strength of a self-tagged upload is not acceptable, so the check now requires the document and an admin-verified workspace. The note I left in the code is the entire reasoning: presence alone is spoofable.

A listing can now be discoverable, credible and complete while its owner's registration is still in progress, with an honest explanation where the booking button would be.

The gate

04

Events are the feature closest to why car culture exists, and the interesting half happens at a folding table with bad signal. An organizer is standing at an entrance with a phone and a queue of cars behind them.

Pre-registered arrivals are the easy case. Walk-ins are where the design work is: no account, not making one in a queue, and often paying a different price than the pre-sale tier. So walk-ins create attendance records with no user attached, priced at a door rate carried on the ticket type. The whole point of early-bird pricing is that the door costs more.

Walk-ins also brought the one genuinely sensitive part of the product. Organizers wanted a plate number and an ID reference for cars entering their venue, which is reasonable safety practice and also personal data collected from people who never signed up for anything. It is readable only by that event's organizers, never rendered on a public surface, and never included in the payload that draws attendee lists for other guests. Check-in verifies the caller against the event instead of trusting an ID passed from the client, because a mutation that accepts who I am as an argument is not authentication, it is a suggestion.

Entry is a single transition, in then out, not a running tally of re-entries. The richer model is easy to build and useless at a real gate, where nobody is reconciling a four-entry timeline for one attendee at 9pm.

What I got wrong

05

We renamed “partner” to “business” across the product. I audited with repository-wide searches, concluded I had found everything, and shipped. Strings assembled from fragments survived in places no pattern reached, and users saw the old word where I had confidently declared the code clean.

A piece of navigation chrome decided what to render from cached workspace state. That state survives logout, so a signed-out visitor could briefly see a control meant for signed-in partners.

A richer vehicles table replaced an older cars table. I moved the garage experience across and missed that the booking flow's vehicle picker still read the old one, so a user could add a car in their garage and then not find it when booking a service. Parking and EV charging still read the legacy source. That is a tracked divergence rather than a discovered surprise, but it is unfixed, and I would rather write that here than claim the migration was clean.

The worst one had no error at all: every class name in the mobile app silently stopped doing anything. The cause was two copies of React Native in the bundle graph. The styling library patched one, my screens imported the other, and both sides worked exactly as designed while producing nothing.

Those are one mistake wearing four sets of clothes. I trusted a proxy for the thing instead of the thing: a grep result for complete coverage, cached data for being signed in, a migrated feature for a migrated data model, one copy of a package for the only copy. What I do differently now is verify at the layer where the truth actually lives: inspect the built bundle rather than the source, ask the auth system rather than the cache, enumerate the readers of a table rather than the features that motivated it.

The brief

01

Garaje is where car culture happens online. You keep a garage of your vehicles and document builds on them, join clubs, find and run meets, book parking and services from verified shops, and buy and sell cars and parts.

The breadth is the product bet, not scope creep. We are not trying to win feature-for-feature against the best events tool or the best marketplace.

Does one place holding all of an enthusiast's activity beat five best-in-class tools they have to stitch together themselves?

Constraints

Framing

02

Six front ends over one shared backend of 53 tables. I own design and engineering; my co-founders own business and operations.

The conventional alternative — an API layer with each client owning its own data access — would have killed this. Every feature gets built twice and the business rules drift apart between them. With one person on the technical side, that is not a tax, it is a wall.

// Your vehicle is the same object everywhere.

  1. Keep a garageYour vehicles, and the builds logged on them
  2. Find a shopVerified services, parking and EV charging
  3. Run a meetTickets, capacity, and a gate that works
  4. Book and payWhere the business's registration allows it
  5. Buy and sellCars and parts, on the same account

What we were betting on

  • One place beats five best-in-class tools you stitch together yourself.
  • Your vehicle is the same object at a meet, in a listing, and in a build log.
  • Breadth is the product, so the schema has to be shared rather than copied.
  • A marketplace with nothing in it fails long before tax registration clears.
  • Turning a feature off at 11pm has to be as cheap as turning it on.

Interface

03

One shared backend library, consumed by every front end including React Native. Ticket pricing, capacity math and validation were written and tested once, so porting events to mobile became purely a UI exercise.

The schema generates its own types, so a field I add to a table is type-checked inside a native screen with no hand-maintained contract to fall out of sync.

Principles

Design system

03b

Colour

  • Action orange
  • Ink navy
  • App ground
  • Surface
  • Card

Build

04

One shared backend library of 53 tables, consumed by six front ends. The schema generates its own types, so the contract between a table and a native screen is checked rather than maintained.

Every flagged surface ships staff-enabled in production and publicly invisible, so I develop against real data and real edge cases while the public sees nothing.

  1. Consumer webNext.jsBusiness webNext.jsMobile appExpo / React Native
  2. Shared backend libraryPricing, capacity, validation
  3. DatabaseConvex, 53 tablesIdentityClerkPaymentsStripeMapsMapbox

Checklist

Tables

  • workspace
  • listing
  • ticket_type
  • attendance

Screens

Built with

  • TypeScript
  • Next.js
  • React
  • Expo
  • React Native
  • Convex
  • Clerk
  • Stripe
  • Sanity
  • Mapbox
  • Tailwind CSS
  • NativeWind
  • Nx
  • Vitest
  • Playwright

More work