by CJ Quines • on
taste and vibecoding
now let me tell you, i’m the biggest hater
feels like it’s unavoidable to be a software engineer and not think about vibecoding in some sense. i’m a huge ai skeptic. for the past five years, the most useful i’ve found llms are for amusement purposes, like writing haiku, and i haven’t found it that useful otherwise. which, i’m glad it’s not just me, but it feels kinda bad since writing code is supposed to be something llms are good at.
my old excuse for this was something like, well, it just isn’t good at the kind of software engineering i need to do? which is, empirically:
-
tricky business logic, which involes juggling a lot of weird distributed state (which, it’s unsurprising that llms aren’t good at this)
-
refactoring (which, for non-trivial refactorings, i’m disappointed it’s not good at, which is kinda sad)
-
pl-flavored ast manipulations or black magic type-level programming (which, sure, there’s not that many examples of)
-
frontend state management and sync logic (which, come on, surely there’s a billion examples of this on the internet)
it’s only recently that i’ve been impressed by llm output, and it was when i was rearranging a bunch of frontend components. i used claude code, gave it a few pointers of where in the code base it can look at examples for, and it kinda worked? i watched what it did, interrupted when i needed to and gave it guidance, but otherwise i wrote zero code and made a pull request and it was. fine.
that moment broke me, and kinda made me feel weird. it’s part of what was going in my head when i wrote my last post: “i don’t like how ai could grow to obviate my job.” if claude code could do a whole frontend ticket on basically its own, who’s to say it can’t do any of the other things in a few months, given the rate of progress?
so i tried using more claude code for gph work. i wrote a bunch of frontend state syncing and hooks and stuff, and i needed to write a ui for it. so i get claude code and get it to make an interface for the state, which it does, and it’s okay. again it’s a little back and forth to get the result i want, but it’s smooth.
i tried playing with it and found some bugs. and i was already in the vibecoding mode, so i thought, heck, why not keep going? i ask claude to try to fix the bug. it goes in an unrelated tangent, and i could tell it’s unrelated, but… i decided not to interrupt. it failed to fix the bug, i say so, and it makes another attempt. this time it entirely replaces some of the state syncing logic i wrote, declaring it buggy. and to be fair, it was, but the design that it chose to replace it with was less elegant. but again, decided not to interrupt.
it was still buggy. so i ask it to write unit tests, and then iterate on them, and then end-to-end tests, with a browser and everything. after two hours of iteration, which mostly consisted of me playing monster train 2 on the side, and saying a sentence or two to claude every other minute, it got the correct functionality. this is after several thousand lines of tests and changes to make things work. the next day, i decided to fix the thing myself, and after thirty minutes or so i got the right result with significantly less thrashing.
i was talking with david yu about this the other day, and we ended up talking about vibecoding. some bullets:
-
claude code suggests doing
/init
on a new repo, which writesCLAUDE.md
, which is documentation intended for claude. you can also get claude to keep writing to this document, growing it over time. you get documentation for, and by, an llm. there’s the nice side effect that the proliferation of ai agents means more repos get more documentation, but isn’t it strange that the documentation is written by, and meant for, ai first? -
more generally, you can imagine a repo where all the code is written by llm, and meant for llm. there’s particular patterns that are ai-optimized, like having lots of tests, or a programming style where you write full-sentence comments every few dozen lines. there’s no code that’s “clever”. the code style and quality wouldn’t matter, because it’s not meant for human consumption at all.
-
if enough of this keeps happening, do you run into a copy-of-a-copy-of-a-copy kinda thing? the code becomes a simulacrum of human code. i dunno
-
effective vibecoding requires some amount of taste, it seems. claude code is too much of a toddler to leave alone and unsupervised, and my best usage of it requires interrupting it and telling it what i want it to do and what’s good and what isn’t. it doesn’t help that claude thinks every single one of my ideas is a good idea (ugh), but without taste it’ll just produce… things that work, but aren’t matching style or aren’t pretty or aren’t elegant. and sure you can try to enforce this with writing stuff in
CLAUDE.md
but you can’t transmit taste in a handful of words imo -
in a world where people vibecode much earlier in their programming lives, how will people develop taste? insert the classic notes on taste or something. similarly, there’s something about the aesthetics, or the soul, of a product or an artifact, that’s somehow in something produced by a human but not something produced by an llm. what is it, where is it, does it matter?
-
the phenomenology of vibecoding is interesting. once code starts being written by an llm, i don’t want to spend effort hand-editing it, i want the llm to edit it itself. to be fair, this is how code review often does go (i say what changes i want in the code, rather than editing the code myself). but there’s something about the, hm, reverse-sunk-cost, where, i haven’t expended effort understanding this code, i don’t want to expend it now, just keep on going and working on it yourself then
anyway. i still use llms for various things. not enough to say that i use use them, given the people i work around. not enough that i’m fully comfortable reaching for them. it’s still weird and it still feels kinda strange when i do so, but i’ll admit that a useful tool is useful even if it’s weird.