Branimir Karadžić's manifesto for "Orthodox C++" argues that the smaller, cleaner language hiding inside C++ is the one worth writing. It is a philosophy of restraint, born from decades of watching language features age badly.
There is a particular kind of wisdom that only arrives after you have made the same mistake several times across several decades. Branimir Karadžić, the author of the bgfx rendering library, has distilled exactly that sort of wisdom into a short document called Orthodox C++, and its central claim cuts against most of what the C++ standards committee has spent the last fifteen years building. The argument is simple to state and uncomfortable to accept: most of what we call Modern C++ is not an improvement, and the disciplined programmer should treat the language as a minimal extension of C rather than a sprawling toolkit of abstractions waiting to be deployed.
Karadžić is candid about where this conviction comes from. He and his peers were, in his words, the modern-at-the-time C++ hipsters of the late 1990s, evangelizing the newest features and urging everyone else to adopt them. What changed was not the language but the experience of living with it. Features that seemed elegant in isolation proved costly in aggregate. Run-time type information, exceptions, and the iostreams library all earned their place on his list of things to avoid, not because they fail to work, but because the price they extract is rarely visible at the moment you choose to use them. His prediction has the quality of a dare: if you think this is nonsense, wait a few more years and you will hate Modern C++ too.

The thesis is restraint, not nostalgia
It would be easy to misread Orthodox C++ as a reactionary plea to write C and pretend the last thirty years did not happen. That is not the argument. The document opens with a quotation from Bjarne Stroustrup, the language's creator, conceding that within C++ there is a much smaller and cleaner language struggling to get out. Orthodox C++ is an attempt to name that smaller language and to commit to it deliberately. The promise is that code written within these limits will be easier to understand, simpler to maintain, and compilable by older toolchains, which matters enormously when your software has to build across the fragmented reality of multiple platforms and compiler vintages.
The practical guidance follows from this single goal. Prefer C-like C++ when the problem does not demand more. Use the C runtime headers like <stdio.h> and <math.h> rather than their C++ wrappers. Reach for printf style formatting instead of streams. Avoid anything in the standard template library that allocates memory unless you have genuinely stopped caring about where your memory comes from, a caveat that reveals just how central manual memory control is to the audience Karadžić is writing for. These are not arbitrary prohibitions. Each one removes a layer of machinery that sits between the programmer and a clear mental model of what the machine actually does.
The case against exceptions, examined honestly
The most developed section of the argument concerns exception handling, and it deserves attention because it is where the reasoning is most concrete. Exceptions are singled out as the one C++ feature that requires substantial support from a complex runtime, and the only one that imposes a cost even on code that never throws. That cost appears as hidden instructions at object construction and destruction, at the entry and exit of try blocks, and, more insidiously, as a constraint on what the optimizer is permitted to assume. The compiler must preserve the ability to unwind the stack at almost any point, and that defensiveness limits the transformations it can safely apply.
Karadžić adds a second criticism that is harder to dismiss. Exception specifications are not enforced at compile time, which means the language gives you a mechanism for propagating errors without giving you any guarantee that you handled the ones that matter. You inherit the runtime overhead without the compile-time assurance that would justify it. And because so much C++ ultimately calls down into C libraries that signal failure through return codes, mixing the two styles produces a real schism in how a codebase reasons about errors. A function that throws and a function that returns an error code do not compose cleanly, and large systems end up paying for the impedance mismatch in awkward wrapper layers.
Whether you accept this fully depends on your domain. For game engines and latency-sensitive rendering code, where predictable performance and tight control over the machine are the entire point, the argument is close to decisive. For an application where developer productivity outweighs a few nanoseconds, the calculus is different. The value of the document is not that it settles the debate but that it states the trade-off plainly enough to be argued with.
A theory of when features become safe
Perhaps the most quietly radical idea in the piece is its rule of thumb for adopting new standards. Karadžić proposes that a feature from C++year becomes safe to use selectively only when the current year reaches C++year plus five. If the standard is C++11, then 2016 is roughly when you can start trusting it. The reasoning is grounded in the lag between when a standard is ratified and when compilers, operating system distributions, and the surrounding ecosystem actually implement it well. He points to constexpr, which arrived in C++11 but only became genuinely usable in C++14, as evidence that the first version of a feature is often a rough draft.
This introduces a memorable bit of vocabulary. Adopting a standard before the ecosystem has caught up is, he suggests, a symptom of Resume Driven Development, the practice of choosing technologies for how they look on a CV rather than for whether they serve the people who must use your code. For open source maintainers the warning is sharper still: if your project requires a toolchain that most potential adopters do not yet have, you have not built something others can use. The revision history shows the philosophy applying its own rule to itself. As of January 2025, the Orthodox C++ committee, such as it is, approved selective use of C++20, which by the five-year heuristic had finally aged into acceptability.
Modules and the cost of the new
The most recent addition to the document, from October 2025, takes aim at C++ modules, and it functions as a case study in the broader skepticism. Modules promise faster builds and cleaner dependency boundaries, but Karadžić tallies the costs without flinching. You may need to rewrite and refactor existing code. You lose portability, because module binary files outside of MSVC do not travel between toolchains, which means you still have to ship header files anyway. The build setup grows more complicated, and only the newest toolchain versions work at all, with Apple's support still listed as partial. Against this ledger of disadvantages he places the advantages for the ordinary working developer, and the entry he writes there is a single word: nothing.
That conclusion is deliberately provocative, and on a long enough timeline it may not hold. But the analytical move underneath it is sound and worth carrying into any technology decision. Before adopting something because it is newer, account for the full migration cost, the portability you surrender, and the maintenance burden you assume, then ask honestly what concrete benefit you receive in return. The new thing has to earn its place against the working thing it replaces.
Counter-perspectives worth holding
It would be dishonest to present Orthodox C++ as the final word. The modern features it dismisses were not invented capriciously. Smart pointers like unique_ptr encode ownership in the type system and have prevented an enormous quantity of memory bugs. Move semantics, RAII, and std::optional let programmers express intent that older C++ left implicit and error-prone. Ranges and concepts, the headline features of C++20, exist precisely to tame the metaprogramming complexity that Karadžić rightly warns against. A blanket rejection of Modern C++ risks discarding tools that genuinely reduce defects, particularly in domains less performance-obsessed than game development.
There is also a selection effect in the list of exemplars he cites: DOOM 3 BFG, dear imgui, The-Forge, and Qt built without RTTI and exceptions. These are systems where control over memory and predictable performance dominate every other concern. The philosophy fits them because it was forged in their world. A web service, a data pipeline, or a research codebase operates under different pressures, and the disciplines that serve a rendering engine may simply cost more than they return elsewhere.
What survives even the strongest counter-argument is the underlying disposition. Orthodox C++ is less a fixed rulebook than a habit of mind, a refusal to use a feature merely because it exists, paired with a demand that every abstraction justify its cost in terms a future maintainer would recognize. The specific prohibitions will keep shifting as the committee's own revision history demonstrates. The deeper lesson, that complexity is a liability to be spent carefully rather than a virtue to be accumulated, is the part that ages well. For anyone weighing the kit of tools to bring to a long-lived codebase, that is the idea worth keeping. The full document, along with its growing list of related manifestos like Sane C++ and the Orthodoxy Clang plugin that enforces these rules mechanically, remains the best place to argue with it directly.

Comments
Please log in or register to join the discussion