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.
- code-chaos is less a load generator than a stateful workflow engine for backend testing.
- Its parser lets YAML carry dynamic values, cached functions, and reused outputs across steps.
- The StoreWrapper and scoped variables keep parallel runs from bleeding into each other.
- The idea is sharp, but the codebase still reads like an early prototype with brittle edges.
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.
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
| Tool | Primary model | State handling | Where code-chaos stands out |
|---|---|---|---|
| JMeter | GUI or script driven request plans | Assertions are usually attached to requests, not shared workflow state | Better for HTTP volume, less natural for multi-step data graphs |
| Locust | Python load scripts | State is possible, but you write the flow yourself | More flexible for traffic, less declarative for backend seeding |
| k6 | JavaScript load tests | Good for scripted logic, still centered on request performance | Stronger for API load, weaker for YAML-first state reuse |
| code-chaos | YAML procedures with dynamic values | Stores responses and reuses them across steps | Built 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.





