The problem
Two clients, two very different clocks. The traffic authority of São Paulo needed a web console to monitor and configure traffic-light controllers across the city, where a lost message means an intersection running the wrong plan. A rail operator needed to know where its freight wagons were along thousands of kilometres of track, with positions arriving from a legacy system through SOAP and no map to put them on.
What I did
I was one of a handful of developers on both projects. On the traffic system I worked on the backend that received controller telemetry and pushed configuration, and on the Angular console. On the rail project I built the geoprocessing that turned raw positions into points along the track and the map interface that showed them. On both I was the person who introduced automated testing to the team.
How I solved it
Messaging that tolerates loss. Controllers published status over MQTT and the backend subscribed with per-intersection topics. Commands were idempotent and carried a version, so a repeated or late message could never move a controller to an older plan. State was stored in MongoDB because each controller model exposed a different shape of telemetry.
Geoprocessing on the server. Wagon positions came as latitude and longitude from a SOAP service. I snapped them to the nearest track segment and computed the kilometre marker, so operators saw “wagon 4821 at km 212 on the Belo Horizonte line” rather than a dot in a field. The map used Leaflet with clustered markers so a thousand wagons stayed readable.
Tests before features. Both teams had shipped without tests. I set up RSpec and Capybara on the Rails side, CucumberJS and Karma on the Angular side, and wrote the first scenarios with the client’s own vocabulary so they could read them. New work followed the pattern.
What was hard
The SOAP integration. The rail operator’s WSDL was large, partially documented and occasionally wrong about field types. Writing a thin client with contract tests against recorded responses was the only way to make changes safely.
Real time in the browser. Keeping the traffic console current without hammering the backend meant a careful mix of pushed updates for alarms and polling for everything else.
The result
The traffic console went into use by the city’s operators. The rail map replaced a spreadsheet-based process for locating wagons. Both codebases had test suites that ran on Jenkins, which the teams kept after the engagements ended.
Where to see it
Both systems are internal to their clients. The approaches to idempotent MQTT commands and track snapping are described above and I am happy to go deeper in conversation.