- Use RAII to guarantee cleanup on all exit paths — destructors run automatically whether a function returns normally or throws.
- Throw exceptions for errors that cannot be handled locally; catch by `const` reference (`catch (const std::exception& e)`).
- Prefer `std::optional<T>` for expected-absent values and `std::variant<T, Error>` for recoverable errors without exceptions.
- Mark functions `noexcept` when they are guaranteed not to throw — this enables compiler optimizations and clearer contracts.
- Write exception-safe code at the basic guarantee level (no leaks, valid state) at minimum — aim for the strong guarantee (rollback on failure) for critical operations.
- Use `std::unique_ptr` and `std::shared_ptr` as RAII wrappers for heap-allocated objects — never use raw `new`/`delete` in application code.
- Prefer `std::error_code` and `std::system_error` for OS and I/O errors that callers are expected to handle programmatically.
- Use constructor initialization lists and ensure constructors either fully initialize or throw — never leave objects in half-constructed states.