Recommended Free Tools
Building Mach, a C++20 web framework, taught Asaf Dahan four practical lessons: a simple API takes deliberate engineering, abstractions need clear ownership boundaries, plausible code still needs adversarial tests, and a worthwhile feature may be better deferred than rushed into a release. These are lessons from one project, not rules proven optimal for every framework.
1. A simple C++ API takes deliberate work
For Mach, Dahan wanted handlers to accept useful inputs without imposing an arbitrary parameter order. The framework’s examples include handlers with no arguments, a request body, a context, or both a body and context in either order. Rather than require users to memorize one convention, Mach inspects handler signatures at compile time, then binds incoming data and invokes the selected handler at runtime. The convenience at the call site depends on machinery behind it.
As an Amazon Associate I earn from qualifying purchases.
The same principle shaped request validation. In Dahan’s account, a request type provides a validate() method, identifies fields with member pointers, and declares rules such as minimum or maximum length. Mach validates the body and returns a 400 response with errors when validation fails. This is an implementation choice in Mach, not a capability to assume of C++ frameworks generally.
The design question is not simply whether an API can hide complexity. It is whether hiding that complexity removes a real burden for the framework’s users—and whether the resulting implementation is worth maintaining. In Mach’s handler example, avoiding a needless ordering rule was the intended payoff.
#1 Best Overall
2. Put abstractions and object lifetimes in the right places
Use indirection where its cost fits
Dahan used PImpl for AppBuilder to separate its public interface from its implementation. Its implementation uses a unique_ptr, which introduces a heap allocation. He accepted that cost during application setup, while being more cautious about adding it to an object created for every request. This is a contextual trade-off, not a measured performance result.
The useful distinction is where an object is created and how often that path runs. An allocation accepted during setup may be a different decision from one added to a high-frequency request path. Dahan’s account does not provide benchmarks comparing alternatives, so it supports a design consideration, not a claim about the size of the cost.
Make ownership and borrowing explicit
Dahan describes learning to distinguish storing an object, borrowing it, transferring ownership, and sharing its lifetime. He used unique_ptr extensively and shared_ptr where shared lifetime made sense, including asynchronous operations. He says he finished Mach without writing an explicit new or delete. That is his experience with this project, not a claim that manual allocation is always wrong.
Build on infrastructure when it lets the project focus
Mach used Boost.Asio and Boost.Beast for asynchronous networking and HTTP, and spdlog for logging. Dahan’s rationale was to concentrate his effort on Mach’s architecture and behavior rather than recreate substantial infrastructure. That choice illustrates a boundary: a framework author can spend effort on the behavior that makes the framework distinct while relying on existing components for other needs.
3. Test the cases that make plausible code fail
A router may need to reconsider an earlier choice
Dahan’s route example registers /a/{param}/c and /a/b/d, then receives /a/b/c. A router that tries the static b branch first follows the second route’s shape, but reaches a dead end at the final segment. The parameterized route would match: b can be the value of {param}.
Mach’s traversal was changed to backtrack. It tries the preferred static path first, then tries the parameterized path if the first choice cannot complete. As Dahan puts it, “Recursion was how I solved the problem; the important missing behavior was the ability to reconsider an earlier choice.” The key lesson is not that every router must use recursion, but that a locally sensible match can fail when the rest of the path is considered.
Reject ambiguity and test combinations
Dahan also says Mach rejects route patterns that are ambiguous for the same method, such as /a/{name}/c and /a/{id}/c. The parameter names differ, but both patterns describe the same path shape, so the router cannot distinguish them by URL alone.
His broader testing lesson is to ask what combinations could break a system that appears to work. That means examining failing tests rather than treating a passing happy path as sufficient—and remembering that tests themselves can contain bugs. The route example gives a concrete way to apply that mindset: combine overlapping route shapes with an input that makes the preferred branch fail only later.
Best Value
4. Defer features when release timing makes the risk too high
Dahan wanted handlers to accept typed route and query parameters directly in their signatures. Implementing the feature meant identifying where each parameter came from, validating which combinations were allowed, and integrating the behavior. As Mach approached v1.0 and an API freeze, he judged that there was not enough time to design and implement it properly, so he deferred it rather than force it into the final stretch.
That decision did not mean the feature was impossible or without value. It meant its design and implementation work did not fit the release window. The distinction matters: deferral keeps an idea available for later, while shipping a rushed API can commit users to a design that has not had enough thought or testing.
Dahan’s account says Mach v1.0 binds request bodies to handler parameters. It does not establish the framework’s current release status, compatibility, or maintenance state. The source for these project-specific lessons is Asaf Dahan’s account of building Mach.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




