Every developer has a piece of code they're secretly proud of. A one-liner that does the work of twenty lines. A chain of arrow functions that reads like poetry if you squint. A class hierarchy so elegant you wanted to frame it. I've got a few. I used to show them off.
I don't anymore. The longer I build for clients, and the more of my code gets written and read by agents, the more convinced I am that "code is art" was never a great idea. It made us feel good without making the software better, and right now it's getting in the way.
Who is code actually for?
Who consumes the code you write?
First, a machine. The browser, the PHP interpreter, the database. None of them care how it looks. A browser will run a gorgeous, hand-tuned module and a messy one exactly the same way, as long as they do the same thing. On most of my Craft projects, Vite minifies the JavaScript before it ships anyway. Whatever beauty you put into that file is gone the moment you run npm run build. The thing that runs in production is a wall of single-letter variables.
Second, the next person who has to change it. For most of the history of software that was another developer, maybe you in six months. Today it's increasingly an agent, with a human reviewing the result. That's the audience that matters, and neither of them is there to admire anything. They're trying to understand what the code does and change it without breaking something.
Third, and this is the one we forget, the client. They never see the code at all. What they see is whether the site works, whether it shipped when we said it would, and what it cost. I have never once had a client ask me if the codebase was beautiful. I've had plenty ask why a small change took three days.
What "beautiful code" usually looks like
When developers call code beautiful, they rarely mean "easy to understand." They usually mean one of these:
- It's short. Something that took ten lines now takes one.
- It's clever. It uses a language feature most people forget exists.
- It's abstract. It solves this problem and six hypothetical future ones. (I made the case against that one in my post on DRY and RYAN.)
- It has no comments. Because "good code documents itself."
Here's a classic. It groups published entries by year and counts them:
The "art" version
const byYear = entries
.filter(({ status: s }) => s === 'live')
.reduce((a, { postDate: d }) => ({ ...a, [d.getFullYear()]: (a[d.getFullYear()] ?? 0) + 1 }), {});I'll admit there's a little thrill in writing that. It's also harder to read than it needs to be, it spreads a new object on every iteration for no reason, and if there's a bug in it, you're going to spend a minute just parsing the syntax before you can even start looking. Here's the boring version:
The boring version
const byYear = {};
for (const entry of entries) {
if (entry.status !== 'live') {
continue;
}
const year = entry.postDate.getFullYear();
byYear[year] = (byYear[year] ?? 0) + 1;
}It's longer, but anyone can read it top to bottom on the first pass. You can drop a breakpoint on any line. When the client says "actually, count drafts too," the change is obvious. Nobody is going to put it on a poster, and that's fine.
Agents changed the math
To be fair, an agent can read the clever version. Modern models are good at parsing dense code, often better than I am at 4pm on a Friday. What they can't read out of it is intent.
Code tells you what happens. It almost never tells you why. Why does this skip drafts? Is that a business rule or an accident? Why is this timeout 45 seconds and not 30? Why does this template check for an empty image twice? When an agent doesn't know why, it guesses. Its guesses are plausible, which means they get through review.
That's why "no comments, the code speaks for itself" aged so badly. The code only ever spoke for the what. The why lived in the head of whoever wrote it, and that person isn't in the room when the agent opens the file. A one-line comment like this does more for the next change than any amount of elegance:
A comment that earns its place
// The CRM rate-limits us to 60 requests a minute, and a full sync
// is about 4,000 contacts, so batch in 50s and pause between batches.
foreach (array_chunk($contacts, 50) as $batch) {Comments that narrate the obvious, like // loop through contacts, are noise. The ones worth writing explain a decision, a constraint or a trap. They've become some of the most valuable lines in a codebase, because they're the context an agent can't get anywhere else.
Same goes for clever abstractions. Every layer of indirection is something the agent has to trace before it can make a safe change, and something I have to trace when I review it. In the diffs I review, code that's written flat and explicit gets changed correctly on the first try far more often.
Nobody agrees on what beautiful is anyway
Put five senior developers in a room and ask them to define beautiful code. You'll get five answers and an argument.
Tabs or spaces. Semicolons or no semicolons. Classes or functions. Early returns or a single exit. Ternaries or if/else. Point-free style or named variables. Tailwind utility classes or "clean" semantic CSS. People have been fighting about every one of these for a decade or more, and there is no winner, because it's mostly preference.
That's what art is: subjective. That's great for a painting and a terrible basis for a professional deliverable that a team, a client and now a fleet of agents all have to work with. When "good" means "matches my taste," every code review turns into a debate about style instead of a check on whether the thing works.
The good news is we already solved the style part. Prettier, PHP CS Fixer, ESLint, a shared .editorconfig. Pick a formatter, let it make the decisions, and never discuss it again. Agents are great at following a formatter. They don't need to have opinions about brace placement, and neither do you.
It was never art
I don't think code was ever really art, and I think the metaphor did quiet damage for a long time.
Art exists to be looked at. Code exists to be run. The closer comparison was always something like plumbing or electrical work. You want it done well, to code (the building kind), in a way the next tradesperson can understand when they open the wall. A great electrician takes real pride in their work, and that pride goes into routing the wiring so the next person can find it, even when a spiral would look cooler.
When we treated code as art, we gave ourselves permission to optimize for how it made us feel. Clever felt good. Short felt good. Abstract felt senior. Meanwhile, the client was paying for the hours, and the next developer was paying for the cleverness.
What clients actually pay for
After a lot of years of client work, I can tell you what they want, and it's not complicated:
- Ship fast. The launch date matters more than almost anything else in the project.
- Cost less to build. Every hour spent polishing code nobody will see is an hour on their invoice.
- Cost less to maintain. Year two and year three are where a site either stays affordable or turns into a money pit.
- Work well. Fast pages, no bugs, accessible, secure, easy for their editors to use.
"Beautiful" isn't on the list, but "maintainable" is, and people confuse the two. Maintainable code and beautiful code often pull in opposite directions. Maintainable is plain, obvious and a little repetitive. Beautiful is usually clever, dense and abstract.
A useful test: if you're proud of a piece of code because of how clever it is, that's a sign to rewrite it. If you're proud of it because nobody ever had to ask you about it, you did it right.
This isn't an excuse for sloppy code
I know how this could read. Letting go of "code is art" doesn't mean writing garbage; the bar stays just as high and moves to a different target.
Instead of beautiful, aim for obvious. Good names. Small functions that do one thing. Clear control flow. Comments that explain the why. Tests that prove it works. Consistent formatting that a tool enforces. That is real craftsmanship, and it takes discipline. It's just aimed at the people and machines that actually use the code, rather than at the person who wrote it.
If anything, agents make this easier. You can put the standard in your CLAUDE.md or AGENTS.md and hold them to it:
- Prefer plain loops and early returns over dense chains of arrow functions.
- Name things for what they are, not for how short the name is.
- Comment the why: business rules, constraints, workarounds and anything surprising.
- Don't introduce an abstraction unless asked.
- Let the formatter handle style.
You'll find that agents follow rules like these more consistently than most humans do, because they don't have an ego about their one-liners.
Letting go
I get why this is hard. A lot of us got into this work because we loved it, and "code is art" was part of that. It made the job feel like a craft instead of a ticket queue. I still feel that. I still smile when a tricky problem comes out clean.
The joy can come from somewhere other than how the code looks: from shipping something that works, on time, that the client can afford to keep running for years, and from knowing the next person who opens the file, human or agent, will understand exactly what's going on.
If you want to make art, make art. I'm all for it. Just don't bill the client for it.
Comments
No comments yet.