TalksSchema Evolution as a Graph RewriteSlides

Presentation

Evolution Completeness Conjecture

Any schema reaches any other by a chain of small, checked steps — or the chain halts and names what cannot survive

First given July 29, 2026 · PuPPy, F5 Tower, Seattle

Slide 5: Evolution Completeness Conjecture
05Evolution Completeness Conjecture

This is a conjecture, not a theorem — maybe we'll get there one day, but for now let's just say I've got a good feeling about it. And what I mean by evolution is the small stuff. Evolution works in tiny steps: one step succeeds, and that leads to the next step.

Slide 6: Definitions
06Definitions

The program works on codebases that have a schema. Any codebase with a defined schema I can scan — and inside it there are interfaces that reference that schema.

Slide 7: Every reference is an edge
07Every reference is an edge

Turn it into a graph. Tables are nodes, foreign keys connect them. Every place in your code is a node too. And when code reaches in and names a field in a schema, that's an edge. A report that reads field_name. A serializer. A CLI flag. A test fixture.

Slide 8: Dependency graph G(S) — is it computable?
08Dependency graph G(S) — is it computable?

Can you compute the dependency graph G(S) of a codebase with a schema? Yes, using a library called LibCST. And if we have seen a pattern before, we can identify it and work with it.

Slide 9: LibCST: every node, typed by parsing
09LibCST: every node, typed by parsing

LibCST is a concrete syntax tree, and the difference between that and grepping for user_id is the difference between finding a string and finding a reference. Grep hands you the comments, the log lines, and the unrelated local variable that happens to share a name. The tree knows what the code means: this is a column definition, this is an attribute access, this one is just a string that looks like your field.

Slide 10: The wire scanner: where the schema leaves C
10The wire scanner: where the schema leaves C

We can also scan the wire — where other systems depend on your schema. So if there is a billing service and a mobile app out there with your field names baked in, we can find those locations and mitigate problems before they occur.

Slide 11: The catalogue of deviations D
11The catalogue of deviations D

In evolutionary theory they call it a mutation, but in programming we use mutation for something else, so here let's say it's a deviation. We have a catalogue of eleven deviations for database changes, and each one does exactly one thing.

Slide 12: Fixation F
12Fixation F

Apply one deviation to a schema and you get a fixation — one established step, the change that actually took hold. And when you fix one, you scan: what depends on it? Rename a field, split a table — what else is affected? The graph answers that exactly, not approximately.

Slide 13: Fixations compose
13Fixations compose

And they compose. Rename, add, remove — chain them and you walk from one schema to another. That chain is what I mean by an evolution. Which gives you the conjecture: any schema reaches any other by a chain of fixations — or the chain halts and tells you exactly what could not survive. The halting is a feature. Some changes genuinely destroy information, and I would rather name the loss than paper over it.

Slide 14: The Fixation Lemma
14The Fixation Lemma

For each deviation there is a fixation with three properties. Code integrity: it still compiles, and the tests it already had still pass. Data preservation: what was stored under the old schema is recoverable under the new one. Interface compatibility: every interface touching a changed element got updated. F is total over the dependency graph — nothing joined to that node is missed.

Slide 15: Fixation certificate P(F)
15Fixation certificate P(F)

Every fixation ships a certificate: a generated test, written alongside the change, asserting those three properties. Imagine every Alembic migration you have ever written arriving with its own test. And be precise about what it certifies — that the data survived and the interfaces still execute. Not that your business logic still means what it meant.

Slide 18: What the conjecture does not cover
18What the conjecture does not cover

The graph tells you where a field is referenced. It cannot tell you whether the meaning survived. Change an int to a string — and LibCST will find every reference and rewrite it, nothing will crash, every test will pass. And ints sort numerically while strings sort lexicographically, so now "10" < "9", and your report is just wrong. That's what happens for any logic that assumed something about a field beyond the fact that it exists — sorting, arithmetic, comparisons, range filters. Finding those is a different job and I have not done it.

Slide 19: Brownfield horse, greenfield chariot
19Brownfield horse, greenfield chariot

Which is exactly why the answer is not to evolve everything. Every interface was built for a particular schema, so don't change the schema underneath it. Take the system you don't own and can't regenerate — that's your horse. Don't try to evolve a horse. Instead, build a chariot: evolve your own schema, your own generated interfaces, joined to your horse by one named column. No foreign key across the boundary, no shared migration, no coupling they have to agree to.

The previous version, on video

SEAPUG and PuPPy, April 2 2026 — the same argument in its earlier form, start to finish.

Field notes

Monthly notes on AI-assisted development — what I'm shipping, what I'm learning, and which tools actually earn their place. It's also where the next talk and the next office hours get announced.

Talks · Reports · Survey of Software · ivantohelpyou.com
Model Citizen Developer · Ivan Schneider · [email protected]