Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Building Mach, a C++20 web framework, taught Asaf Dahan four practical lessons: make convenience intentional, draw clear boundaries around abstractions and object lifetimes, test the cases that can defeat apparently correct code, and defer features when release timing makes them a poor fit. These are lessons from one project, not a universal prescription for framework design.

1. A simple API takes deliberate work

Dahan wanted Mach handlers to accept request data without imposing an arbitrary rule about parameter order. In his account, handlers could take no arguments, a request body, a context, or both the body and context in either order. Mach inspected handler signatures at compile time, then bound incoming data and invoked the handler at runtime.

The convenience at the call site depended on more machinery inside the framework. Dahan considered that trade-off worthwhile: users did not have to remember a parameter-order convention chosen for implementation convenience.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

He also describes a validation builder. A request type provides a validate() method, fields are identified through member pointers, and rules such as minimum or maximum length can be declared. Mach then validates the request body and returns a 400 response with errors when validation fails. These are details of Mach’s implementation as Dahan describes it, not capabilities provided automatically by C++.

2. Abstractions and ownership need boundaries

Choose where an abstraction earns its cost

Dahan used PImpl for AppBuilder to separate its public interface from its implementation. The implementation uses a unique_ptr, which adds a heap allocation. He accepted that cost for an object involved in application setup, while being more cautious about adding it to an object created for every request. This was a design judgment, not a reported benchmark.

Make lifetime relationships explicit

During development, Dahan learned to distinguish objects that are stored, borrowed, transferred, or shared. He used unique_ptr extensively and shared_ptr when shared lifetime made sense, including asynchronous operations. He says he finished Mach without writing an explicit new or delete; that describes his experience with this project, not a rule that manual allocation is always wrong.

Reuse infrastructure when it serves the project

Mach used Boost.Asio and Boost.Beast for asynchronous networking and HTTP, and spdlog for logging. Dahan’s stated rationale was to focus his effort on Mach’s architecture and behavior instead of rebuilding substantial infrastructure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

3. Challenge code that feels correct

Let route matching reconsider a choice

Dahan gives a router example with two registered routes: /a/{param}/c and /a/b/d. A request for /a/b/c can expose a failure in a traversal that tries the static b branch first: that branch eventually reaches a dead end, although the parameterized route matches. His fix was to backtrack—try the preferred static path, then try 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.”

Reject ambiguous route definitions

Some patterns should be rejected before serving requests. For example, registering both /a/{name}/c and /a/{id}/c for the same method creates ambiguity: the parameter names differ, but the route shapes do not. Dahan connects this kind of edge case to a broader testing habit: consider which combinations could break a system that appears to work, examine failing tests carefully, and remember that tests themselves can contain bugs.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

4. Shipping requires timing decisions

Dahan wanted handlers to accept typed route and query parameters. Implementing that feature meant identifying where each parameter came from, validating combinations, and integrating the result into the framework. As the v1.0 release and API freeze approached, he deferred it rather than force the work into the final stretch.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Deferring the feature did not mean deciding it was impossible or undesirable. It meant keeping the idea available for later instead of adding a substantial design and implementation task at a risky point in the release schedule. Dahan’s article says Mach v1.0 binds request bodies to handler parameters; it does not establish the project’s current release status, compatibility, or maintenance state. Read his account at DEV Community.

Best Value

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.