AI Can Make the Sad Stack Work

by

this post has been formatted by AI

The most dangerous architectural mistake in vibe coding is not choosing technology that cannot solve the problem. It is choosing the wrong technology that can.

One of AI’s greatest strengths is also one of its most important weaknesses: it can make almost anything work. Give a capable coding agent a questionable framework, an unnecessary database, three overlapping abstraction layers, and a deployment model designed for a company a hundred times your size. It will not sigh, push back from the keyboard, and ask, “Why are we doing this?”

It will get to work.

It will generate adapters. It will reconcile incompatible types. It will build wrappers around wrappers. It will patch the deployment pipeline, add retries to compensate for an unreliable boundary, and write enough glue code to make the whole contraption appear intentional.

And because AI writes code so quickly, the result may even look like success.

That is a particular danger in vibe coding. A human describes the desired result, the AI begins building, and the first plausible architectural choices quietly become facts. Every subsequent prompt inherits them. The AI becomes increasingly effective at answering the question, “How do I continue down this road?”—without revisiting the more important question: “Should this be the road at all?”

Capability Is Not Suitability

Most bad technology choices are not completely incapable of doing the job. If they were, they would fail early and do us a favor.

The dangerous choices are merely ill-suited. They work, but only with more machinery, more exceptions, more operational knowledge, and more places for bugs to hide. A document database can imitate relational behavior. A relational database can be made into a queue. A frontend framework can render a mostly static page. A distributed collection of services can power an application that would fit comfortably in one process.

AI is extraordinarily good at filling the distance between “possible” and “appropriate” with code.

But every line that closes that distance becomes something a human may eventually have to understand, secure, operate, migrate, or remove. The AI experiences none of that future pain. It does not get paged at 2:00 a.m. It does not explain next year’s infrastructure bill. It does not watch a new engineer spend a week discovering why a clever abstraction exists.

It simply sees a solvable next step.

Ask a Graybeard About CGI and Perl

Older engineers have already lived through several generations of perfectly workable technology becoming the wrong default. And good engineers learned (painfully) to move on to the new.

Ask a graybeard about CGI scripts and Perl powering the web. Long before Javascript or PHP, the web was static and quite successfully. Perl was expressive, portable, practical, and superb at turning messy text into useful output. CGI offered a wonderfully direct model: a request arrived, a program ran, and a response came back. And suddenly the web was Dynamic. Instead of “Hello World” we could do “Hello <firstname>”

Then scale, security expectations, application complexity, deployment practices, and the economics of computing changed. Persistent application servers, richer frameworks, managed platforms, and new languages displaced much of that stack. Perl and CGI did not suddenly become incapable. The context around them changed until their tradeoffs were usually no longer the tradeoffs we wanted.

Old-school engineers learned—sometimes painfully—that technical maturity includes saying goodbye to tools they once mastered.

AI cannot do that. Just like the Jackson 5, your AI agent Never Can Say Goodbye.

Reducing the cost of writing code does not eliminate the cost of owning a system.

Larry Wall’s Virtues—and Why AI Has None of Them

Larry Wall famously described the three great virtues of a programmer as laziness, impatience, and hubris. They are preserved at https://thethreevirtues.com/ Each vice conceals judgment AI cannot instinctively possess.

Laziness motivates a developer to avoid unnecessary work—not only today, but tomorrow. A properly lazy engineer automates repetition, removes needless components, and asks whether the feature needs to exist at all.

AI does not mind work. Generate 5,000 lines or delete 5,000 lines: to the model, both are token sequences. It may imitate simplicity when instructed to, but it does not resent needless complexity. It has no Saturday afternoon it would rather keep. The cynical engineer might think frontier models actually prefer spending tokens…

Impatience makes a developer angry at wasted time. It leads us to improve feedback loops, shorten builds, simplify deployments, and reject systems that are technically elegant but miserable to use.

