TMR · Selected work
RTDP — Making a transport data pipeline operable
I was the Lead Frontend Engineer on the Real-time Transport Data Portal: two React applications that let people configure, test, publish and monitor Queensland's transport data.
Most of the job was not drawing screens. It was deciding where complexity should live, which state an operator needed to trust, and how two products could share a foundation without becoming the same product.
- Role
- Lead Frontend Engineer
- Started
- January 2024
- Focus
- Frontend architecture and operational experience
01
The system, in plain English
The Real-time Transport Data Portal sits between organisations that hold transport data and the systems that consume it. It brings datasets in, transforms them, checks their quality, and publishes them through consistent interfaces.
The frontend had two distinct jobs: Data Exchange, where external organisations configure data sources, source feeds, transformations and quality rules; and Control Centre, the internal portal for organisation administration and availability monitoring.
02
The complexity had to go somewhere
Transport standards are built for interoperability, not for pleasant editing. In one early design, the canonical model closely followed DATEX II, the European standard used to exchange road-traffic data. A relatively simple transformation expanded into more than a thousand lines of standards-shaped JSON. The platform could process it, but that did not make it a reasonable interface for a data owner.
The frontend question was simple: were people configuring their transport data, or were we asking them to learn the exchange standard underneath it?
My position was that standards-specific complexity belonged in backend adapters. The user-facing model needed to be smaller, neutral, and explained in the language of the task. Validation needed to say what was missing in the data, not recite a schema failure.
That discussion captures how I tend to work. I do not treat backend structures as inevitable UI. I look for the boundary where technical completeness stops helping the person at the screen.
03
One page had to carry the whole story
The Source Feed Version page became the centre of the publishing workflow. A version was not just a database record. It gathered configuration assets, draft and published state, test activity, and the actions that could move the work forward.
I organised the page around three operator questions:
- 01What version am I looking at?
- 02What is connected to it?
- 03Has it been tested?
I separated metadata, the pipeline view, configuration assets, activity, quick actions, and test status into readable sections. Detail drawers let operators inspect a connected item without losing the version they were working on. Draft creation followed the real constraint of one active draft per source feed.
None of those choices was dramatic. Together, they changed the page from a collection of records into a place where an operator could understand the state of the work.
04
A spinner was not enough
Testing a source-feed version was a long-running operation. A spinner could say that something was happening, but not what had run, what had passed, or where attention was needed.
I designed and implemented a step-based test runner that exposed the pipeline stage by stage. Running, passed, and failed states stayed visible as the test progressed, and the result sat beside the configuration being reviewed rather than in a separate technical tool.
- 01Start the run
- 02Follow each stage
- 03Inspect the result
The interface did not make the operation faster. It made the waiting legible, which is often the more useful frontend job.
05
The boundaries I put into the code
Data Exchange and Control Centre had different users, permissions, and operational risks. I kept them independently deployable, but built them from one frontend workspace so the parts that should feel familiar did not drift.
- 01
Share only what is genuinely shared
Layout, UI primitives, authentication helpers, GraphQL infrastructure and administration patterns lived in common code. Routing, state and domain behaviour stayed with the application that owned them.
- 02
Let the schema own the contract
Backend GraphQL schemas generated the TypeScript types used by the applications and tests. When the API changed, the mismatch became visible during development instead of becoming a production guess.
- 03
Treat organisation context as a security boundary
Data Exchange cleared organisation-scoped MobX state when a user switched context. Routes, navigation and actions also reflected the role held in that organisation.
- 04
Keep quality close to everyday work
Storybook documented component use and frontend conventions. Vitest, accessibility checks and Playwright covered the layers where each kind of failure was easiest to catch.


06
What I left behind
By the end of the project, RTDP had a more structured operational experience for versioned configuration and pre-publication testing. Shared administration patterns, generated contracts, and documented conventions gave the two applications a common engineering language.
I cannot reduce that work to a neat performance percentage, and I would rather not manufacture one. The evidence is in the shape of the system: clearer workflow state, reusable foundations, broader test coverage, and documentation that another engineer could use without first loading the whole project into their head.
Looking back
The work I value most here is the accumulation of small, explicit decisions: visible state, honest boundaries, generated contracts, reset state, and written conventions.
None of them is a slogan. Together, they made a difficult platform easier to operate and easier for the next engineer to extend.
07
Technologies
Frontend
React, TypeScript, Vite
Data and APIs
GraphQL, Apollo Client, AWS AppSync
State and forms
MobX, React Hook Form, Zod
UI
Tailwind CSS, Radix UI
Testing and documentation
Storybook, Vitest, Testing Library, Playwright, jest-axe, Lighthouse
Platform and tooling
AWS Cognito, GitHub Actions, pnpm workspace