The natural step after my last post would be to examine some LLM-generated code and point and laugh. I don’t think many of you would be in on the joke, though, because it depends on a shared understanding between us of what programming code is, and what it should look like. Thus, I must explain the joke before I tell it.
The Punchline
I think of “good” code as no different than “good” writing: programmers spend far more time reading code than writing code, and people read code to understand the world in some way and/or share ideas with other people. The contours of human cognition thus define how code should be structured, and code that respects that is “better” than code that does not.
For instance, our memory has severe limitations. We have an enormous capacity, but accessing any one item can prove difficult. To retrieve one we need to put ourselves in a similar state to when that memory was first laid down. This can create “drift” where the memory subtly changes over time, and more popular memories with similar states can lead the memory to decay or even be forgotten. How does that relate to programming code? The more commentary we write in parallel to our code, the more likely we are to remember what it does and how it interacts with other parts of the program. The more descriptive the variable or function name, the easier it is to figure out what it contains, and the less time we have to spend researching or thinking.
We can only juggle a handful of concepts at the same time, conversely. Our love of abstraction is our primary way of overcoming this, it helps immensely if one of those concepts can be something as expansive and complex as (say) “love.” Thus it’s a wise move to form hierarchies, be they of objects or concepts, to reduce the amount we need to juggle simultaneously. Good code should also be quite narrow, focusing on just one or two tasks at a time, but we can cheat by making that task ridiculously high-level on the outside while breaking it down into smaller and more numerous low-level tasks internally. We can even take advantage of abstraction to reduce the amount we need to memorize; if adding together certain types of objects makes sense, we can allow the programmer to write “a + b” rather than “a.add_to( b )“, though even the latter is better than “collection( a, b )“.
By now, you must have spotted that this joke is ridiculously problematic. If I define “good” code as being written by humans, for humans, then the best case scenario for LLM-generated code is inferiority, and at worst outright exclusion of being programming code at all. And where’s the computer in all of this? If the code compiles, runs, and does what you ask it to, isn’t that all that really matters here? My idea of “good” code stinks of self-serving bias.
Heard of This Guy?
Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.
The practitioner of literate programming can be regarded as an essayist, whose main concern is with exposition and excellence of style. Such an author, with thesaurus in hand, chooses the names of variables carefully and explains what each variable means. He or she strives for a program that is comprehensible because its concepts have been introduced in an order that is best for human understanding, using a mixture of formal and informal methods that reinforce each other.
I didn’t come to my idea in a vacuum, though. Others reached the same conclusion nearly half a century ago; the quote above is from Donald Knuth in 1984. I don’t think I encountered Knuth’s version until the last decade, however. We both reached the natural conclusion of anyone who has spent a lot of time programming or thinking about programming code.
- Sooner or later someone else is going to have to understand the programs you write.
- Programs must be written for people as well as computers.
- Knowing that a program can be understood and amended by someone else ought to be one of the programmer’s criteria for success.
Mike Punter. “Programming for Maintenance” in Techniques of Program and System Maintenance. QED Information Sciences, 1988. As quoted by Knuth.
In each of the jewels I’ve seen, the designers had obviously spent a lot of time thinking about the structure of their system before writing code. The system structure could be accurately described and documented without reference to the code. Programs were not just written; they had been planned, often in some pseudocode or a language other than the actual programming language. In contrast, the worst software I’ve seen was written in “stream of execution order” without a design having been produced (and reviewed) in advance.
D. L. Parnas, “Why Software Jewels Are Rare,” Computer 29, no. 2 (1996): 57–60.
Clarity is the granddaddy of good programming, the platinum quality all the others serve. Computers make it possible to create systems that are vastly more complex than physical machines. The fundamental challenge of programming is managing complexity. Simplicity, readability, modularity, layering, design, efficiency, and elegance are all time-honored ways to achieve clarity, which is the antidote to complexity.
Clarity of code. Clarity of design. Clarity of purpose. You must understand—really understand—what you’re doing at every level. Otherwise you’re lost. Bad programs are less often a failure of coding skill than of having a clear goal. That’s why design is key. It keeps you honest. If you can’t write it down, if you can’t explain it to others, you don’t really know what you’re doing.
Paul Dilascia. “What Makes Good Code Good?“. MSDN Magazine, July 2004.
The Ironic Twist
As a result, programming code has been designed, from the start, to be easy for humans to read. Stop laughing, I’m dead serious here.
In the 1950s, ‘programming’ referred to the process of devising an algorithm, while ‘coding’ was the more tedious task of encoding the steps of the algorithm as machine instructions. Programmers of the early computers soon realised that they can use the computer itself to make coding easier. The Short Code system, built in 1950 for the UNIVAC I computer, enabled programmers to specify programs in a more human-readable, although still numerical, code that resembled infix mathematical expressions. … The approach of writing programs using more expressive pseudo-instructions and translating those to the machine language became known as automatic coding. As illustrated by the FLOW-MATIC promotional brochure it promised to ‘virtually eliminate your coding load’.
A popular description of programming at the time was that the programmer had to come up with a program and then translate it into a language the machine understood. This used the anthropomorphic metaphor of computers as giant electronic brains, which was introduced by the media and was further popularised by ongoing developments in cybernetics. The metaphor made it natural to describe programming as a translation from human language into a machine language. …
During the second half of the 1950s, the notations used by automatic coding systems became known as programming languages. This happened as the language metaphor used in the context of computers ‘lost its anthropomorphic connotation and acquired a more abstract meaning, closely related to the formal languages of logic and linguistics’.
Tomas Petricek. “Cultures of Programming: The Development of Programming Concepts and Methodologies,” 1st ed. (Cambridge University Press, 2026).
It sure doesn’t feel like it, does it? Let me illustrate by pulling up some examples of Quicksort from Rosetta Code.
# Perl
sub quick_sort {
return @_ if @_ < 2;
my $p = splice @_, int rand @_, 1;
quick_sort(grep $_ < $p, @_), $p, quick_sort(grep $_ >= $p, @_);
}
/** C **/
void quicksort(int *A, int len) {
int i, j, pivot, temp;
if (len < 2) return;
pivot = A[len / 2];
for (i = 0, j = len - 1; ; i++, j--) {
while (A[i] < pivot) i++; while (A[j] > pivot) j--;
if (i >= j) break;
temp = A[i];
A[i] = A[j];
A[j] = temp;
}
quicksort(A, i);
quicksort(A + i, len - i);
}
The heavy usage of abstract symbols demands a lot of memorization. However, C was created in the early 1970’s to “improve” on an earlier language created by the same authors, B, which was an “improved” BCPL, which itself was an “improved” CPL.
But what were they all “improving” on? Let’s rewind back to one of the earliest programming languages.
* COBOL
IDENTIFICATION DIVISION.
PROGRAM-ID. quicksort RECURSIVE.
DATA DIVISION.
LOCAL-STORAGE SECTION.
01 temp PIC S9(8).
* [this section is long, so we'll skip the rest]
LINKAGE SECTION.
78 Arr-Length VALUE 50.
* [same issue, skipping ahead]
PROCEDURE DIVISION USING REFERENCE arr-area, OPTIONAL left-val,
OPTIONAL right-val.
IF left-val IS OMITTED OR right-val IS OMITTED
MOVE 1 TO left-most-idx, left-idx
MOVE Arr-Length TO right-most-idx, right-idx
ELSE
MOVE left-val TO left-most-idx, left-idx
MOVE right-val TO right-most-idx, right-idx
END-IF
IF right-most-idx - left-most-idx < 1 GOBACK END-IF COMPUTE pivot = arr ((left-most-idx + right-most-idx) / 2) PERFORM UNTIL left-idx > right-idx
PERFORM VARYING left-idx FROM left-idx BY 1
UNTIL arr (left-idx) >= pivot
END-PERFORM
PERFORM VARYING right-idx FROM right-idx BY -1
UNTIL arr (right-idx) <= pivot
END-PERFORM
IF left-idx <= right-idx
MOVE arr (left-idx) TO temp
MOVE arr (right-idx) TO arr (left-idx)
MOVE temp TO arr (right-idx)
ADD 1 TO left-idx
SUBTRACT 1 FROM right-idx
END-IF
END-PERFORM
CALL "quicksort" USING REFERENCE arr-area,
CONTENT left-most-idx, right-idx
CALL "quicksort" USING REFERENCE arr-area, CONTENT left-idx,
right-most-idx
GOBACK
.
COBOL looks a lot more like English. That was by design; Grace Hopper was once a professor of mathematics, so she encountered students who seemed unable to understand mathematical symbols. She loved the mathematical appearance of some early programming languages, but knew others violently disagreed, and so in 1953 she pitched her boss on a pseudo-English programming language that would become FLOW-MATIC. When she joined the committee that created COBOL, they were all too happy to borrow heavily from FLOW-MATIC because it was easier for novices to understand than the alternatives.
Conversely, you can see how COBOL would drive an experienced programmer nuts. What takes less effort to type, PERFORM VARYING i FROM i BY 1 UNTIL A(i) < pivot, or while (A[i] < pivot) i++;? Once you understand basic programming concepts, COBOL’s verbosity really grinds away at you. Typewriters at the time almost never had curly or square braces, and early programming languages respected that; those keys were added to computer keyboards at the request of programmers, because it reduced the amount of typing they had to do from BEGIN (ALGOL, 1960) to $( (BCPL, 1967) to { (B, 1969). As time marched on, FUNCTION (FORTRAN II, 1958) became DEF PROC (BBC BASIC, 1981) and then def (Python, 1991). Those later versions may look opaque to a novice, but the more you’re in on the joke the more they look natural.
Programming code doesn’t look alien because it was designed for computers, it looks that way because it was designed by and for experienced human programmers! Isn’t that hilarious?
Perl is a very high-level language. That means the code is quite dense; a Perl program may be around a quarter to three-quarters as long as the corresponding program in C. This makes Perl faster to write, faster to read, faster to debug, and faster to maintain. It doesn’t take much programming before you realize that, when the entire subroutine is small enough to fit onscreen all at once, you don’t have to keep scrolling back and forth to see what’s going on. Also, since the number of bugs in a program is roughly proportional to the length of the source code (rather than being proportional to the program’s functionality), the shorter source in Perl will mean fewer bugs on average.
Like any language, Perl can be “write-only”—it’s possible to write programs that are impossible to read. But with proper care, you can avoid this common accusation. Yes, sometimes Perl looks like line noise to the uninitiated, but to the seasoned Perl programmer, it looks like the notes of a grand symphony.
Tom Phoenix, Randal L. Schwartz. “Learning Perl, 8th Edition”. O’Reilly Media, Inc., 2025.
Every Family is Uniquely Unhappy
We’ve come full circle, but did we really wind up in the same place? Squaring “good” Perl code with “good” literate programming code may seem like a challenge, but remember: if programming code is written for humans by humans, then we should not be surprised if it exhibits some of the diversity and contradictions we find in human beings. We all have different motivations for writing code, and different life experiences, which leads to divergent views on what constitutes “good” code. Here, I found Petricek’s “cultures of programming” approach quite informative. They identify five different philosophies about programming:
- Mathematical: Programs are a precise formal logic.
- Hacker: Programs are abstract tools for manipulating computing devices.
- Managerial: Programs are a manufactured product that fulfill some need.
- Engineering: Programs are the result of a measurable, potentially unending design process.
- Humanistic: This culture “is less concerned with how to construct particular programs and cares more about what programs can be created, how humans interact with them, and how they influence the society. The humanistic culture often treats programming not as a mere tool, but as a medium for thinking or as a new kind of literacy.”
Unlike C.P. Snow, though, Petricek doesn’t view these cultures as mutually-exclusive, let alone inherently antagonist. On the contrary, their interactions often lead to advancements within the field of computer programming.
Programming languages appeared thanks to a productive meeting of managerial, hacker and mathematical cultures of programming. Programming languages serve as boundary objects that the different cultures could share, but that is interpreted differently by each of them. Programming languages also enable further communication and exchange of ideas between different cultures.
Petrick, “Cultures of Programming” (2026)
Even if you think Petrick is over- or under-simplifying, this not only goes a long way to explaining how such divergent views of programming code exist, it also hands us another method to figure out what “good” code looks like. It’s simply code that conforms to the values and ideals of all the people involved, on top of any limitations they may have. Take Perl:
Perl programming is unabashedly genre programming. It has conventions. It has culture. Perl was the first computer language whose culture was designed for diversity right along with the language. We’re not objective about Perl, but as postmodernists, we freely admit that we’re not objective, and we try to compensate for it when we want people to think we’re objective. Or when we want to think ourselves objective. Or, at least, not objectionable.
Larry Wall. “Perl, the first postmodern computer language,” 1999.
One of my favourite lines of Perl code is rand($.)<1 && ($line=$_) while<>;, which selects a random line from a file. It is “good,” in that it is short, clever, and gives you a small sample of the Perl language’s quirks. The near-equivalent Python code is:
from random import randrange as rand
from sys import stdin
for lineno,candidate in enumerate(stdin):
if rand(lineno+1) == 0: # simple reservoir sampling
line = candidate # (The Art of Computer Programming, Volume 2, Section 3.4.2)
Poll a number of developers, and most would consider the Python version “better.” The imports are in alphabetical order, and are narrowly targeted to avoid namespace conflicts. The variable names are carefully chosen to make it clear what they contain. No comment is necessary to explain what is happening, but one is helpful to explain the why and how for anyone who doesn’t immediately grasp the concept. Python’s comparatively verbose syntax is a close match for what the equivalent pseudo-code would look like. That means it takes less brainpower to parse, on average, and thus inexperienced developers have an easier time with it, making it easier to keep labour costs down by “culling the herd” of experienced developers.
The Python version is bland and soulless, a Justin Bieber next to Perl’s Angine de Poitrine. But bland and soulless is a “good” thing when you value “robust” and “safe” more than you value “compactness” and “expressiveness.” I may have taken a cheap shot at the programming industry in the last paragraph, but a glass-half-full interpretation of why Python became so prevalent in said industry while Perl has fallen out of favour should be obvious, even more so when you realize the industry is dominated by C and C-like languages.
But that’s just your opinion
Before ending this set, let’s confront the obvious loop hole. If “good” code is code that conforms to the values, ideals, and limitations of all involved, couldn’t we tweak the first two to be in line with the programming code LLMs spit out?
Yep!
But I’m also involved here. That doesn’t grant me a veto over what’s “good,” but it does mean my own values/ideals/limitations should also be taken into account. Whether they line up with yours, or are worthy of mockery, can only be determined by listening to my jokes.
