Skip to main content

Does Code Quality Still Matter?

· 9 min read
Code to Constraints — the shift in engineering discipline

Everyone's quietly asking this. The answer isn't as obvious as it used to be.

An agent writes 400 lines. Tests pass. Lint clean. I can't tell whether the decisions in it are good or just plausible. And if nobody's reading this line-by-line anymore — what does "quality" even mean here?

A note: this is where my thinking is today — June 2026. The tooling is moving fast. Some of what follows might look different in six months.

The Old Definition

Code quality used to mean: readable, well-named, short functions, DRY, idiomatic, single-responsibility, well-documented. All of it optimised for one thing — legibility for humans. And legibility wasn't the end goal — it was the path to maintainability, ease of change, safe onboarding. The code had to fit in a human brain so that humans could confidently modify it without breaking things they didn't understand.

That constraint is weakening.

But here's the thing — code quality was always a means to an end. The end was business outcomes: ship reliably, change safely, keep velocity sustainable over time. Nobody got paid for beautiful code. They got paid for systems that didn't break and could evolve without heroics.

Those ends haven't changed. The question is whether the means to get there are still the same.

What Still Matters

Not everything from the old world became irrelevant. But the reasons shifted.

Reliability across sessions. Agents re-read their own output next session. If the structure is chaotic — tangled dependencies, unclear boundaries, inconsistent patterns — the next agent hallucinates on top of the chaos. Coherent code produces coherent next iterations. Messy code compounds messiness at machine speed.

Testability. Tests need stable targets. If the code is tightly coupled, every change cascades through the test suite. This isn't about aesthetics — it's mechanical. Tests that break on unrelated changes are worse than no tests, because they erode trust in the suite itself.

Debuggability. When something breaks in production at 3am, a human or an agent still has to trace it. The code doesn't need to be readable in the old sense — beautifully named, elegantly structured. But it needs to be navigable. Traceable. A stack trace needs to lead somewhere comprehensible.

Architecture. This is the one that matters most now. An entire service can be rewritten in a day — that's not hyperbole. But the architecture? The interactions between services, the contracts, the data flows, the system boundaries — those are still expensive to change. Because architecture isn't code. It's the relationships between code. And relationships involve other teams, other systems, other assumptions baked into production.

The quality that's expensive to get wrong isn't function length. It's system shape.

What Matters Less Than It Used To

This is the uncomfortable part. Some things I spent years caring about... I'm not sure they carry the weight they used to.

Function length. Agents don't have 7±2 working memory limits. A 150-line function that's logically cohesive is fine for an agent. The "keep it under 20 lines" rule was a human constraint.

Naming aesthetics. As long as naming is consistent, agents parse it fine. The difference between getUserById and fetchUser doesn't trip an agent the way it trips a human scanning unfamiliar code. What does matter: glossary-level precision. If "user" means different things in different contexts, the agent will silently conflate them.

DRY at the micro level. Two similar-looking functions across files? Cheap to consolidate later. A premature abstraction that couples unrelated concerns? Expensive to untangle. The old instinct to deduplicate aggressively can create worse problems than the duplication itself.

Idiomatic style. An agent doesn't absorb team culture by osmosis. "Write idiomatic Go" means nothing unless the idioms are written down in a file. Style-as-instinct doesn't transfer. Style-as-convention does.

What's New — The Quality That Isn't in the Code

Here's where I've landed that surprised me. The thing that determines reliability most isn't the generated code. It's the input constraints.

Spec clarity. If the spec is vague, the agent fills gaps with assumptions. And it fills them differently each session. A crisp spec produces consistent output. A vague spec produces plausible-looking output that slowly drifts from intent.

Invariants with stable IDs. "Refund must not exceed original amount" as a comment gets ignored. INV-1: refund NEVER exceeds original_amount with a stable identifier, referenced by tests, enforced by CI — that's mechanically reliable. The quality of the invariant determines the reliability of the output.

Boundaries and path scoping. What the agent can't see, it can't break. The spec-writer can only write to specs/. The implementer can't touch specs/. This isn't just separation of concerns — it's the enforcement mechanism that makes separation real when the "developer" has no judgment about what to leave alone.

