Decisions
The choices that shaped Lex, each with the alternative it was chosen over and what it cost.
Every entry here had a reasonable alternative that other people chose. What follows is the choice, the road not taken, and the bill.
The history is not the git log
The choice. The corpus is append-only git. The history is not.
Every consolidated file a publisher issues is stored verbatim, under its sha256, in a repository that only ever gains commits. That is the evidence, and anyone can audit it with git clone. But no query walks it. Point-in-time answers come from a signed SQLite index carrying three separate time axes, rebuilt from the corpus every night. The only git call anywhere in the engine is rev-parse HEAD, which stamps the index with the exact commit it was built from.
The alternative
Store each law as a file and let git be the history: a commit per version, git log for the timeline, git diff between two dates. It is elegant, it is nearly free, and it arrives with a browsable web interface that somebody else operates. Independent projects run on exactly this. Archeo-Lex, by Legilibre, replays French law from the LEGI database as Git and Markdown, one commit per consolidated version. bundestag/gesetze does the same for German federal law from gesetze-im-internet.de; it is a community project rather than the parliament, despite the organisation name. It was the obvious thing to do, and I did not do it.
The German project is worth reading on its own commits: they aim to follow publication in the Bundesgesetzblatt, and, in its words, “das funktioniert nicht immer problemlos”, this does not always work smoothly. That is the same wall met from the other side. Nothing is wrong with their engineering; a commit graph is simply not shaped like a legislative timeline.
Why not
Five reasons. Each is a fact about legislation rather than a preference about tools.
1. Git's clock is the wrong clock. A commit records when I learned something, never when the law applied. Last night's ingest added versions that took effect on 1 January 2026 and versions that take effect years from now. Git timestamps both with last night. git log answers "when did we find out", which is a real question, but it is not the question this product exists for.
2. Law is dated into the future. Lex holds versions valid to 2030-09-15. Publishers routinely issue today a text that becomes binding years from now. Git cannot express a commit that becomes true later, so a git-as-history model must either publish future law as if it were current, or drop it. Both are wrong answers to "what is in force today".
3. Publishers backfill. A consolidation covering 2019 can be issued in 2026. Under git-as-history that arrives as a 2026 change to the law, which is simply false. The fix is to record two things separately: when the text applied, and when we saw it. That is two columns, and it is not something a commit graph can carry.
4. git diff cannot see a renumbering. When Article 7 becomes Article 7-1 with its wording untouched, that is a rename inside a file, not a file rename, and a textual diff reports one deletion and one insertion. Lex detects it mechanically instead: a renumbering event is emitted only when the text hash matches across the change of anchor. None of that is inferable from a diff.
5. There is no per-article axis. "When did Article L. 111-1 last change" needs either one file per article, which no publisher provides, or a table keyed on (work, anchor, valid_from). Lex has the table. A repository of whole documents cannot answer the question at all.
What it cost
Four things. An argument that omits them is not worth reading.
A build step, every night. Git-as-history is free: commit, and you are finished. Lex has to ingest, derive, catalog, index and sign before anything is answerable, and that pipeline is the largest part of the codebase.
Duplication. The same facts now exist twice, as bytes in the corpus and as rows in an index, and two copies can disagree. That is exactly why the index stamp binds a digest of its own content and names the corpus commit it came from: the duplication is allowed, drifting silently is not.
Schema drift. A column was once added to the index without changing the schema string, so an index built the day before opened cleanly and then failed inside a request with a raw SQL error. A repository of files has no schema to drift. Opening an index now checks that every column the reader needs is present, and refuses with a message naming what is missing.
The free interface. GitHub hands a git-as-history project a browsable, diffable, permalinked view that somebody else maintains. Choosing a data layer meant building all of it, and the reading, comparing and searching on this site is the bill for that decision.
What it bought
A point-in-time answer is one indexed lookup rather than a walk backwards through history. Future-dated law is representable. Every record separates what the publisher asserts from what Lex observed, so "what did it say" and "what did we know" stay different questions. Articles have their own lifetimes, renumbering included. And because an answer comes from a single artifact rather than a traversal, that artifact can be signed: the stamp commits to a digest of the content, so an index that was altered fails verification instead of serving quietly.
Check it yourself
Verify a build → · The data model → · The evidence repo → · What is missing →
More entries as they are written. If you disagree with one of these, that is the point of publishing them: open an issue.