Radio-network software doesn't get a graceful shutdown for a refactor. Live traffic runs through it continuously, and the cost of a bad deploy isn't a rollback — it's dropped calls and degraded coverage for real people. Modernizing legacy C++ inside that constraint teaches you a different discipline than greenfield work does.
Small, provable steps beat big rewrites
The instinct with legacy code is often to rewrite the whole module cleanly. In a live telecom system, that instinct is close to reckless. Instead, the useful unit of work is the smallest change you can verify in isolation — swap one raw pointer pattern for RAII, replace one macro with a constexpr, migrate one interface to use structured bindings — each one small enough that if it's wrong, the blast radius is contained and the cause is obvious.
Tests are a contract with the next person, not a checkbox
Modernization work only stays safe if the test suite actually reflects real behavior, not just compiles. Before touching a module, the more valuable work is often improving its test coverage first — writing down what "correct" means for that code before changing how it's written. That's slower up front and much faster the moment something almost goes wrong.
Backward compatibility is a feature, not a compromise
C++17 gives you real tools — structured bindings, if constexpr, std::optional — that make code substantially clearer. The temptation is to use all of them everywhere immediately. The more sustainable path was introducing them where they reduced risk (clearer error handling, fewer manual lifetime decisions) and holding off where the surrounding code wasn't ready to be trusted with the change yet.
What this looks like day to day
In practice: pick a module, understand its actual failure modes before its ideal design, land the smallest safe improvement, verify it under realistic load, repeat. It's less dramatic than a rewrite. It's also the only version of "modernize this" that doesn't risk the thing it's meant to improve.