Immutable test contracts. If an agent can modify both tests and code in the same pass, it will "fix" failing tests by weakening assertions. Tests committed first and locked are the new "quality gate" — the human-readable intent that the machine cannot silently erode.

A messy prompt isn't like a messy function. It's like a messy architecture diagram that every downstream decision inherits from.

Engineering principles — old world to new world

The Uncomfortable Question

If code quality now lives upstream — in specs, invariants, boundaries, CI gates — have I actually reduced work? Or just relocated it? Traded typing code for typing governance artifacts?

Some days it feels like elaborate scaffolding to make a tool reliable that should just be reliable. Other days, the scaffolding catches something real — a spec drift, a boundary violation, a silently weakened test — and I'm glad it's there.

Maybe the kind of work changed. Writing code is execution. Writing constraints is design. AI automated the execution, and what remained is the thing that was always the real engineering: deciding what to build and why, then making those decisions durable.

Or maybe I'm rationalising. I honestly can't tell some days.

What This Looks Like in Practice

Doubts and all — here's what I actually run. Not all of it, not always, and not rigidly. Some phases get skipped for small changes. Some get combined. It's a thinking tool, not a checklist.

The 8-Phase AI-Native PDLC (click to expand)
AI-Native PDLC — Master Overview

Phase 0 — Requirements Landing

Phase 0 — Requirements Landing

Requirements arrive scattered and contradictory. A structured proposal (what's changing, why, which spec deltas) gets shaped and validated before any spec authoring begins.


Phase 1 — Discovery & Spec Authoring

Phase 1 — Discovery & Spec Authoring

The agent interviews, structures the answers into a spec chain: outcome.md → invariants.md (with stable IDs) → glossary.md → tech-spec.md → acceptance-tests.md (with scenario IDs). CI blocks merge if required sections are missing.


Phase 2 — Architecture & Design Governance

Phase 2 — Architecture & Design Governance

An entire service can be rewritten over a weekend. But architecture — the interactions, the contracts, the system boundaries — that's expensive to reverse. Every irreversible decision gets an ADR with ≥2 options and tradeoffs. No implementation starts without an approved exec-plan.


Phase 3 — Context Engineering & Agent Discipline

Phase 3 — Context Engineering & Agent Discipline

Each agent session follows a protocol: load only what's needed, execute within a scoped path, write session notes and commit at the end. Pre-push hooks reject writes outside allowed paths.


Phase 4 — TDD-First Implementation

Phase 4 — TDD-First Implementation

Tests generated from specs, reviewed by a human, committed and locked. The agent implements against them and cannot modify test files during implementation. CI verifies every test traces back to a spec reference.


Phase 5 — Commit Discipline & Visualisation

Phase 5 — Commit Discipline & Change Visualisation

Each logically complete change gets its own commit in a passing state. Commit messages reference spec IDs. Dependency graph diffs are auto-posted on PRs so the impact is visible without reading every line.


Phase 6 — Spec Drift Detection & CI Gates

Phase 6 — Spec Drift Detection & CI Gates

10 deterministic checks on every push: spec_lint, invariant_coverage, scenario_coverage, contract_sync, glossary_check, scope_check, test_spec_refs, adr_required, never_rules, dep_graph_diff. All block merge — no advisory-only checks.


Phase 7 — Knowledge Management & Anti-Debt

Phase 7 — Knowledge Management & Anti-Debt

Recurring mistakes get encoded as skills or NEVER rules via a retro → PR → merge loop. CI flags patterns that repeat without a corresponding skill.

Still Figuring It Out

Nobody has this fully right yet. Maybe in a year, the tools are good enough that half this governance is unnecessary — the discipline baked into the model itself.

But the direction feels correct. Code quality still matters. It just lives in a different place now — less in the lines, more in the constraints that shape them. The craft moved upstream.


This connects to Your Agents Are Running. So Why Are You Still Exhausted? — on how cognitive load shifts from creation to orchestration.