To Rivendell where Elves yet dwell
In glades beneath the misty fell,
Through moor and waste we ride in haste,
And whither then we cannot tell.
JRR Tolkien, Farewell We Call to Hearth and Hall!
On my LinkedIn feed, I am continuously encountering posts about “Software engineers are still needed. It’s always been about judgment, and that’s more important than ever.” This is naive.
The False Camera Analogy
“Smartphones brought the camera to everyone, and photographers adapted and found success. AI being brought to software engineering is not any different.”
False analogy.
The democratization of cameras and AI are not analogous. First, there is still an art and a poetic sixth sense that makes for a good photographer, and this remains regardless of the medium. Whether you are using an iPhone or a more powerful camera, you still need an eye for a fitting background, techniques for finding good lighting, understanding of layouts and perspective, et cetera. The instrument (i.e., cameras) have been made ubiquitous, but not the art. The iPhone doesn’t press down at the perfect moment, tell a family to smile, move itself from this position to that position to get multiple angles in a narrow window of kids holding still. Second, professional photographers (typically) spend money on nicer equipment, better software, and partnerships with strategic venues. There is some association between upfront investment in a better instrument and a better end product.
Contrast this with agentic development. AI doesn’t just offer you a new medium for your art of crafting a programming language. It’s not an improved code editor or an adjacent tool of that kind.
In Peri Hermeneias, Aristotle distinguishes between names and enunciations. A name is a sign of a concept without an assertion. An enunciation is a composition of a subject name and predicate name that makes a truth claim about how two things actually stand. For instance, “Socrates is a man” asserts that Socrates belongs to the category of “man.”
Code is slightly different. A function definition signifies a procedure without executing it. It asserts that this operation should be performed on this data. In this way, it isn’t just describing states of affairs, for it also produces them.
In Metaphysics, Aristotle claims that all material beings are composites. Every material being is made up of matter and form. For example, a marble statue has the matter of marble but the form of a statue. Similarly, code has a functional form amidst all the various lines. Namely, it has a functional output that it produces: a program runs without errors and passing tests. Ultimately, there is an output that needs to be produced from inputs, and something can be functional regardless of whether it's legible.
The art of coding is more than reaching the functional output, for it also involves refactoring. Any engineer knows what refactoring feels like: thinking hard about the names of variables, functions, etc., splitting out dual purposes in functions, deciding whether to make a block of code dynamically reuseable for multiple callers, determining whether to utilize your own code or to reach for a public and reusable library that may do the same, etc. To describe this phenomena more accurately, we can say that good refactoring makes the end goal self-evident. In metaphysical terms, it is making the formal cause of the solution legible. Good naming conventions can help you know what you are working with, how you are handling that what, and why the various steps are helping to attain the end goal (i.e., the output).
function slurp(derp) { return derp + ‘chirp’ }
The example above obscures the concepts involved through poor naming conventions.
Functions can also create boundaries of abstraction. The following terms are helpful to explain this:
slinky, toy, object = increasing extension / abstraction (becoming more abstract)
object, toy, slinky = increasing intension / concreteness (becoming less abstract)
Now, review this code:
function getFullName(name) { return getTitleCase(name) }
function getTitleCase(string) { return … }
In this example, the order of functions increases in extension. “getFullName” is specific to a unique set of data that presumably models a person. “getTitleCase” is a general string manipulation that can work on any kind of string.
The art of refactoring requires determining when introducing a layer of abstraction (i.e., a more abstract, general function) is worthwhile. The alternative is that you’d fully write out the same same string manipulation logic in every instance. This means you don’t have to remember two functions, but it comes with the cost that you could accidentally create inconsistent behavior across the codebase and/or you make yourself write more lines of code than otherwise possible. There may be a functional cost, a cost of time, and/or a cost of legibility. In other words, deciding on something like whether or not to introduce a helper function is considering the knowledge of what and the knowledge of why that will persist in the codebase. Weighing these considerations and making a decision falls under what Aristotle would call practical prudence, and what we tend to label as “judgment.”
This is just a pencil sketch of how the what, how, and why is central to refactoring, but it serves the general point. “Clean” code is code that makes the programmatic instructions more intuitive to the reader.
Going back to the phone illustration, smartphones make the instrument more accessible but do not automate the art of knowing what, how, and why with that instrument. Joining smartphones and AI to form an analogy about the impact of technology on an industry, therefore, does not hold.
Judgement Absorbed
The remaining-judgment-despite-AI discourse at times can misconstrue what exactly AI is able to do. Consider the refactoring example. AI can still write code that would be, to some degree, functional and legible. There, of course, are cases when it is not fully functional (i.e., has not handled every case, has introduced unintended side effects impacting the holistic end goal, etc.) and when it is not maximally legible. However, AI can be quite impressive with the code it produces, with respect to both function and legibility. Hence, it is more accurate to say that AI still requires a degree of judgment, and that degree can vary case-to-case. The point being is that AI does absorb the exercise of judgement.
Additionally, even when you need to add your own judgement to perfect the output of AI, this can be done in a “set it and forget it” kind of way—like in context files—that even more absorbs the degree of judgment needed in a day-to-day workflow.
Moreover, using an LLM to talk through potential design decisions et al absorbs even more judgment about whether to look for information and expedited the determination of what needs to be done.
We could continue but the point is that AI absorbs the need for human judgment. Say AI absorbed all the judgment except when to click a button to write and release code. Judgment would still be required, but that avoids the harder question. The real question is not about whether any slice of judgment remains, it is about what is the portion of judgment remaining when all is said and done—and what is left for the mass of developers that were needed for that judgment only a year or two ago. For those who remain, the question is whether the judgment required today is the judgment that will be required tomorrow as AI accelerates not only its own judgment but also the judgment of others on how to maximize AI’s judgment.
Software Folk
Another critical phenomenon is often not addressed: the quality of software engineering that remains. In this case, I’m not talking about the code’s quality; I’m talking about the coder's quality [of life].
Right now, engineers remaining are still riding the wave of novelty. However, that novelty may be masking a qualitative crash.
Quality of work impacts the quality of life. The notion that we can overcome issues in work quality with hobbies has its place, but it has a finite limit. What, then, makes for quality work for an engineer?
Things have not changed since Aristotle. Happiness involves excellence, and excellence involves good accomplishment despite challenges/hurdles/friction. Besides the general, humane applications of this, I want to focus on the phenomena of coding more narrowly. Engineers, of all stripes, like solving problems. Why? Because problems contain hurdles by definition, and solving them merits happiness by nature. While various domains of work involve problem-solving, the very thorniness of analytically-demanding problems is the joy of an engineer.
It is rather evident, then, that AI takes some of the joy away. This is not a novel claim. However, it’s worth reflecting on these more granularly.
As noted above, coding requires the challenge of being given a problem with certain requirements as parameters and shaping the functional form to solve it. However, most experienced engineers hit a point where the challenges in the functional form are not as demanding as they once were. However, there is the challenge of making the code as legible as possible, and the very interest in such is typically a signal of an engineer’s technical maturity. Testing, DevOps, new projects/products/teams, all serve to keep the love for solving problems alive and well. As AI automates, the problems shift to the adjacent, but there is finite limit. None of this is novel.
My more original claim is that physically writing out code creates a friction that merits happiness per se. There have always been domains of work that differ in degree of judgment. Aristotle uses the example of a carpenter and a mathematician. A carpenter studies the right angle toward the end of perfecting his craft of producing quality furniture; a mathematician investigates the right angle for the sake of comprehending a mathematical truth.1 So, should you be a carpenter or a mathematician? In an ideal world, pleasure is a strong indicator while the root cause is a differing mix of circumstances, habits, and dispositions.2 There are certain circumstances, habits, dispositions, and rituals that together form a certain software folk. By this I mean the subtle and overt swirl of habits, rituals, dispositions, and aesthetics that comprise software engineering culture and lore.
I’d argue that simply opening up a code editor, firing up various tools, writing out code, and developing certain rhythms around this are all things that required more friction than is now present. None of this is new, the tools of a kind of work have always been essential. Running an insitution through emails on a laptop is qualitatively different than reading and sending letters. Sure, offices, desks, and pens may leave a degree of resemblance; yet the experience is quite different even if the work is largely the same. In other words, work is more dynamic than the responsibilities and output; the medium of that work is also impactful on quality according to one’s preferred folk. AI, arguably, is changing the folk. It is not that the same folk experience typing prompts into LLMs as it is to write code out, even when we compare the two without consideration of the absorption of responsibilities. Not seeing the blob of complementary colors in a code editor using a custom theme as frequently removes a habit, embeddedness in a tool, an aesthetic encounter.
Another underappreciated dimension of software folk is the interest that comes in reviewing someone else’s code and seeing how their approach and progress compares with yours. With the introduction of AI, there is blurriness as to whether your peer truly progressed or retrieved an answer from their own initiative, autocomplete, or prompting—and how whether the prompt took much skill. In turn, the feedback loop of your mentorship and support atrophies. Similarly, AI absorbs the experience of whiteboarding and rubber ducking where you can enjoy meaningful contribution toward someone else’s good.
I suppose this blurriness applies to the individual as well. There is blurriness as to whether you are progressing. Was it your skill, the autocomplete, or the LLM that produced the output? If it was a mix, how would you measure that? The lack of benchmarks and feedback loops is genuinely disorienting—so disorienting that I challenge if “judgement” is even discernable at some point. What impact does this uncertainty have?
I don’t have a magic solution, but I do know a sense of progression in life is essential to living well. Of course, it is not a categorically new phenomenon. For example, you would not know whether your peer got something off Stack Overflow instead of working through it the hard way. Yet, somehow this was existentially manageable. There is a line in the sand with these kinds of things where the blurriness overcomes the clarity and judgment really cannot be made. There is a point where the fog has fully crept in and the vista is no longer in sight.
These ideas can be mapped out more precisely and more broadly. I merely aim to introduce this mode of thought to the reader to continue the discourse. Judgment may still remain, but it is absorbing more than time. It is absorbing hurdles/challenges/friction, and it is absorbing a certain folk. Moreover, the lack of feedback may obscure “judgment” enough to make it practically disappear.
To conclude, I’ll at least throw out some starts to solutions, although they will need to be watered.
If friction produces happiness, then friction may need to be sought for its own sake. Its presence can no longer be assumed. This may look like turning off autocomplete complete for certain period and writing out the code manually. Or, you could develop the habit of not tab completing after you make a quick glance. Similar habits can be reviewed for Claude Code et al usage. The point is to intentionally write out code even when you don’t have to. There used to be no choice, now there is, and the solution is a habit not a tool.
Another idea is for developers to make explicit their path to a functional and legible slice of code. They could comment if the used autocomplete in an inline comment, a commit message, a pull request description, etc. Or, maybe a LLM chat history can have a git-like workflow that gets checked in.
Still brainstorming, I’ve even thought about introducing a new software engineer language that is a layer on top of a LLM chat, for it would add friction and developer-specific lore. Perhaps I’m not wildly off given I recently saw a famous developer writing new posts about code Japanese grammar. (I can’t knock this—I’ve worked my through Latin grammar and Familia Romana this past year. Harry Potter in Spanish and Latin is on my list.)
However, lack of friction alone is not the biggest problem. The biggest threat is the existential one. Namely, the difficulty with AI-driven engineering lacking feedback loops to measure your performance vs. that of the agent. This may require placing overarching, human-generated benchmarks for successful AI-driven engineering into the developer experience. This one deserves the most follow-up.
Whatever the solutions may be they flow from addressing the problem with precision. Perhaps one friction we can introduce is to take more time to reflect on these subjects than write shallow “judgment” posts ad nauseum.
Aristotle, Nicomachean Ethics, Bk. 1 Ch. 7.
This is a dignified way of saying vibes (as the kids say).


