Writing

Two shared libraries, one pattern

August 20263 minC++API design

At Airbus I built two shared C++ libraries for the flight-planning platform. One handled zip files, the other polled weather data. Different problems, same root cause: teams kept writing their own version of something that should have existed once.

ZipManager

Zip functionality was scattered across the codebase. Several teams had their own implementations, and no two covered quite the same cases: one team only zipped a single file, another needed multi-file archives, a third needed to unzip incoming data. I consolidated all of it into the shared-libraries codebase and reimplemented it behind a single ZipManager class: single-file zip, multi-file zip, unzipping, and proper error handling throughout. Other teams call one interface now instead of rolling their own.

Three product teams use ZipManager now. That's one implementation instead of three, each with its own quirks and its own bugs to fix twice.

ApiPolling

The second library followed the same shape, but the base class itself isn't weather-specific. Multiple teams were each polling their own aviation weather providers (SADIS, NMS, NWS), and each had built its own bespoke implementation to do it. NWS's wasn't even part of the C++ codebase: it was a Perl script.

Worth knowing

Three providers, three different API shapes, three separate implementations to poll them. The differences that actually mattered were per-provider. Everything else (retry behavior, timeouts, how a poll result gets handed back) was duplicated logic with no reason to be.

I built ApiPolling as a generic base class: just the shared shape of polling an external API and handling the result, nothing about weather in it. Then I built weather polling for SADIS, NMS, and NWS on top of it, migrating NWS off its old Perl script and onto a proper C++ implementation in the process:

include/polling/api_polling.hpp
class ApiPolling {
public:
  virtual ~ApiPolling() = default;
  virtual Response poll() = 0;
};
 
class NwsPoller final : public ApiPolling {
public:
  Response poll() override;
};

The rewrite was also a chance to fix what NWS was actually downloading. NWS data comes as GRIB files, and the old script downloaded every GRIB available, then parsed out the ones it actually needed, paying the transfer cost for data it was about to throw away. The new implementation skips straight to requesting only the GRIBs it needs, with no download-then-discard step in between. That alone cut per-query payloads from about 2–3 GB down to 30–150 MB, depending on the data files requested:

NWS payload~60 MB
6 hours

Same fix, twice

Both libraries solved the same problem: logic duplicated across a codebase with no canonical home for it. The fix in both cases wasn't clever. It was building the shared version once and making everyone build on top of it instead of around it.