Hacker News new | ask | show | jobs
by nzeid 23 days ago
I do not have the burnout but I certainly operate similarly to the author. I continue to be unable to establish a workflow where allowing the LLM to generate code that I review is faster than writing the code myself. Literally the only two ways out of this dilemma is to blindly trust what was generated or to generate an uncharacteristically exhaustive suite of unit tests to validate every possible scenario. I just write the business logic myself and have the LLM do a lot of the rest. Boilerplate falls into the latter as well.
5 comments

> generate an uncharacteristically exhaustive suite of unit tests to validate every possible scenario.

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

I see this get mentioned a lot but I still am skeptical that AI can generate tests we can trust more than any other code we know we cannot trust.

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.

You have to iterate on the tests, review and validate them, just like any other code, and if you generate a whole project's tests all at once the quality is abysmal, of course. I've been using a lot of old school data-driven testing techniques, where the harness is just code I review, and the data itself is e.g. json files and drives the system.

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.

Then this falls into the exact same pit the OP mentioned, either you need to blindly trust that the LLM is generating tests that actually work, or you need extensive test coverage for your tests to ensure that your tests are actually testing.
It turns out that you don't actually need tests for your tests, because the code provides a baseline truth for the tests. You do, at some point, have to be epistemically sound enough to actually look for correctness in either the code, behavior, or tests. We unfortunately haven't fully unlocked completely solipsistic value generation yet.

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.

Maybe it's because I haven't had my coffee yet, but I cannot understand what you are saying.

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.

You: >>> You want comprehensive tests at every level, far more than is reasonable for a human to build or maintain

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.

Which is why test generation has to be carefully guided as well, and this is something at which I've incidentally been fast. Ultimately it's a constant battle between LLM handholding and doing things yourself.
I don't even care about tests being correct as you can still verify them even when tedious. What I care is that, more often than not, the shape of the solution is not fixed. Having unit tests for those can be extremely costly as when the changes happens, you have to change all the tests.

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.

I want all the layers of the pyramid, eventually, but the top layers matter the most. I can't count the number of times my paranoid "make sure that customers can successfully pay us" end to end test suite has prevented the money faucet from being shut off. I install one perennially at any company I work at and they always pay for themselves surprisingly quickly.
I’ve been involved in B2B (so no payment flow). But it’s basically the same with an handful of integration tests for common workflows. They run fast and mostly serve a canary to ensure that we are not crippling some use cases. When a bug hits us, a test case is added/modified for it.

They’re mostly a reflection of the current requirement of the project.

AI shouldn't write tests. At least not all of them. Definitely not e2e's. The tests should be guardrails to constrain agents. This way, the author of code matters less.
I used an LLM to build this

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.

That's interesting because I would feed that benchmark back into the agent and loop over it, to see how much faster you could get it, and agents are really good at that kind of recursive optimization. And I would definitely add at least a simulated 1GiB write test, probably a real one honestly, if I was building something like that.

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.

You're right. This was just a performance issue, but what if next time it is a corruption bug or a security vulnerability or really anything that can cause real consequences if happened in production? I don't think that LLM systems are inherently bound to have this flaw, but I think that we are pretty far from harnesses and algorithms becoming advanced enough so that the LLM system can kind of continuously evaluate its output and ensure it is good in all aspects.
I don't know about that, Fable is, when properly guided, a better engineer for those things than I am. Narrow breadth, weird priorities, myopic and ivory tower as hell, but superhuman. Maybe that says more about me, or maybe not, but certainly it's caught bugs I would not have, and point it at things like a fuzzer, woo buddy, it has been many years since I broke out valgrind and nailed down a memory leak, but it sure can.
Yep.
100% this is what I've done. I sucked it up and adapted myself to the tool (agents) by having as many implicit guardrails (static typing, functional, no nulls, great linting) and then layering on explicit guardrails (TDD) on top. I also want my workflow to be portable because I don't really trust the frontier model providers.

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.

> 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.

Maybe famous last words, but I'm not buying the hype that the "clankers" will take over. I suspect reality will catch up soon and we'll be left with a set of pretty powerful but still limited tools. I see no evidence to the contrary, just investment hype on one side and sky is falling on the other.
The “clankers” won’t take over, but have you also noticed that most people are talking about their workflows/process instead of their results/outcomes? It’s all about “Is the train still running?” than “Are we getting close to the destination?”.
That's true and interesting. Personally I've been rebuilding an application in Rust, learning Rust at the same time and leaning heavily on AI agents for both the building, but also the learning. I've been at it for a few months now (large application) and should be done pretty soon. I'm fairly comfortable with ML languages, and Rust has felt pretty good.

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.

Yeap. Hundreds of tests for small tools are completely trivial now.
We end up having to review the copious tests. In addition to the code (we still review it, right)?
I've just been carefully reading the code. It is easy to slip into just accepting what comes out to speed things up, but reading the code is important.

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.

And for those that have similar-ish sentiments, what mental defect is had that prevents them from just drinking that sweet tasty kool-aid and just use the slop created. What demented trait is in them that causes everyone to just be a stick in the mud trying to ruin everyone else's good time?

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.

If you can't review the code faster than you could write it yourself, write it yourself.
Don’t read the code!!
I actually dispute this, I read all the code, the core thing people have to give up is not "reading the code" per se, it's giving up on "that's not how I would have done it".

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.

This all reminds me of the differing experiences people had outsourcing coding in the 2010s when it was still called oDesk. You don’t need to read code, you just need to know that the code works. If something doesn’t show up as a problem it doesn’t need to be fixed, and reading code is the least efficient way to discover problems.

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.

Yeah this is how I feel about it. Does it look correct? is it doing something weird? Is it forgetting about some gotcha in our domain that it hasn't been taught about yet? Otherwise, ship it.
> When you see a perfectly clear function or object that just isn't your style

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.