Design Principles¶
Three decisions shape everything else in Test Data Workbench. They are worth stating plainly, because they explain not just how the system works but why it declines to do certain things that competing tools do.
Generated code is the product¶
Most synthetic-data tools generate rows: you configure them, and they hand back data, either directly into a database or through an API. The generation logic stays inside the tool.
Test Data Workbench generates generator source code instead. When you run
tdw deploy against a schema, the output is a directory of readable Python
modules, one per table:
generated/
├── generators/
│ ├── users.py
│ ├── products.py
│ └── orders.py
├── scenarios/
├── config.yaml
└── CONTRIBUTING.md
This is a deliberate trade. It costs a layer of indirection: you run generated code rather than call a library function. It buys three things that matter more in practice.
Inspectability. You can read exactly how every column is produced. There is
no hidden model, no opaque configuration resolution. What you see in
users.py is what runs.
Correctability. When the tool misreads a column, the fix is one obvious line of Python, not a new entry in a configuration DSL that you must first learn. Heuristic classification is always going to be wrong sometimes; the design assumption is that being wrong in a way you can trivially fix beats being wrong in a way you have to work around.
Ownership. Generated generators are yours. Commit them, extend them, code- review them like any other module. The tool can leave your project entirely and the fixtures keep working.
Degrade, never fail¶
A schema-adaptive tool meets schemas it does not fully understand. The design question is what happens then. Test Data Workbench never answers that question with a stack trace.
Every column and every table flows through a four-level fallback ladder, described in detail in The Fallback Ladder:
Specialized generation for recognized entity types, with domain logic (unique emails for users, plausible ranges for prices).
Generic generation from column-name and type patterns when the entity type is unrecognized.
Simple generation from the column’s declared type alone.
Minimal generation that always produces something type-valid.
Each level is strictly less clever and strictly more reliable than the one above it. An unfamiliar table does not break the run; it lands a rung lower on the ladder and still produces usable data. Failures are isolated per generator, so one problematic table cannot cascade into the rest of the output.
The principle behind the principle: for test data, partial and plausible beats perfect and fragile. A dataset that is 90% ideal and 100% present is more useful than one that is flawless for the tables the tool understood and absent for the rest.
Determinism on demand¶
Reproducibility is not automatic in a system built on Faker and random.
It has to be engineered, and small leaks defeat it. Test Data Workbench treats
byte-level reproducibility as a testable property, not an aspiration.
Pass --seed and two runs against the same schema produce identical output,
verified by comparing the full generated tree:
tdw deploy "sqlite:///shop.db" -o out_a --seed 42
tdw deploy "sqlite:///shop.db" -o out_b --seed 42
diff -r out_a out_b # no differences
Achieving that required closing two classes of leak that are easy to miss:
Wall-clock values. Any
datetime.now()embedded in generated artifacts makes two runs differ. Timestamp embedding was removed from generated code so that a seeded date is derived deterministically instead.Hash-seeded iteration order. Python randomizes the iteration order of sets of strings between processes. Emitting anything derived from
set(...)iteration silently breaks cross-process reproducibility; the code sorts before emitting.
The generated generators carry the property through: instantiate one with
seed=42 and it produces the same records every time. This is what makes the
output safe to use as CI fixtures, where a nondeterministic dataset is worse
than none.
What these principles rule out¶
Stating the principles also settles what the tool does not try to be, covered in Privacy and Data Access and the scope section of the README:
It is not a statistical or ML synthesizer. It does not learn distributions from your data; it produces plausible values from schema structure.
It is not a production-data masking tool. Preserving the statistical shape of real rows is a different problem with a different risk profile.
It is not an ETL or data-pipeline framework.
Those are real, valuable products. Keeping them out of scope is what lets this tool make its own guarantees hold every time.