A year ago, you couldn't vibe code in Lisp. Even the SOTA models
had trouble balancing parentheses, and they'd hallucinate packages
and symbols that didn't exist. A year makes a big difference in this
field, and the latest models are capable of vibe coding moderately
sized programs in syntactically correct Lisp.
I have been experimenting with vibe coding in Common Lisp and I'm
hooked. It is a blast. It is like having on hand a talented
undergraduate who just took a Lisp course. If you give him
small enough, focused tasks, he will churn out passable code. If
you give him a good chunk of legacy code, he will churn out more
code in the legacy style. The models are not good enough to do a
full rewrite of a large codebase, but they are good enough to
handle a small library with supervision.
I find myself accepting a large amount of code with just a glance—if
it passes the Lisp reader, compiles, and the tests pass, I accept
it. Unlike the code of a year ago, the generated code these days is
far less buggy, and the models are pretty good at debugging their
own code. I'll do spot checks on the code, but I don't bother
reading it line by line unless I see something odd. If the model
generates code in a style I don't like, I'll ask it to rewrite the
code to be more to my liking.
But frankly, you don't need to read the code at all. If there is a
good test suite, the model will generate code that passes tests. If
the code is functionally correct, it doesn't matter if the code is
pretty. In one way, it doesn't matter if the code is easy for a
human to read and maintain because we ask the model to maintain it.
We treat the code as a black box and we constrain it to pass the
tests. (We accept machine code largely unread.)
Failure Modes
By far the most common failure mode is the model getting the number
of closing parentheses wrong. The tail end of a block of code is
usually a bunch of closing parentheses, and the model will be
tokenizing them in groups of 2 or 3. But the likelihood of the "))"
token isn't very much different from the likelihood of the ")))"
token, so the model will sometimes grab the wrong one.
Depending on the model and the agent, when it tries to recover from
the ensuing read error, it will re-compute the tokens in the output.
It sometimes will thrash as it tries to balance parentheses,
adding and removing them from various places in the code. (Sort of
like a noob Lisp programmer.) Some models are more susceptible to
this than others. I have found that the solution here
is to pause the agent and manually fix the parentheses when the
agent starts to thrash.
Vibe Coding Workflow
I've been using Copilot CLI and Gemini CLI to vibe code in Common
Lisp. I start with a blank project directory and create an .asd
file that loads the packages.lisp file and the main file for the
project (which can start out as a "hello world"). Basically, make a
minimal project that you can load with ASDF or Quicklisp.
The models can work at moderate levels of abstraction, but they do
better if there is existing code supporting the abstraction level,
and this suggests a `bottom-up` approach to the problem rather than
a `stratified` design. But the models are actually quite capable of
starting at a moderate level of abstraction right from the get-go.
So starting with a minimal project, I boot up the model and ask it
to write the first things needed for the project—some data
structures, some utilities, a few tests. The very simple stuff that
is easy for the model to do ab initio. Then I ask the model to
write a minimal main function that will implement the basic
functionality of the project—a command loop, a server, what-have-you—with
stubs for everything. Once a framework is in place, the
models are easily able to extend it.
The agents will get into a loop of adding code, adding tests, and
running all the tests. They will debug any test failures and only
consider a task to be complete when all the tests pass.
The model does not write great code, and you will accumulate
technical debt if you accept it as is. But the model can write code
that works and passes the tests. It is a good idea to pause during
development and simply ask the model to find the technical debt in
the code, enumerate it, and rank it in order of importance. Then
you ask the model to address each item in turn and the model will
clean up the code. After a couple of iterations of cleanup, the
code will look no worse than what I've seen in many professional
codebases.
There are sort of two modes that you operate in: one is to modify
the existing code (e.g. refactor) without disturbing the
functionality; the other is to extend the functionality without
disturbing the core operation. It is important to spend enough time
refactoring and cleaning up. But the model is good at generating
potential refactorings, and it is not good at knowing when to call
it quits. It will happily churn away at your code making it
`better' and doing more and more trivial refactorings. If you give
the model one particular refactoring task and tell it to do just
that one, it will do a good job.
Refactoring is satisfying in a certain way, but adding features
gives you more instant gratification. The models are good at adding
features and extending existing code, especially if the feature
shares any similarity with existing code.
For more complex features and refactorings, tell the model that you
want a 'plan' for the feature or refactoring. The model will come
up with a multi-step plan, broken down into a series of tasks. The
tasks in the plan are generally small enough to be handled by the
model itself.
The models are good enough to maintain a codebase, so once you
have a project up and running, the model will generally choose file
names and a directory structure that is appropriate to put in
the .asd file. If you get the model started with a test suite, it will
extend the tests as it extends functionality, or you can ask it to
add specific tests.
I have found that building a project by vibe coding it is an
extremely rapid way to prototype. The model can churn out `obvious'
code much faster than I can and it frees me up to think about the
higher level design issues. I can build in a weekend what would
have taken me a month before.