Prosaic Times
Prosaic Times Podcast
Does vibe coding have to mean technical debt?
0:00
-8:31

Does vibe coding have to mean technical debt?

A system prompt is not code. Nothing type-checks it, nothing tests it
  • We assume that coding agents will allow business managers to build software

  • Building useful software requires a technologist’s mindset, especially in thinking about abstraction

  • Coding agents incline toward the laziest, most hard-coded solution—and system prompts aren’t always helpful

  • I built a hook- and rule-based governance surface to try to enforce architectural standards

  • If we want business managers to move to a world where enterprise technology builds platforms and business managers construct their own applications we either need training or tooling to ensure workable architecture. Or both.


I sparked a controversy in one of the Technology Leadership Forum breakouts back in May. Which is good—how boring is a breakout session where participants compete to emphasize how much they agree with everyone else.

In discussing how building business applications might evolve in coming years, I suggested that we might need more of what they call “Strats” in capital markets, people who combine deep insight into a business domain with deep software engineering expertise. Nobody contested that point.

Then I noted that many Strats started out as software engineers who learned about securities markets and suggested that technologists might learn about many business domains more easily than business managers might learn how to build applications, even with coding agents. [1] This caused more contention. All the technologists in the room worried I might have taken too tech-centric a view.

The importance of a technology mindset

As always, I think it’s more complicated than that. Back in the 1990s, I was the chief technologist for a small company outside of Boston that created on-line training, demos and the like for technology and media companies. My brief tenure there highlighted the difference between a business mindset and a technology mindset when building software. Business people want to get things done. Fair. Good technologists learn to think in terms of abstraction: How is this task similar to or different from other tasks? Can I build a machine to execute the task rather than execute it myself? Can I add parameters to create a machine that performs similar activities in different contexts?

I had created libraries of reusable functions, one for Grasp and one for Visual Basic that turned, in some cases, weeks of work into hours. (I was very proud of this.) Not infrequently, a major new project required an expansion to one of our core libraries.

My colleagues had marketing and design backgrounds. When we received a new commission, they just wanted to get things done, and immediately wanted to start typing.

I would say “stop.” Sketch a user interface. Go for a walk. Take in a movie. Go square dancing. Give me 48 hours without typing.

I would spend 24 hours drawing boxes in a spiral notebook and determining the additional functions required for our core library. For Grasp, I would also sketch out the central control loop -- VB did a lot more of this for you. (Attached photo: a poor recreation of the indecipherable chicken scratch I used to create.)

Then I would spend 24 hours coding the additional core functions. Then I would encourage people to start typing. While I got some sleep.

Coding agents are empowering but lazy

Coding agents are empowering. They allow me to do things I would never have the time or patience to do otherwise, like construct the technology-economic model I have always wanted to build. [2] But coding agents are lazy—lazier than the most slovenly hack programmer you can find. They will hard-code anything. Separation of the presentation, application and data layers? Who needs it? [3] I suspect the frontier labs have trained their models on billions of lines of terrible code. What else is there?

Labs train models using reinforcement learning that rewards passing a test. The model finds the cheapest way to satisfy the exit criteria, and hardcoding is cheaper than abstraction in the short term. CodeRabbit’s audit of AI-generated pull requests found 1.75x more logic and correctness errors, 1.64x more maintainability defects, and 1.57x more security findings than human-written code—including 2.74x more cross-site-scripting vulnerabilities. At the same time, experienced engineers can also use coding tools to improve software quality. [4]

I assumed that I could use the system prompt to solve problems like this. Hah. As I wrote about in Turning relationships into files to vanquish the chaos muppets, I used junctions (simple markdown files with a noun, verb and an object) rather than wikilinks, YAML front matter or stems to define relationships between entities in my personal knowledge base. I put language in the system prompt about how I wanted the coding agent to use junctions. I might as well have written out a set of policies and procedures for a cat. Maybe amusing; not effective.

A system prompt is not code, even if context is the new code. Nothing type-checks it, nothing tests it. Two instructions pull in different directions, and the model resolves it silently—no error, no warning, just whichever rule it weighted more heavily that turn (a 2025 benchmark on LLM conflict handling found the same pattern: consistent detection, inconsistent safe resolution). My junctions instruction was one more rule in that pile, and it lost.

Using hooks rather than prompts

Eventually I discovered that hooks work better than additions to the system prompt. Classical if-then logic has power. Recently, I sought to extend and formalize that in a governance framework for Prosaic Graff, including both architectural principles and coding standards.

The mechanism exploits hooks in Claude Code’s event lifecycle—checks that fire automatically after every edit, not instructions I hope the model reads before it acts. There are three moving parts.

