Interface in a header, implementation in a .cpp. The header (uart.hpp) declares the namespace and class, the public methods, with only inline accessors or templates defined in place, guarded by #pragma once or include guards. The .cpp (uart.cpp) includes that header and defines the method bodies (uart::Uart::send(...)). This keeps the compile-time dependency surface small: code using the driver includes only the header, and changing a method body recompiles just the one .cpp, not every dependent. You also minimize what the header itself includes, prefer forward declarations over pulling in heavy headers, since every header include is pasted into all dependents and slows their builds. The one exception is templates, which must be fully defined in the header because they can't be instantiated from a separate translation unit. One namespace per subsystem keeps names collision-free and self-documenting.
C++ for Embedded · Interview question
How should you split a C++ driver across files?
A strong answer
What a weak answer sounds like
You know the answer. Do you know what gets you dinged?
Pro breaks down the answer most candidates actually give to this question — and the specific reason an interviewer marks it down. It’s the difference between sounding correct and sounding senior, on all 472 questions.
From the lesson
Namespaces & Code Organization
Group related names to avoid collisions, replace C's prefix conventions, and structure firmware into headers and translation units that compile fast and read clearly.