AI does not wait in the human sense. Even when a process is slow, the AI has no frustration accumulating across weeks of development. It does not dread the twelve-minute test suite or the six-step release ritual. Without an explicit request to measure those costs, it will often accept them as features of the landscape.

Hubris is the pride that makes a developer want to build something worthy of other people’s respect. In its healthy form, it creates accountability: I chose this design, I put my name on it, and I expect another engineer to judge it.

AI has no reputation at stake. AI doesn’t get fired if it underperforms. It can produce code that sounds confident, but confidence is not pride and fluency is not ownership. It will not feel embarrassed when an architecture collapses under requirements that should have been anticipated.

AI can perform the behaviors associated with all three virtues. It can automate, optimize, simplify, and critique. But it does not possess the motives behind them. Unless we deliberately supply the questions, constraints, and standards, it has no durable reason to prefer a small, boring system over an impressive maze that passes the tests.

A Note to Younger Engineers

If you are entering software development through AI-assisted coding, do not mistake rapid implementation for architectural validation.

When the application runs after twenty prompts, you have learned that the chosen stack can produce the desired behavior. You have not yet learned whether it is the simplest stack, whether it will survive deployment, whether its dependencies are justified, or whether you understand it well enough to repair it without the transcript that created it.

Pause before the codebase becomes large and ask a different brand of AI to argue against the existing design:

  • What is the simplest architecture that satisfies the requirements?
  • Which component could be removed entirely?
  • What assumptions drove the choice of language, framework, database, and hosting model?
  • What would an experienced engineer consider overengineered here?
  • Under what future conditions should this architecture be replaced?
  • What are the operational costs, not merely the implementation costs?

Better still, start a clean conversation and describe the problem without revealing the current stack. An AI asked to critique ITS OWN accumulated work is anchored by that work. An independent design pass is more likely to expose alternatives.

Learning architecture means learning to see costs that do not appear in a demo: cognitive load, failure modes, security boundaries, vendor dependence, observability, migration difficulty, and the number of concepts a maintainer must hold in mind. AI can help evaluate every one of them—but you must remember to ask.

A Note to the Graybeards

Experience is more valuable in the AI era, not less—but only if we use it for judgment instead of gatekeeping.

Knowing how we built systems twenty years ago is useful. Knowing why we stopped building them that way is priceless. Younger developers do not need lectures about earning the right to use powerful tools. They need the history embedded in our scars: which shortcuts became permanent, which abstractions leaked, which “temporary” integrations survived three acquisitions, and which elegant architectures made ordinary changes unbearably expensive.

At the same time, veterans must apply the same skepticism to their own favorite stacks. “This has always worked for me” is not architecture; it is autobiography. AI makes it easy to preserve our habits by automating away their friction. We should not confuse that renewed convenience with renewed suitability.

Our job is not to compete with AI at typing code. It is to recognize when typing more code is the wrong response. AI can’t be lazy. You and I however are experts in the field of lazy, and should help AI get there 😉

Make Architecture a Deliberate Interrupt

AI-assisted development needs explicit stopping points because the machine’s natural momentum is forward. I personally use Spec Kit to design my inflection points around projects and tasks. YMMV, but do SOMETHING

Before implementation, require competing designs. After the first working version, schedule an architectural reset: restate the requirements, inventory the chosen tools, and ask what can be removed. Before production, have someone who did not participate in the initial build explain the system back to you. If they cannot, the problem may not be their understanding.

Most importantly, separate two questions that AI’s speed tends to blur:

  1. Can we make this work?
  2. Is this what we should make?

AI is rapidly becoming astonishingly good at the first question. That makes human responsibility for the second question greater, not smaller.

The future will not be divided between engineers who use AI and engineers who do not. It will be divided between people who use AI to accelerate judgment and people who use it to avoid judgment. The code will probably work either way for a year. Changing architecture after a year is almost impossible to do well.

But if you ask AI they’ll get right on it. =)


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *