Try five live demosdemo.gravixar.com

Work

A booking app for a women-⁠only driving school, with its rules built in

The school's booking rules now live in the software instead of in someone's memory.
schematicWhole course, or no slotA slot only appears when one instructor is free for every lesson day of the course.

At a glance

Women-⁠only driving school, PakistanLive since July 2026

First version
Built and deployed the same day
Under 18
Guardian record required, closed routes until 18
On this page

What was going wrong

A driving school does not book appointments, it books courses: a run of lesson days in a row at the same hour, taught by one instructor who has to be free for all of them.

On top of that sit rules that are not optional. A minimum age. A guardian on file for anyone under 18, plus a limit on where those students may drive until they turn 18. Seasonal opening hours. A closed day each week. Payment by mobile wallet or bank transfer, with a person checking the transfer went through, because that is how the students actually pay.

And a privacy request that runs against every driving-school website: do not put the instructors' names on the public site. In an off-the-shelf booking tool, nearly every one of those is a workaround, a free-text note, or a rule that lives in someone's head and gets broken on a busy afternoon.

What I did

I built the school's rules into the app itself instead of squeezing the school into a template. Date of birth is the one thing that decides whether a student can enroll and whether a guardian is needed, so nobody can skip the under-18 rule by editing a field.

Each of the three instructors has a weekly shift pattern and their time off, and a slot only appears when one free instructor covers every day of the course, not just the first. Summer and winter hours and the weekly closed day are part of the same check.

Students pay by mobile wallet or bank transfer, enter the transaction number, and an admin confirms it before the enrollment is final. The payment screen only shows methods that are fully set up, so a placeholder account number can never reach a student. Instructors mark each lesson done or missed and only ever see their own lessons. The first lesson starts the course, and the last one completes it.

What changed

The first version went live on 12 July 2026, built and deployed the same day, then improved for weeks while the school used it. Rules that used to live in someone's memory are now enforced by the app: an under-18 student cannot be booked onto an open route, and a course slot cannot be offered unless it can be honored end to end.

The launch did not go smoothly. Nobody could sign in after the first deploy, for three reasons stacked on top of each other, and a database change reached the live system in the wrong order and briefly broke pages for signed-in users. Both are written up below, with the fixes and the standing rule that came out of them.

The booking logic is the part I would carry into the next build of this shape: a business that sells a course rather than a single slot has the same availability problem, whatever it teaches.

The long version

What's running today

An installable web app, built for phones first. Students register, enroll, book a course and track their lessons on a phone, because a phone is the only device most of them will ever open it on. Admins confirm payments and assign instructors. Instructors log lessons.

The interesting part is not the screens, it is what the app refuses to allow.

Eligibility comes from date of birth, not from a form field. A student enters their date of birth once. The minimum age of 12 is worked out from it. So is whether a guardian record is needed. There is no age field to get wrong, and no age field to lie in.

Under 18 is a different product, not a warning label. A student under 18 cannot enroll without a guardian name, guardian ID and guardian phone on file, and their bookings are locked to fixed closed training routes until they turn 18. The limit lifts on the birthday. Nobody has to remember to flip it, and nobody can flip it early.

A slot is only offered if the whole course can be honored. A booking is a course: a set number of lesson days in a row at one fixed hour. Three instructors teach it. Each has a weekly shift pattern plus separate time off, and the check behind the slot picker asks the harder question: is there one working instructor, not already booked, who covers every single lesson day of this course? If not, the slot is not shown. Seasonal hours are part of the same check, 9 to 6 in summer and 9 to 5 in winter, and Sundays are closed.

Payment is manual on purpose. The student pays by mobile wallet or bank transfer, enters the transaction reference, and an admin confirms it before the enrollment is final. The payment screen only shows the methods that are set up, and the server rejects a payment against a method that is not, so a half-set-up option can never show a student a placeholder account number.

Lessons drive the course, not a status dropdown. Four roles share one permission map. An instructor marks a lesson done or missed, with an undo, and can only touch their own lessons. The first lesson record starts the course. The last one completes it. The course state is a result of work that happened, not something an admin types.

Decisions I'd defend

Derive, don't ask. Age, the guardian requirement and the route limit all come from one date. Every rule worked out this way is a rule that cannot drift out of step with the record it depends on.

Answer the expensive availability question once. Offering a slot that only works for the first three of eight lesson days would push the failure onto a student who already paid, and onto an admin who then has to phone somebody. Asking the whole-course question up front is more work for the database and less work for people.

Build for the way people actually pay. Cards are not how these students pay. A transaction reference plus a person checking it is, so that is what shipped, with the check as a real step in the app rather than a note in a spreadsheet. Only showing the payment methods that are set up is the small guardrail that makes the manual flow safe.

The instructors are not on the public site. Every driving-school template leads with instructor photos and names. The school asked for the opposite, and for this school, in this place, that request outranks the pattern. A student sees only the instructor assigned to her. This one is worth saying plainly: a rule from the client beats a convention from the industry.

What broke, and the policy that came out of it

On launch day nobody could sign in, and the real cause was hiding under two smaller ones.

Nobody could sign in after the first deploy. Each fix revealed the next problem, which is the part that makes this worth writing down.

First, the sign-in secret had never been set on the server. That alone produced roughly 547 errors in about an hour. Setting it moved the failure rather than ending it. Second, the key the app uses to prove itself to the sign-in service was out of date, so sign-in kept failing on details that looked correct. Replacing it got sign-in working, and that is when the real cause showed up: the live database was completely empty, because its tables had never been created. The moment sign-in got far enough to succeed, it crashed on a table that did not exist.

Two real problems sat on top of the actual one. Neither was the cause, and fixing either on its own looked like no progress at all. The fix was to create the tables on the live database and test sign-in from start to finish on the live system, not on my own machine, where the tables happen to exist.

A code change went live before the database was ready for it. The change only added things and was safe on its own, but the order was wrong: the live app started asking for a table that did not exist yet, which meant a short window of errors for signed-in users until the database caught up.

That one produced the standing policy, and it is not negotiable now: database changes go live first, then the code. Even changes that only add something. The database leads, the deploy follows.

What's next

The school is using it, and the next changes are theirs to direct. The reusable piece is the availability engine, the part that answers whether one person can cover a run of days in a row at a fixed hour. Any business that sells a course rather than a single slot has that same problem, and it is the one generic booking tools quietly do not solve.

If you run something where the rules are the product, and every tool you have tried made you work around them, tell me about it.

Bring me a real operations problem. I will show you the system running before you sign anything.

The Ops Leak Audit is the fixed-price first step: I count the hours your tools cost your team, then price the fix. Rather talk first? Book a 30-minute call. If it is not a fit, you still leave with notes you can use.

Portrait of Qamar
Qamar, the person you would work with