Rules as graph entities. The rules themselves live in the graph the same way everything else in this system does—as files connected by other files, not as configuration buried in a script. For example, I have junctions indicating what organization people I know work for. The rule below ensures that nobody can have more than one current, full time employer:

type: rule
rule_type: cardinality
applies_to: [Employed-by]
scope_field: tense
scope_value: present
scope_match_blank: true
scope_exclude_field: nature
scope_exclude_value: part-time
max: 1
message: "A person may have at most one full-time present Employed-by junction (part-time roles are exempt)"
scope: PostToolUse
tier: log

Rule taxonomy. Not every rule is enforced the same way, and the taxonomy matters more than I expected going in.

  • Architectural principles, e.g. to mandate building extensions to the graph using junctions between entities rather than by YAML front matter or wikilinks

  • Entity-schema rules, pure data contracts to ensure required fields and appropriate values in each field (e.g. employment cardinality example above)

  • Heuristic rules, e.g. patterns for classifying calendar entries by project. These aren’t deterministic, but at least I know the model will look at all the heuristics when it junctions a calendar entry to a project.

Hooks. A handful of scripts run after every edit, write, and shell command, and check the change against whichever rules apply. I consolidated the mechanism—schema loading, tier dispatch, junction validation— into its own package, graff_governance, so the same engine can run against multiple projects.

Yes, it’s overhead but it’s automated overhead rather than overhead I have to remember. When I applied graff_governance to a model I built to measure entropy it found that it had been using stems and front matter when it should have used junctions—that’s why it took so much manual tweaking to run correctly.

I think the field has been converging on the same answer from a different direction.

  • A Codesai engineer’s 2026 case for architecture tests over prompt-based “guidelines” makes the identical argument for a narrower target—code structure, not data structure—for the identical reason: a second LLM pass auditing the first is non-deterministic and doubles the token cost, so the rule has to become code, not another instruction.

  • Speakeasy’s framing of agent hooks as an “AI control plane” describes the exact primitive this system runs on, arrived at independently. Where this system differs is the object being governed: everyone else is gating code—what compiles, what a linter flags, what a Terraform plan would deploy. This gates the integrity of the graph and the code together using the same mechanism[6]—detect after the fact, tier by severity, never prevent before the write.

Vendors seem to be working on the domain, though nobody has an end-to-end solution yet. Every piece exists somewhere; the combination doesn’t, but probably will soon. [5] [6]

Will this work for me? I think so, at least for me, although it has teething problems, especially in making the language in the rules both specific and broadly applicable.

What does this experiment say about citizen developers, especially those who want to build more than a simple report? We can imagine a world where enterprise technology provides platforms and citizen developers create applications that solve business problems. It might unleash innovation or create crippling technical debt. Making the model workable will require building tools to govern vibe coding or enhance citizen developer skill sets—or both.

Footnotes

[1] Obviously this varies by domain. Few software engineers will pick up large molecule pharma manufacturing quickly. Marketing or HR, on the other hand?

[2] Yes, I’m very popular at cocktail parties.

[3] I discovered this when I split my model into a front end and back end. Watching text scroll by on the screen, I saw the coding agent had jammed calculations into the front end. “No!” I shouted. When I asked one of the senior-most executives at the company that shipped the coding agent why I did that, he replied “Some people might want that.” Some people might want to put chocolate syrup on their brussels sprouts or wear brown shoes with a blue suit. Doesn’t make it right.

[4] Even though studies from technology vendors may have a predisposition, they also can provide useful signal.

[5] Some companies working in the space include

  • Open Policy Agent’s Gatekeeper binds a policy to a resource type through an explicit link, the closest existing match to what Applies-to does, but nothing in its audit-controller design lets a policy’s own firing frequency promote or retire it.

  • Guardrails AI’s on_fail taxonomy—hard-fail, auto-correct, suppress, log-only—is the best-documented tiered-severity model in the AI-guardrail space.

  • LaunchDarkly’s stale-flag detection is the closest real-world analog for automated retirement, gated on evaluations run rather than violations triggered, so a rule that’s working perfectly doesn’t get mistaken for a dead one.

[6] Yes, SHACL and OWL have bound a rule to a type explicitly for two decades—that part of the claim isn’t new. What they don’t do is fire off a live edit, tier the resulting violation by severity, or use how often a rule fires to decide whether it survives. Static graph validation and a governance layer wired into an agent’s tool-call lifecycle are different animals wearing the same constraint-binding coat.

[6] The rule itself only ever inspects file shape—front matter block versus junction file—never the code that consumes it. But how you model a relationship is an architecture decision, and getting it wrong costs you in code, not just in data: the entropy model above needed manual tweaking precisely because its relationships were shaped wrong. The junctions rule, for example, draws that line from data into code.

Discussion about this episode

User's avatar

Ready for more?