Routes, departures, seat maps and the tickets sold against them — for bus, rail or anything else where a specific seat on a specific journey is the thing being sold.
Talk about scopeA reservation system is a concurrency problem wearing a booking form. The interesting moments are all contention: two people reaching for the last seat at once, a payment that confirms after its hold expired, an operator swapping in a smaller vehicle after tickets are sold. Systems that model a booking as a row inserted at checkout work perfectly in testing and fail on the departures that matter, because those are precisely the ones where more than one person is trying at the same time.
Between choosing a seat and completing payment, that seat cannot be sold to anyone else — and it cannot be held forever either. The hold, its expiry, and what happens when payment succeeds after the hold lapsed are the core of the system, and getting them wrong is how two people arrive with a claim to the same seat.
Seats do not exist as stock; they are generated by a route running on a date. Change the vehicle and the seat map changes under bookings that already exist. The schedule, the vehicle and the seat map are separate things bound together per departure, not one table.
Demand concentrates on popular departures, which means simultaneous attempts on the same seat are normal rather than rare. This needs real locking at the seat level, and a checkout that fails clearly and immediately rather than optimistically accepting and apologising afterwards.
Cards decline, gateways time out, and users abandon at checkout constantly. Every one of those has to release the held seat promptly and leave no partial booking behind — and a payment that confirms late, after the hold expired, needs a defined answer rather than an exception in a log.
Refund windows, partial refunds, date changes and no-shows are where the operator makes or loses money, and where customers complain. These rules change often and per operator, so they belong in configuration with an audit trail, not compiled into the booking flow.
Whoever is at the vehicle needs the current passenger list, including the booking made twenty minutes ago and the cancellation made ten minutes ago, on whatever connection exists at a bus stand. Offline-tolerant and reconciling is the requirement; a web page that needs signal is not.
A packaged product is the right answer more often than a development company usually admits, and if your problem is an ordinary one that something off the shelf already solves, we will tell you so rather than quote for it. What follows is the case for the other answer — the conditions under which building is worth what it costs.
A packaged system that mostly fits leaves a residue — the spreadsheet kept alongside it, the step done by hand every month, the field used to mean something it was not named for. When that residue is where the actual work happens, you are already maintaining custom software, just without any of the benefits of having built it.
Every business has a few processes that are genuinely its own, and those are usually the ones it competes on. Off-the-shelf software is built for the average of a sector, so it handles the ordinary parts well and the distinctive part not at all. Building is worth it exactly where that overlap fails, and rarely anywhere else.
A vulnerability in a packaged product is a vulnerability in every deployment of it, and attackers only have to find it once. A system built for one business is not on that list.