The more I think about Worse is Better, the more my logical circuits start to smoke. And I wonder why I can’t wrap my automated deduction heuristics around it.
Then I think “Of course! It’s a logical contradiction.”
Duh.
But more seriously—isn’t Lisp worse—and therefore better?
I mean, Lisp seems to embody the worse-is-better mentality more than most languages I know. Perl might actually be the worst—and therefore the best. But that’s beside the point.
But Lisp is exactly the language used as the example of better-is-better (also known as “The Right Thing”).
Ok. You might be thinking “this guy’s got a mismatched parenthesis in his head”. But, you’ve read this far, so I figure you’ll hear me out.
Ok. What’s more worse-is-better:
- Creating a gigantic UML design document, then translating it to Java or C++
- Bottom-up programming in Common Lisp
Am I missing something here? I mean, how can you get more The Right Thing than the standard waterfall design?
And how can you get more worse-is-better than coding up a simple language layer—then covering up its worsiness with a new language layer?
Reasons why Lisp is worse-is-better:
- You can cover up the worse bits with better syntactic abstractions.
- Incremental development. Start worse—get better.
- Bottom-up programming. Write the worst code you can. Now refactor it better.
- Use a list as a data structure until you really need to change it.
Right. So I do get the whole worse-is-better thing. For instance, no standard socket library exists in Common Lisp because there was no The Right Thing for that at the time of standardization. I get it. I get it.
So, is it really about worse now is better than better later? Wow. That made my head spin.
Let’s look at it a different way:
Richard Gabriel’s formulation is paraphrased (and simplified):
The Right Thing
- Simplicity —- Simple interface is better than simple implementation.
- Correctness — Incorrectness is not allowed.
- Consistency — Inconsistency is not allowed.
- Completeness — Completeness is more important than simplicity.
Worse-is-Better
- Simplicity — Simplicity is the most important consideration in a design.
- Correctness — It is slightly better to be simple than correct.
- Consistency — It is better to drop the inconsistent parts than sacrifice simplicity.
- Completeness — Completeness can be sacrificed in favor of any other quality.
I must be missing something. Whenever I look at these two lists, I think: Oh, The Right Thing, that’s all those static languages, where the compiler tells you what to do. You have to do all sorts of designs up front because it’s hard to experiment.
Worse-is-Better—that’s for dynamic languages (like Lisp) where you can write simple programs that don’t require a lot of up-front design. You can just save everything in a List—no need to create a class, define its interface, etc.
But Lisp is explicitly put into The Right Thing category!
Richard Gabriel:
The worse-is-better philosophy means that implementation simplicity has highest priority, which means Unix and C are easy to port on such machines.
I agree that Unix and C are relatively easy to port. But I can’t figure out how a bootstrapped language (like Lisp) is hard to port. In theory, there are only a handful of operations that need to be implemented before the whole system can build itself.
And it continues:
There is a final benefit to worse-is-better. Because a New Jersey language and system are not really powerful enough to build complex monolithic software, large systems must be designed to reuse components. Therefore, a tradition of integration springs up.
Ok, I must be crazy: is he suggesting that any system where huge, monolithic pieces of software are created is The Right Thing? Does that mean Windows? Or is Windows just not in either of these two camps?
A “tradition of integration springs up” in Worse-is-Better—and what could be more integrated than “everything’s a list”?
Some possible explanations for my confusion:
Maybe I don’t understand the ideas. But I think I do. Maybe software has changed. It could be that Lisp, in prior incarnations, was used differently. And software has changed. Perhaps also I’m looking at it unfairly: all of the design work that went into Common Lisp makes writing simple programs easy—but that simplicity comes from having a simple interface—definitely a The Right Thing idea.
My whole feeling on this is based on one simple idea: that in Lisp, you can do incremental development in a way you can’t do in a language like Java. In Lisp, I very often write a language layer in less than 100 lines of code, that is worse is better. It doesn’t handle nearly every case. It does some things wrong. Basically, the only thing it’s got is simplicity.
And then I write another layer on top that fixes some of the problems. And then another layer. And eventually, I have a system that does The Right Thing—or enough of The Right Thing for my purposes.

Layer 1
(defun open-file (filename) . . .)
(defun close-file (stream) . . .)
Simple implementation, but it introduces some interface complexity: as a programmer, you have to remember to close the file when you’re done with it. After using it a while you realize what you should write next . . .
Layer 2
(defmacro unprotected-with-open-file (var filename &body body)
`(let ((,var (open-file ,filename)))
,@body
(close-file ,var)))
Now you don’t have to remember—the language will do it for you. But what if the program throws an exception? Your file won’t get closed—unless:
Layer 3
(defmacro protected-with-open-file (var filename &body body) . . .)
You get the picture? I suspect that these ideas—and the associations with programming languages—need to be revisited.
