code-chaos: The stress tester that treats state as the product

code-chaos turns YAML into repeatable backend workflows, stitching HTTP calls, SQL, and message brokers into one execution loop.

11 min read · dashn9/code-chaos

An editorial illustration of a YAML sheet feeding a chain of backend systems. A set of values moves from an HTTP terminal to a database vault and then to a message broker drum, showing that each step depends on the previous one. The image explains that code-chaos is built around reusable state, not isolated requests.
The core idea is simple: every response can become the next input.
Key Takeaways

Most load tools ask how many requests a service can survive. code-chaos asks a sharper question: can the system survive a realistic sequence where yesterday's response becomes today's input? That makes it feel less like a traffic cannon and more like a stateful harness for backend teams.

State, not traffic

The hard part in backend testing is rarely the first request. It is the join across steps: create a user, capture its ID, write dependent records, publish an event, then verify the downstream state. code-chaos packages that sequence into a declarative procedure so the test case can live as data.

The repository is organized around that idea. /schema defines the contract, /parser turns tiny DSL expressions into values, /data stores responses and variables, and /tester executes endpoint, SQL, and RabbitMQ actions. That separation matters because parsing, storage, and execution stay distinct enough to evolve without rewriting the whole tool.

code-chaos is a closed loop: parse, inject, execute, capture, reuse.

The custom DSL is the real bet

The parser accepts expressions shaped like name(type):value or name(type):func!(args). That ! cache marker is the quiet killer feature. It lets one random or computed value be reused across later steps, which is how you keep referential integrity when a single procedure fans out across multiple systems.

procedures:
  - id: seed-order
    type: generate
    count: 3
    actions:
      - id: create-user
        type: endpoint
        request:
          method: POST
          url: 'https://api.example.test/users'
          body:
            id: 'rng(1000(int),9999(int))'
            email: 'user-{{count(int)}}@example.test'
      - id: write-invoice
        type: db:sql
        query: 'INSERT INTO invoices(user_id, external_id) VALUES({{create-user.body.id}}, rng(1(int),999999(int)))'
      - id: notify
        type: broker:rabbitmq
        message:
          user_id: '{{create-user.body.id}}'

The generator turns that into a loop. For each iteration it writes a count(int) variable, runs the action list, and checks ExpectedResults. In practice, code-chaos is doing two jobs at once: seeding data and asserting that the state it created still looks right.

Why the storage model matters

That second job is where the StoreWrapper abstraction earns its keep. It can query both HTTP and DB stores, and the variables are scoped by procedure type and procedure ID, so parallel runs do not bleed into each other. The project is trying to make backend state behave like a first-class test fixture instead of a pile of ad hoc globals.

What it beats, and what it does not

ToolPrimary modelState handlingWhere code-chaos stands out
JMeterGUI or script driven request plansAssertions are usually attached to requests, not shared workflow stateBetter for HTTP volume, less natural for multi-step data graphs
LocustPython load scriptsState is possible, but you write the flow yourselfMore flexible for traffic, less declarative for backend seeding
k6JavaScript load testsGood for scripted logic, still centered on request performanceStronger for API load, weaker for YAML-first state reuse
code-chaosYAML procedures with dynamic valuesStores responses and reuses them across stepsBuilt for dependent actions across HTTP, SQL, and brokers

That comparison is the point. code-chaos is not trying to replace your load testing stack. It is trying to compress the awkward part of backend work, the part where one response unlocks the next action and the whole chain needs to stay coherent.

The prototype is part of the story

The repo is promising, but it still reads like an active design sketch. The main loop is unfinished in places, some critical paths use panic(), and the parser leans on regex enough that future extensions could get brittle. That does not make the idea weak. It tells you where the maintenance cost will show up first.

The author has organized the project like an engine builder would: schema, parser, stores, executors, and generators. That structure suggests a practical goal, not a framework play. Someone wanted a reliable way to model backend workflows without hand scripting every dependency.