
Mobilauto
Ukrainian taxi dispatch software, sold since 2008. I worked across the platform from 2020 to 2022
Mobilauto is a Ukrainian company that makes taxi dispatch software, and it's been doing that since 2008. A lot of taxi services run on its software even right now, across Ukraine and beyond. It's the original brand of the platform that Express Soft sells today, and Cabinet24, the telephony and CRM product, is sold right beside it.
I worked there from 2020 to 2022 as a full stack developer, a DevOps engineer and, pretty soon, a team lead. A stretch as a senior technical consultant followed, up to 2024. This is about those years and the systems I built there.
The team I walked into
I came to Mobilauto in early 2020, right after Qberry. I was a young developer, I passed the interview with pretty good results, and I went straight onto Cabinet24, the product the company actually sold. My knowledge back then was nowhere near what it is now, and the project was a big one, so I had to figure most of it out almost immediately.
The team was tiny. It was me as the only developer, one telephony administrator and one support manager. The developer before me had already left the team, but he gave me a short knowledge transfer, and that did help a little.
What the company already had
Mobilauto had a working taxi program long before I showed up. It dated from 2008, and its server had to be installed on a Windows machine. In 2008 there was no Docker and no Kubernetes, so that was simply how server software got executed.
The phone side was the same age. The applications were Java applets, the kind that ran on keypad button phones and on the first Android handsets. One older phone client was written in C sharp. Telephony on Asterisk and text messaging were already part of the setup, and the company ran its own machines on its own premises. All of it kept taking orders while the new platform got built around it.
Express Soft was the company's answer to all of that. It runs in a browser and doesn't need a Windows machine anywhere. There's still a server behind it, of course. The new things we built went out to those taxi services gradually, to every service that agreed to move over, and plenty of them did.
Hiring
Once we got going on Cabinet24, it was pretty obvious that one developer wasn't enough. That's the point where my job stopped being only code. I started acting as a team lead, which meant screening new people and finding the ones I could actually build a great product with.
I didn't source candidates. Resumes came to me, and I was the judge of who joined. Before judging anybody I wrote the requirements document, split into must-have skills and helpful ones. The must-have half was pretty ordinary: the language, the runtime, the browser platform, the databases, the patterns, version control, containers, and how the protocols under a live system really behave. The helpful half was really just the shape of our own product, such as telephony, maps, the protocol the carriers speak and running a cluster.
The test itself was small. A frontend candidate built an application that reads Bitcoin prices and shows them. A backend candidate built the API for the same idea, one that fetches the prices, keeps every reading and lets the caller change how often it looks.
We hired two developers for Cabinet24 this way. First came a frontend developer for the move from plain HTML to React, then a backend developer. That made 3 developers, with me as the lead who still wrote code on both sides and reviewed what the other two did. On top of that I did the SRE and DevOps work, like setting up the Kubernetes cluster and making sure everything stayed up.
The corporate accounts portal
Mobilauto had a few more projects beside Cabinet24, and one of them was the corporate accounts portal.
Some context first. Taxi firms that use the company's software also serve corporate clients, and a corporate client is a business that prepays trips for its own staff. At the end of the month that business wants to know exactly what it paid for, and the portal is where it goes to see that. 3 different kinds of people sign in to it. A corporate client sees its own dashboard, its ride history and its employees, meaning who rode, how many trips they took and how much money they used. A taxi firm gets its dashboard, its clients, groups and reports, the same picture across every corporate client it has.
The firm dashboard counts closed corporate orders over whatever date range you pick. Each line has a count and a percentage: all orders, closed, finished, closed because there was no car, closed on a miscalculation, refused by the client, finished by the firm itself, and finished by another company.
The balance works as a ledger. Every top-up writes a row into the taxi firm's own transactions table, with the client, the amount, the time, a note and a mark that the portal was the source, so when an invoice gets disputed, the answer is sitting right there in the history. Each client also has a spending limit, and a firm can price each corporate client differently from the street: a markup, as a percentage or a fixed amount, separate markups for the city and out of the city, a price per distance and a minimum order price.
Then there are the statements, and pretty much every screen ends in a document somebody can send to an accountant. There are named reports for one corporate client or for all of them, a fuel check, a consolidated cashless statement, agent orders and agent cash. They export to Excel and PDF, bundled into one archive when a firm wants the whole set, and the ride history prints too. A company with several departments can have group accounts that read the ride history of every rider under them. Every person keeps their own column choices on every table, stored on the server under their name, so a table somebody set up comes back the next time they sign in. Paging, sorting and date ranges all run on the server as well.
flowchart LR
OP[Taxi firm added to the portal] --> CN[Connection opened to that firm's own dispatch database]
CN --> RD[Corporate orders read for the chosen period]
RD --> ST[Completed, refused and handed-on totals]
RD --> BA[Account balance, spending limit and price rules]
ST --> FV[Taxi firm view across all of its corporate customers]
BA --> FV
ST --> CV[Corporate customer view of its own people and its own money]
BA --> CV
FV --> EX[Statement for the accountant]
CV --> EXGateway
The gateway came out of a pretty plain problem. Many small taxi firms have no domain of their own, and some don't even have a stable IP address. A rider's phone can't reach a dispatch machine sitting in an office with no public name. The gateway is one shared public address that any such firm can route its own passengers through, and the passenger phone application goes through it for sign-in, saved addresses, ordering, ride history and the live positions of the cars on the map. A firm can move its machine behind it, and nobody has to deploy a new version of the application.
To do that, it has to know where each firm's dispatch software actually lives. It builds every address a firm could be at, from the name the firm has published and from the machines the registry knows about. Then it probes each one with a real dispatch call because the rider is waiting on the dispatch software, and a machine that's merely switched on doesn't help anybody. A worker beside the gateway holds the connections and keeps a picture of every firm in memory, and a request reads that picture and goes straight out.
The one call it guards is sign-up, because sign-up sends a one-time code to a phone by text message. The guard is IP-based with hCaptcha on top, and each firm picks one of 3 modes: off, a captcha every time, or a middle one. In the middle mode a sign-up from the firm's home country passes, a sign-up from anywhere else gets a captcha, and any address with more than 10 sign-up calls in a day gets one wherever it is.
flowchart TB
PH[Rider phone and browser] --> GW[Gateway, one public address for every small firm]
GW --> GL[Guard mode for that firm, re-read on a short cycle]
GL --> CA[Captcha on sign-up, or straight through from the firm's own country]
GW --> BL[Card check on paid orders]
GW --> MP[Picture of every firm, held in memory beside the gateway]
MP --> DM[The firm's own dispatch machine answers]
SW[Address sweep asks each candidate a real dispatch question] --> MP
RG[Registry of live firms and their settings] --> SWI kept the gateway from 2021 to 2022. Late in that stretch it got a card check on paid orders and more address-lookup providers.
The partner dispatch bridge
Some partner taxi firms run a third-party dispatch product, and orders moved both ways between them and us. The partner dispatch bridge is what carried those live orders. A partner qualifies only when both sides agree to it: our side has send rights to that partner, and the partner has receive rights from us.
When a ride finds no local taker, the bridge opens a real mirror order on every eligible partner at once. The first partner to assign a car keeps the ride, and every other mirror closes in the same second. If our own dispatcher takes the order back while those mirrors are open, they close too.
The part a rider actually feels is the price. The price the rider was quoted stands, even when another firm drives the car, so it's settled before any mirror opens. The partner prices the route first, and the bridge adds an extra charge equal to the gap between our price and theirs, with its own coefficient for each partner. After that it keeps our order up to date, writing back the assigned car, the driver, the status and the cost.
flowchart TB
OR[Order nobody local will take] --> EL[Partners where both sides agreed to that direction]
EL --> PR[Partner prices the route and the gap is added to the order]
PR --> MI[A real order opened on every eligible partner at the same time]
MI --> AS[First partner to assign a car keeps the ride]
AS --> CX[Every other copy cancelled the same second]
AS --> WB[Car, driver, status and cost written back into our own order]A partner comes as a list of machines, in the order that partner prefers. One watcher keeps a running picture of which of those machines are answering and shares it across every order in flight, so the orders don't all go knocking on the same box. Each connection walks its list until it lands on a live machine, and it keeps checking after that. A partner having a bad hour doesn't take our dispatchers down with it.
Both sides of an order can change at the same instant, so one queue per order carries every change. A car arriving from a partner and a cancellation from our own dispatcher can't half-apply on top of each other.
Two riders can also wait at the same address at the same moment, and a partner system would then read the second order as a duplicate of the first and drop it. So the bridge adds invisible special characters to the second address. For a person it renders exactly the same, and for the partner system it's a different string, so both orders stay. The marks come off before the address reaches anybody.
What I covered
Most of the company's new systems started with me: both halves of the corporate accounts portal, the Cabinet24 backend, the Cabinet24 call-back widget, the platform server, the dispatcher application, the super-admin server, the partner dispatch bridge and the gateway.
The portal was all mine, backend and frontend. The call-back widget, the one that sits on a customer's own site, was mine from start to finish too. On the Cabinet24 backend I built the parts the business actually ran on, such as the calls, the client cards, the tasks, the accounts and the telephony. We built the Cabinet24 frontend together as a team, and my part was the frame the others built inside, meaning the layout, the base table, the client card and the state under it.
On the platform server I laid the foundations first and then took on the review work: the transport everything else speaks through, the shared data layer under all of it, and how the platform is packaged and installed. On the dispatcher application I did the skeleton, how the panels dock, which transport version it speaks, and every release. On the super-admin server I set up the tables, their relations and the routes, and came back to its settings years later.
The traffic inside the dispatcher, client and driver applications runs on prpcow, my own transport library, published separately under MIT, and Mobilauto is where it went into production. Inside the dispatcher it moves the live data in real time, such as position, speed and heading, order status and driver assignment, driver availability and shift schedules.
The shape of the job changed over time. At first I was the developer everywhere. Then I started things and held the middle of them together, and more and more of the day was other people's work coming past me before it went out. The company picked up again after I'd left, and by then I'd owned too many parts of the platform to hand them on in a week, so from 2022 to 2024 I stayed with it as a senior technical consultant. The work narrowed to the parts only I knew, like handing an order from one company to another, the shared data layer, the transport itself, the gateway, the bridge or the super-admin settings.
The least forgiving industry
Of all the industries I've worked in, taxi is the least forgiving. Outages happen, everybody has had one. But if a taxi service goes down with the infrastructure it runs on, the person who has to fix it has no car to get there.
I remember a conversation with my boss about another feature. I proposed, without a second thought, an external provider that "has all been solved and covered" and that we could use as a dependency. He asked me something like this: "But what if this provider stops working tomorrow? Are we able to provide the same mission-critical functionality to our customers after that?"
That question made me really picky about third parties. I much prefer having hosting and execution control over each application, and the cluster those products ran on was mine to set up and keep up. With few third parties and control over your own servers, you can stop a problem before it turns into a much worse situation. On the other hand, that control comes with the full weight of running the system yourself: every security vulnerability, every patch, every upgrade, and every reliability problem is yours to handle, often at 3 a.m. with no one else to call.
Working at Mobilauto was really interesting, almost fascinating even, and at the same time it was a hard one. I did a lot of overtime. Some of it the work needed, but mostly I did it because I enjoyed it. The company handed me things well above what my years said I could carry and let me run them, and I'm really grateful for that trust and for all the hands-on work that came with it.
2022
In 2022 the full-scale invasion of Ukraine started. In March I left for a nearby country, and I came back in the summer. The invasion cut the company's money flows, and everybody's contracts at Mobilauto got paused. If not for that, I wouldn't have switched, but I needed work, so I started looking for a new company. That's how I got to The Parker Avery Group.
Stack
- JavaScript
- Node.js
- Express
- Sequelize
- React
- Material-UI
- Redux
- SQLite
- hCaptcha
- Prometheus
- prpcow
- Asterisk
- A2Billing
- Docker
- Kubernetes
- nginx ingress
- Let's Encrypt
Credits
- Mobilauto
- Wordmark
Related work
- Express SoftA fully upgraded and rewritten taxi dispatch platform, doing much more than before, much readier to integrate with other services and much easier to use
- Cabinet24A cloud PBX with a light CRM in one browser cabinet, rewritten and optimized into a platform that's ready to go
- PprpcowAn MIT WebSocket RPC library in TypeScript that I put into production inside a commercial taxi dispatch platform