Rewrite the goals as plain one-line aims, with draws kept only by name and paths stepping up with ..

This commit is contained in:
2026-10-03 00:21:29 +02:00
committed by Lillem4n
parent 2d5e7da8dc
commit f43064ed97
+60 -55
View File
@@ -174,6 +174,14 @@ zone), `useragent` and `uuid` (v4). Many carry sub-fields — `misc.currency.sym
`misc.territory[SE].capital` and `misc.currency[Euro].symbol` select a row; `car` and
`useragent` carry no key or name, so they are drawn from rather than selected in.
A fact is read from its source by a script under [`data-import/`](data-import), as
goal 12 asks. Where no script exists yet, a small hand-written set ships, and
[`todo.md`](todo.md) carries the step that replaces it. A script drops a source row
only by a rule it states. A category is added in proportion to how many real schemas
store it, per goal 13. Per goal 14, a category whose values could reach something real,
such as a domain anyone may register, also gets a path that draws only values that
reach nothing; `todo.md` carries the categories still missing one.
`misc.httpmethod`, `misc.protocol` and `misc.port` are IANA's registries.
`misc.httpmethod` is the eight methods RFC 9110 defines and PATCH; the register's
other entries, WebDAV and DeltaV among them, do not ship. Its `safe` and
@@ -760,6 +768,8 @@ token {w} is repeated, and uppercase operand "w" holds "w" to one draw per expan
### Performance
Goal 11 aims at about a microsecond per value.
Each file is parsed, validated and weight-indexed once, in `New`, bar a table's name and
parent columns, which are mapped on the first `Fake` that selects by name or descends
through it. Proving the draw fences adds a pass over the loaded tree per
@@ -820,61 +830,52 @@ the Development section below, and who ships a register the five above then draw
## Goals
1. **The best fake-data tool available, and used because it is** — a developer
choosing a seed-data library or CLI, in any language, should find this the one
worth switching to, and every goal below it is what that costs. Merit is only
half the goal: once the grammar, the data and the CLI are complete and stable,
it ships where those developers install from and is announced where they read.
2. **Understanding the code is part of the product** — it is a compiler with static
fences behind a small API, it will grow, and the people who extend it will not be
the people who wrote it. So reading cost is judged alongside behaviour: reaching
the unit behind a symptom without asking a person, changing one piece without
holding the rest, names that tell the truth, and a file that stands with no second
document open beside it. A simulated reader panel scores those four, the score is a
ratchet no release lowers, and [`AGENTS.md`](AGENTS.md) says what each pull request
owes it. Below the floor it holds back new capability, never a repair of behaviour
this README already documents, as long as the round that lifts the score stays planned.
3. **Valid by construction** — every value passes the check a current release of
its real consumer applies; facts that belong together come from one draw, within
a value and across categories.
4. **Templates are intuitive, and text means what it says** — a template or a path,
and every path in a template, renders what every writer reading it would expect,
judged by simulated writers from the Audience. A format renders as written; only `{…}` varies, random characters included
(`{digits(3)}`), and a selected row, `[SE]`, is that row wherever it is written.
A name is changed whenever a more intuitive one is found; the Versioning table
prices the change, never blocks it.
Where two writers could expect different outputs, the template is a load error
naming a spelling for each. One spelling per result; the wrong one is a load error
naming the right one.
5. **Errors are up front and easy to understand.**
6. **Zero to a value in one command** — `go install`, then `fejkdata sv_SE.person`:
no checkout, no flag. Flags are GNU-form (`--seed 42`, `-n 3`) in any position;
the first custom template needs no escape and no option.
7. **Data lives in JSON** — a builtin only for what data can't express.
8. **Reproducible** — seed in, same stream out; no builtin reads a clock.
9. **Zero dependencies** — standard library only.
10. **Docs index the grammar** — every syntax feature is a heading; every example
runs under test and shows its output; a rule is stated once.
11. **Fast enough to be free** — a value renders in about a microsecond and `New`
parses and validates the whole set once up front, so generating fixtures stays
noise against a test's own runtime.
12. **Data is sourced, or on its way there** — a shipped fact, a name, place,
code, id or classification, is read from a register or open dataset by a
[`data-import/`](data-import) script wherever one exists to read; where none
does yet a small hand-written set ships and [`todo.md`](todo.md) carries the
step that replaces it. Only non-factual copy stays authored. A sourced table
holds the rows its source holds: none is added by hand, and one is dropped
only by a rule the script states.
13. **Breadth follows what most systems store** — a category is added in proportion
to how many real schemas hold it: names, addresses, phones, ids, money and
timestamps before anything domain-specific, and a catalogue serving one niche
waits behind everything serving many.
14. **Realism is the default, inertness is selectable** — where a value could reach
something real, a domain anyone may register or an account a bank could issue,
the realistic breadth ships *and* so does the subset that provably reaches
nothing, each on its own path. A fixture that looks nothing like production
tests nothing; the caller who needs a value that can touch nothing asks for it
by name.
1. **The best fake-data tool there is, and the one developers reach for.**
1. You can install it the way you install your other tools, and people hear about
it where developers read.
2. **The code is easy to understand.**
1. You can go from a bug to the code behind it without asking anyone.
2. You can change one part without keeping the rest in your head.
3. Every name says what the thing really is.
4. Each file makes sense on its own, without another document open beside it.
3. **Every value is valid.** It passes the same check the real system would apply,
such as the checksum in a Swedish personal number.
1. Values that belong together come from the same random pick. You choose which
ones by giving that pick a name.
4. **Templates are easy to write and read.** A template is the text you write to
describe a value, such as `{first} {last}`.
1. Everything outside `{…}` prints exactly as written.
2. Each `{…}` makes a new random pick.
3. A name keeps one pick. `{/person as p}` picks a person and prints nothing.
`{p}` then prints that person, and `{p.last}` their last name.
4. A path works like a folder path: `.` goes down a level, and `..` goes up one.
5. `[SE]` selects one row of a table, and means that row wherever you write it.
6. If two people could read a template differently, loading it fails, and the
error shows how to write each meaning.
7. Each result has one way to write it. Any other way fails to load, and the error
shows the right one.
8. When a clearer name turns up, it replaces the old one, even if that needs a new
major version.
5. **Mistakes are caught when the data loads, and the error says plainly what is
wrong.**
6. **One command gets you a value.**
1. Flags work like in other command-line tools, and can go anywhere on the line.
2. Your first template of your own needs no escaping and no options.
7. **Data is JSON files. Code is only for what JSON can't express.**
8. **The same seed always gives the same output.**
9. **It depends on nothing but Go's standard library.**
10. **The docs are a map of the template language.**
1. Every feature has its own heading.
2. Every example runs as a test and shows its output.
3. Each rule is stated in one place.
11. **It is fast enough that you never notice it.**
12. **Every fact comes from an official or open source,** such as a government
register.
1. A table holds every row its source holds, and nothing added by hand.
13. **The most common data comes first,** such as names, addresses, phone numbers and
dates.
14. **Values look real by default. You can also ask for values that can never reach
anything real,** such as a domain nobody can register.
## Layout
@@ -995,6 +996,10 @@ docker compose run --rm --user "$(id -u):$(id -g)" data-import data-import/tld.p
docker compose run --rm --user "$(id -u):$(id -g)" data-import data-import/useragent.py
```
Goal 2 is scored by a panel of simulated readers, the `comprehension-panel` skill, and
[`AGENTS.md`](AGENTS.md) says what the score asks of a pull request. Goal 4 is checked
against simulated template writers from the Audience.
To release, head `CHANGELOG.md` with the version's section in place of `Unreleased`
and merge: once `main` passes the gate, CI tags that commit `vX.Y.Z` and publishes
the GitHub release with the section as its body. A top heading of `[Unreleased]`