Case study
One codebase for two product lines
Our onboard software was built for a modern computer. I ported it to an older generation of onboard computer with 512 MB of RAM and 1 GB of flash, which had been considered too small for it. That wasn't a recompile. I had to work out what the software really needed and cut the rest.
Context
Buses don't get replaced because the software moved on. An older computer that's already installed, wired and working can still be the right unit for a fleet. The question was whether our current software could run on it.
The company had two product lines on separate codebases. Porting the modern C++ stack (gcc 12) to the older computers would put one codebase on the whole fleet. It was the company's biggest technical challenge to date. The application stayed the same, with the driver touchscreen, passenger information, onboard services and the data behind them. It just had to fit in a much smaller box.
The problem
The older computer has less memory, less storage and an older Linux than the modern one. Dependencies that cost nothing on the modern unit suddenly mattered. I had to rethink startup time, display performance and recovery.
The obvious move is to keep the application as it is and squeeze the system around it. That tends to break somewhere. A runtime is missing, a library is too new, or the disk fills up the first time the device writes a log.
With 512 MB of RAM, every library has to earn its place.
Approach
I started by treating it as a size problem. I cut the application down to the screens, services and data paths a bus actually uses in service. Anything that was only there because the modern computer had room to spare got questioned.
Then I moved the older computer to TinyCore Linux and rebuilt the boot sequence, daemons and libraries around what it can handle, with stripped binaries. Where the display or startup code needed changes, I kept them below the application, so the modern computers kept behaving exactly as before.
What I built
- A port of the onboard software to the older computer, from the same C++ codebase.
- A TinyCore Linux base with a rebuilt boot sequence, daemons and libraries.
- Display and startup changes for the smaller device.
- Packaging that fits in 1 GB of flash, application and data included.
- Testing on the real older unit as well as the modern one.
Choices I made
Measure the real machine
Everything feels cheap on a fast computer. So I started by measuring what the older one actually had in memory, storage, libraries and display speed, and designed for that.
Cut first, optimise later
The cheapest dependency is the one you don't ship. Removing libraries and features did more than tuning code that shouldn't have been running anyway.
Leave the screens alone
Drivers and passengers shouldn't notice that the hardware is older. The changes went into the layer under the application, and the interface code stayed the same.
Test on the real unit
Emulators and fast laptops help, but they won't give you a full disk, a slow display or an old kernel. The real unit is the only test that counts.
Where it ended up
One codebase now runs across the entire fleet, older computers included. Old hardware didn't mean freezing the product. It meant deciding what the software needs, what it can drop and where each change belongs.
Most of the work was close to the metal, in Linux, boot, libraries, packaging, memory and display. And most of the wins came from deleting things.