This is what you want. You want comprehensive tests at every level, far more than is reasonable for a human to build or maintain, from unit, functional, to full end to end and beyond. Adversarial testing (both TDD-style "write tests to demonstrate this bug", and posthoc "prove this patch wrong with a new test") is the best way to keep AI on track and make those diffs you have to read clean and easy.
An even better way is to use a more strongly typed language and really lock it down, but you can use testing in any language. I feel like my background in TDD and "TATFT" has been secret sauce when working with AI
https://github.com/dprkh/eventfs
It has good test coverage, mostly unit tests but also a number of end-to-end tests. I also made the LLM build a benchmark, which you can find at the bottom of the readme. It is obviously slow, but I thought that it is good enough to work. When I tried to write a 1 GiB file, I found that it broke down, and after writing half the file, the speed went to under one megabyte per second. Implementation is 10k+ LoC, and I have no idea what is going on there.
At least with agent-run tests I care about loop speed a lot, but I care about complete coverage more, so having the odd heavy weight full stack integration test is fine, I think.
Yes tests are conceptually isolated and that helps, but I've personally seen unit tests get generated that are semantically incorrect - that is, they test the structure of the code (e.g. they can check function output types and values), but they can't know _why_ the unit tests need to be there, so the really really helpful tests never get generated. Not to mention the obvious issues with generated tests only testing is x = x, or needless redundant tests for the same thing, or them essentially testing basic features of the language.
I actually have a public (AGPL) example here: https://github.com/pgdogdev/pgdog/tree/main/integration/sql - pgdog is particularly testable since it is trying for complete transparency, so you have a perfect oracle in hand via base postgresql, but it demonstrates the concept at least.
This is also part of why I like end to end tests that use actual UI flow, so I can watch it go by in slow mode before letting it loose fully automated.
What do you mean by "be epistemically sound enough"?
You are using it as if to say "if your code is grounded in sound abstractions, you'll be fine and tests will therefore generate successfully" but preface that claim with "the code provides a baseline truth for the tests". The latter does not follow from the former, and it also does not lift the burden of responsibility away from the programmer - which is where my doubts on test generation stem from in the first place.
Additionally, what is "completely solipsistic value generation"?
You reference it like a perk in a skill tree, but to my ears "generating completely solipsistic values" seems like a way of describing AGI with a philosophical wording instead of just saying AGI.
Also you: > You have to iterate on the tests, review and validate them
Yes, "maintain" is not quite the same as "review", but the line is veeery fine. I find it really tiring to review masses of tests that an agent spews out.
Especially because I know what it has a tendency to write irrelevant/vacuous/useless tests. It's insane the amount of times I have told Codex to "write a test that reproduces the reported bug, SEE THE TEST FAIL, then implement a fix", only for it to guess an irrelevant test, not run it to see it fail, and implement a code change that has nothing to do with either the test or the actual bug.
I've been burned by this in my honeymoon period with unit testing (pretty much the reason it ended). These days, I prefer broader scope of testing, especially user-facing part. The users may be other developers or end users. I only do unit testing for tricky algorithms or math formulae.
They’re mostly a reflection of the current requirement of the project.
It is different though. Basically a lot of what I do has changed over the last 2 years. I totally get that a lot of people won't want to adapt though.
Or people don't want to be reverse centaur keeping the clankers happily running. Instead of helping to solve users/consumers problem.
It's been an experiment to see how much more performance I can squeeze from a Rust version (spoiler: it's a lot), how well the agents code in Rust (pretty great and seems idiomatic AFAICT), and if this is a good way to learn a new language (I'm learning, but the verdict on how efficient is still out).
I might be self deluding, but I do think it's been productive, even though I'm intentionally moving slow with small TDD vibe spikes followed by completely reading over everything, adding more guard rails if necessary, refining requirements and tests, sometimes ripping it out then and have the agent rewrite it more iteratively with meticulous reviews, etc. Honestly, I have the time to do this right, so I've been focused on correctness and making it enjoyable to avoid burn out... but what I find enjoyable, won't be the same thing others find enjoyable. I also have the autonomy and financial security to adopt entirely new workflows and do rewrites of my own products, which not everyone has. I would absolutely hate being forced to token max or w/e that insane BS is all about.
I save myself by skimming things like tests, templates, some UI. Anything cosmetic. But I have to read the majority of code that ends up on my back end systems.
In my personal experience, the ones most enthusiastic about LLM magic are those that can't code, but can now walk away with something functional if not quite the best code. Now that they can produce workable code, it will make everyone better. Yet, they have no idea how maintainable the slop is or if it's slop at all.
When you see a perfectly clear function or object that just isn't your style, you have to accept it and move on. Where there are concrete concerns, or it's unreadable, demand excellence, but treat it like a coworker, not an IDE.
The only time I look at code is when something isn’t right and I ask for a root cause analysis. The LLM will show me some offending code or code for reference or evidence and then I quite often say “well that’s dumb you should do it like this instead” but I never need to actually go into the files. I do sometimes look at a git status or git diff.
is the critical caveat to “that’s not how I would have done it”. Basically, choose your battles because we all have limited bandwidth. So, it’s not really a perfect binary, but a taste that you personally develop.