Vix.cpp v2.8.5 is here Read the blog
Skip to content

Design Decisions ​

Vix is built around one idea:

txt
C++ should feel direct for application development without losing its power.

Summary of key decisions ​

DecisionReason
Runtime, not new languageKeeps the power of C++ while improving workflow.
Direct compileMakes single-file C++ fast and simple.
CMake supportKeeps real project builds correct and portable.
Explicit APIsAvoids hidden behavior and unexpected magic.
Public module headersKeeps documentation stable and learnable.
MiddlewareKeeps shared logic out of route handlers.
Config via .envChanges runtime behavior without recompiling.
Prepared statementsProvides safer database access.
Cache is temporaryAvoids treating cache as durable state.
Sync persists firstPrevents local operations from being lost.
P2P as transportSeparates connection logic from delivery logic.
DiagnosticsMakes C++ errors easier to understand and fix.
Benchmark modeMeasures performance with less runtime noise.
Native deploymentRuns applications as normal Linux services.

1. Vix is a runtime, not a new language ​

Vix keeps C++, improves the workflow around it:

cpp
#include <vix.hpp>
using namespace vix;

int main()
{
  App app;

  app.get("/", [](Request &, Response &res) {
    res.text("Hello Vix");
  });

  app.run(8080);

  return 0;
}

2. Application-first workflow ​

Start from "what do I want to build?", not from build setup:

bash
vix run main.cpp    # immediate start
vix new api && cd api && vix dev  # real project

3. Direct compile for the simple case ​

One file → fast compile → run. Docs examples should be self-contained:

bash
# Good — reader creates their own file
touch main.cpp && vix run main.cpp

# Bad — depends on repo being cloned
vix run examples/internal/file.cpp

4. CMake is supported, not replaced ​

txt
direct compile → one file, fast experiment
project build  → multiple files, real app, CMake presets

Vix is an application workflow above the build system.

5. --run is separate from -- ​

bash
--    = compiler or linker flags (sent to compiler)
--run = program arguments (sent to argv)

vix run main.cpp -- -O2          # compiler optimization
vix run main.cpp --run server    # runtime mode

6. Public headers should be simple ​

cpp
#include <vix.hpp>           // core HTTP
#include <vix/db.hpp>        // database
#include <vix/sync.hpp>      // offline-first sync
#include <vix/p2p.hpp>       // P2P runtime

Docs prefer public headers. Internal paths exist but users shouldn't need them first.

7. APIs should be explicit ​

cpp
// Route — explicit
app.get("/users/{id}", [](Request &req, Response &res){
  const std::string id = req.param("id");
  res.json({"id", id});
});

// Database — explicit
auto stmt = conn->prepare("SELECT id, name FROM users WHERE id = ?");
stmt->bind(1, id);

// Sync — explicit
Operation op;
op.kind = "message.send"; op.target = "/api/messages"; op.payload = R"({"text":"hello"})";
outbox->enqueue(op, t0);

8. No garbage collector ​

Vix keeps the C++ ownership model: RAII, std::unique_ptr, std::shared_ptr, move semantics, destructors. Deterministic lifetimes matter for servers, networking, and database connections.

9. Modules are focused ​

txt
json       → JSON values and responses
db         → database access
middleware → logic around routes
websocket  → realtime communication
cache      → caching layer
sync       → offline-first delivery
p2p        → peer-to-peer runtime

One module solves one clear problem. Module dependencies go downward: high-level API → runtime modules → core primitives → system utilities. No circular dependencies.

10. HTTP uses App, Request, Response ​

txt
Request  → what the client sent
Response → what the app returns
App      → routes and server lifecycle

Small, memorable mental model.

11. Middleware keeps routes clean ​

txt
middleware → shared request logic (CORS, rate limiting, auth, logging)
route      → business endpoint

12. Route order is explicit ​

cpp
// Correct — specific before wildcard
app.get("/users/search", search_handler);
app.get("/users/{id}", user_handler);
app.get("/*", fallback_handler);

13. Config lives outside source code ​

cpp
config::Config cfg{".env"};
app.run(cfg);  // port, TLS, database from .env

Change runtime behavior without recompiling.

14. Database uses prepared statements ​

cpp
// Safe — prepared statement
auto stmt = conn->prepare("SELECT * FROM users WHERE id = ?");
stmt->bind(1, id);

// Dangerous — never do this
auto rows = db.query("SELECT * FROM users WHERE id = " + id);

15. Cache is not the source of truth ​

txt
cache    → faster reads, temporary resilience, stale fallback
database → durable state, source of truth
WAL      → durable intent
outbox   → durable delivery queue

16. Sync persists first ​

txt
persist first → then network call (not the reverse)

Key invariant: an operation must exist locally before the network is used.

17. Retry distinguishes temporary vs permanent failure ​

cpp
// Retryable — try again later
return SendResult{.ok = false, .retryable = true, .error = "network offline"};

// Permanent — do not retry
return SendResult{.ok = false, .retryable = false, .error = "bad request"};

Retryable: network offline, timeout, 503. Permanent: bad request, unauthorized, validation failed.

18. P2P is transport, not a replacement for Sync ​

txt
Sync → durability, outbox, retry, convergence
P2P  → node discovery, connection, transport

Sync works over HTTP, P2P, or another transport. Keep delivery logic separate from connection logic.

19. P2P control routes must be protected ​

txt
POST /p2p/connect    → sensitive
GET  /p2p/peers      → sensitive
GET  /p2p/logs       → sensitive
POST /p2p/admin/hook → sensitive

Protect with authentication or keep internal. Do not expose publicly by default.

20. Diagnostics are part of the experience ​

txt
raw error → parse → classify → code frame → hint

The compiler remains the source of truth. Vix adds classification, deduplication, code frames, and next-action hints.

21. First error matters most ​

Show 3 distinct errors, deduplicate the rest. Fix the first error — many later ones may disappear.

22. Performance must be measured ​

txt
v2.4 → ~66k to 68k req/s
v2.5 → ~98k req/s        (on /bench route)

Concrete numbers are stronger than vague claims. Optimize one thing at a time, measure before and after.

23. Benchmark mode reduces noise ​

VIX_BENCH_MODE=ON removes development overhead to measure the runtime path more clearly. Never remove required behavior (validation, security, durability) for benchmark numbers.

24. Native deployment is intentional ​

txt
systemd → Vix binary → Nginx → HTTPS users

No special application server. Deploy like a normal Linux service.

25. Health checks are normal ​

cpp
app.get("/health", [](Request &, Response &res) {
  res.json({"ok", true});
});

Production readiness is part of normal app design.

26. Logs must be safe ​

Good logs include:

  • Startup events
  • Request method and path
  • Error codes
  • Request duration
  • Sync failures
  • P2P statistics

Never log:

  • Passwords
  • Tokens
  • Cookies
  • Private keys
  • Authorization headers

27. One-file docs are intentional ​

Start simple, add structure later:

bash
touch main.cpp → vix run main.cpp → works

Then grow to routes/, services/, repositories/ when the concept is understood.

28. app.del is used for DELETE ​

cpp
app.del("/users/{id}", delete_handler);  // correct

29. Avoid magical claims ​

Better than "no configuration ever" or "everything is automatic":

Vix makes the common workflow smoother while keeping the full C++ path available.

What you should remember ​

Vix is designed around a balance:

txt
simple workflow + explicit C++ + production reliability

The core design idea: make C++ application development feel direct without hiding how the system works.

Vix helps developers move from:

txt
one file → real project → production service → reliable distributed system

without changing the identity of C++.

Released under the MIT License.