- Use `std::unique_ptr` and `std::shared_ptr` exclusively — eliminate raw `new`/`delete` to prevent use-after-free and double-free bugs.
- Use `container.at(i)` instead of `container[i]` for bounds-checked access — or use `std::span` with explicit size to enforce bounds.
- Prefer `std::string` and `std::vector` over C-style arrays and `char*` — they manage memory automatically and prevent buffer overflows.
- Use `std::string_view` for read-only string parameters — it avoids copies without risking dangling pointers when used within scope.
- Compile with `-fsanitize=address,undefined` in CI to catch memory errors, signed overflow, and null dereferences.
- Avoid `reinterpret_cast` and C-style casts — use `static_cast`, `dynamic_cast`, or `std::bit_cast` (C++20) for type-safe conversions.
- Use `std::array<T, N>` instead of C-style arrays for stack-allocated fixed-size buffers — it provides `.size()`, `.at()`, and iterator support.
- Never store references or raw pointers to elements inside `std::vector` — reallocation invalidates them. Use indices or `std::list` for stable references.
- Use `constexpr` and `static_assert` to validate security-critical sizes and alignments at compile time.