When you enter an origin and a destination in a mobility app, everything seems simple: two fields, a tap, a few moments of waiting, a list of travel options. We give little thought to that wait, and yet it’s there that the most interesting work is concentrated. Behind two points on a map lies a microcosm of systems, algorithms, and architectural choices that, in a few tenths of a second, build a reliable and useful result.
The hidden microcosm in a route calculation
Why isn't it enough just to be able to calculate a route?
Finding a route from A to B is not, in itself, complex: it’s a well-studied problem, by now “solved” by the industry. The difficulty arises under real-world conditions: large geographic areas, multiple transport modes to combine, data that changes constantly, extremely low response times. Four constraints that are manageable one at a time, but that together change the very nature of the problem. To tackle them all at once, we don’t rely on a single tool, but on multiple route-calculation algorithms (proprietary algorithms for on-demand transport, for public transport, for carpooling), skilfully fused together. For simplicity, in this case we focus on OpenTripPlanner (OTP) 2.
OTP 2 is a free, open-source route-calculation engine, maintained for over ten years by an active community and widely used in the field of route planning for local public transport. It is, in effect, the “brain” that, given an origin and a destination, calculates how to reach the destination by combining public transport, walking, cars, and other mobility services, starting from public data in standard formats and taking real-time updates into account.
The advantages of this engine are manifold:
- Integrated multimodality: public transport, walking, cycling, bike sharing, and on-demand services within a single itinerary, rather than in separate solutions.
- Multi-criteria calculation: it handles modes, schedules, transfers, and preferences together, adapting to heterogeneous constraints.
- Real time: alerts and itinerary recalculation based on delays and vehicle positions.
- Performance and scalability: fast even across very large areas.
- Open standards: native support for GTFS, GTFS-RT, NeTEx, SIRI, OSM, and GBFS, backed by an active community, reduces the risk of lock-in.
What does OTP work on?
When it receives a request, the engine works on a graph: the digital representation of the transport network, the roads, and the connections between points. And here one of the initial constraints returns — the data that changes. Schedules, lines, and sources are updated continuously: the graph cannot be static, and every change must be reflected in the system without interrupting the service.
The solution is an automated pipeline that works in the background through a process that monitors changes in the data sources. When the static scheduled data (GTFS or NeTEx) or the map and elevation information change, a build process is triggered that rebuilds the graph; the new graph is loaded into a new router and released into production in place of the previous one, without the user noticing. When, instead, the listener intercepts changes in the real-time data (GTFS-RT, SIRI, GTFS Service Alerts, or GBFS), these are integrated directly into the router.
Orchestrate, don't just connect
But an engine, however advanced, is not enough: it needs to be integrated into a broader architecture that leverages its strengths and compensates for its limitations. From the management and updating of incoming network data through to the pre-processing of requests and the post-processing of results, so as to ensure performance, reliability, and scalability even under heavy load.
The point, therefore, is not that we adopted OTP2, but how we integrated it. The most obvious choice — having the app query the routing engine directly — is simple but fragile: hard to scale, complicated to control, and poorly suited to evolution. That’s why, between the frontend and the route-calculation engine, we inserted an intermediate layer that orchestrates, controls, and optimizes every request. Origin and destination never reach the engine directly: they first pass through this layer, which checks whether a similar response already exists in the cache, routes the request to the correct router, applies the custom logic for the case (for example, favouring the fastest route or the one with fewer transfers), and optimizes the call.
The result is a shift in perspective: routing stops being a feature and becomes a shared service. It’s no longer the app that does the routing. It’s the platform.
The right technologies, in the right places
Adding an extra layer must not, however, compromise the response times the end user expects; to achieve this, you need a stack in which each technology is positioned where it generates the most value. The orchestration layer is written in Go, chosen for handling large volumes of concurrent requests with extremely low latency: this is where call handling, coordination between services, and management of custom logic take place. Alongside it, Redis powers the cache: it stores the most frequent responses and avoids querying the engine every time, cutting perceived latency and overall load. Communication between services travels over gRPC — faster and more structured than classic REST APIs, ideal for high call frequencies — with Protocol Buffers defining a common language that is compact and free of ambiguity.
Everything at a glance
Seen from above, the architecture reads as two parallel flows that meet at a single point.
The runtime flow is what happens in the tenths of a second during which the user waits: no service ever talks directly to its respective engine; instead, requests pass through a gateway up to the orchestration layer (otp-api, in Go). Here the request meets the Redis cache, the custom logic, and the supporting microservices, and only then is it routed to the correct OTP router — one per area or instance. All of this over gRPC and Protocol Buffers, to stay fast even under load.
The build flow operates in the background: a process activates every day and checks the static data sources — GTFS, NeTEx, maps, and elevation data. If one of them turns out to have changed, the otp-builder process is launched, which builds a new graph; this is then loaded into a new router. Real-time data (GTFS-RT, SIRI, and GBFS), on the other hand, follows a different path: it’s the OTP router itself that downloads it in real time and integrates it into the graph directly during execution.
The two flows touch at a single point: the new router enters production, replacing the previous one via a hot-swap, with no perceptible interruption. This is where the whole idea comes together — a system that updates itself while it keeps responding.
Where all this comes to life
Theory is one thing; it’s in the field that an architecture shows whether it really holds up.
Milano Cortina 2026: the mobility of a distributed Olympics. For the Olympic and Paralympic Winter Games, we built the mobility platform: a single digital ecosystem for accredited users (journalists, athletes, Olympic delegations) and for the general public. The challenge was unprecedented: the first “distributed Olympics,” reaching beyond the single host city to embrace a vast area across multiple regions. The graph was extended to cover the whole of North-East Italy, integrating over ten transport operators and a multimodality ranging from buses, metro, trams, and trains to private shuttles, with real-time data and dynamic management of alerts and service changes. Connecting multiple regions and making them work as a single system is exactly the problem for which an orchestrated architecture — rather than one that is merely connected — makes the difference.
The Andemo a Venezia app: a strongly multimodal ecosystem. Few territories put a mobility engine to the test like Venice, where the network isn’t made up of roads alone. We work with six operators and a variety of modes that includes buses, trams, vaporettos, trains, bicycles, e-scooters, and car sharing, all combinable within a single route. Such a heterogeneous network is the ideal proving ground for a graph that has to merge very different modes into a coherent result.
LinkingSST and cross-border travel. The latest frontier is the border. In the European LinkingSST project, we enable intermodality between Italy and Switzerland, integrating data on buses, trains, parking, and other forms of mobility: crossing an international border becomes as simple as getting around your own city.