Skip to main content
Test Double company logo
Services
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Impact
Drive results that matter
Legacy Modernization
Renovate legacy software systems
Pragmatic AI
Solve business problems without hype
Upgrade Rails
Update Rails versions seamlessly
DevOps
Scale infrastructure smoothly
Technical Recruitment
Build tech & product teams
Technical & Product Assessments
Uncover root causes & improvements
Case Studies
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say Hello
Test Double logo
Menu
Services
BackGrid of dots icon
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Impact
Drive results that matter
Legacy Modernization
Renovate legacy software systems
Pragmatic AI
Solve business problems without hype
Cycle icon
DevOps
Scale infrastructure smoothly
Upgrade Rails
Update Rails versions seamlessly
Technical Recruitment
Build tech & product teams
Technical & Product Assessments
Uncover root causes & improvements
Case Studies
Solutions
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say hello
Leadership
Leadership
Leadership
AI

What does "Good Code" even mean now?

For decades, we've optimized code for human readers. Agentic coding is forcing a renegotiation of what "craft" means, and that's shifting emphasis from code-level readability to system-level observability and problem-space incrementalism.
Doc Norton
|
March 24, 2026
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Part two of a three-part series on agentic coding and what it means for the profession. Part one made the case that we're in the middle of a sea change. This one asks what that means for how we work.

For decades, we've debated what makes code "good." Variable names. Method length. Cyclomatic complexity. Nesting depth. Coupling. Cohesion. SOLID principles. The list goes on.

And most of it is right. But most of it is also built on an assumption we've never had to question before: that humans are the ones reading, reasoning about, and maintaining the code.

That assumption is crumbling fast.

Craft has always been about humans

Martin Fowler put it plainly: "Any fool can write code a computer can understand. Good programmers write code that humans can understand."

That framing has been a north star for our community for a long time. And for good reason. The principles we've developed around our craft exist for two reasons. First, fit for purpose: does the code do what it's supposed to do? Second, human maintainability: can a person read this six months from now and understand it? Can someone new to the codebase find their way around? Can I debug this at 2 a.m. without wanting to flip a table?

Human maintainability isn't a hidden variable exactly. It's actually the one we talk about most directly with developers, because it's the most immediately relatable. "Write this so future-you can understand it." That lands. That resonates. It's arguably the most accessible on-ramp to good practice precisely because we can reason about it from personal experience.

But it's also so omnipresent in the argument that we've stopped noticing it as an assumption. We've treated it as a given. We call things "good" or "bad" as if those judgments are purely about the code itself. They're not. They're about the code in the context of a human reader.

Take naming. Why do we care so much about variable names? Not because the compiler does. Because humans need to reason about intent. A well-named method is a gift to the next person who has to touch it. A poorly named one is a puzzle they'll have to solve before they can get any real work done.

Or method length. Why do short methods matter? Not because a long one won't execute. Because a human can hold a short method in their head. A 400-line function isn't "bad" in some abstract cosmic sense. It's hard for a person to reason about.

Same with complexity. Cyclomatic complexity, nesting depth, cognitive load: these are all human concerns. We built tools to measure them because we discovered that humans make more mistakes, take longer, and feel worse about code that piles it on.

Fit for purpose is the prerequisite. Human maintainability is what we've been optimizing above that for a very long time.

My first paying gig was teaching this

I want to be clear that I'm not some recent convert to caring about craft. This has been the cornerstone of my career for 35-plus years, since before I had precise language for it.

My first paying gig as a consultant was teaching other developers how to write code "safely" in FoxPro. If you're not familiar: FoxPro was an interpreted language with wonky scoping, loose types, and enough sharp pointy features to let you run very fast with very large scissors. It was entirely possible to write code that worked, shipped, and then became completely unmaintainable the moment the original author left the room. I spent years helping people understand the difference between code that runs and code that holds up.

I've been learning and honing craft ever since, with one consistent conviction: code needs to be first fit for purpose and second fit for humans. That second part has never felt optional to me. It's been as important as the first.

Which is why this next part is genuinely difficult for me to write.

Agentic coding changes the equation

Agentic coding systems don't struggle with long methods. They don't lose the thread at level four of a nested conditional. They don't get confused by a poorly named variable if they have enough surrounding context. They can hold an entire codebase in scope while making a change.

I'm not saying they're perfect. I'm saying the specific failure modes we've been designing around for fifty years are not their failure modes.

So what happens to "good" when the primary author and maintainer of the code isn't a human?

The fit-for-purpose piece still matters, maybe more than ever. Does the code do the right thing? Is it correct? Is it observable enough that we can tell when something goes wrong? Those aren't going away. But a significant portion of what we call craft, the parts optimized for human readability and human reasoning, is on shaky ground.

We're at the very early stages of this shift, and I'm not sure enough people in the industry are reckoning with it yet. More urgently: this isn't a shift measured in years. We need to be thinking in months. It is moving that fast.

The behaviors that survive, and the ones that need rebalancing

I've spent a lot of time developing a framework around the behaviors that characterize effective teams. Two of them are running headlong into this shift right now: composition and creating simple things in small steps.

Composition, being meticulous about how software components are assembled, has always served dual purpose. Good composition makes systems malleable and observable. It also makes them navigable for humans. The first purpose survives agentic coding just fine. High cohesion, low coupling, systems that are relatively independent and reliable: these matter regardless of who or what is writing the code. The second purpose is getting complicated. The specific conventions we've used to signal compositional quality to human readers are going to need reexamination. The goal stays. Some of the rules we built to serve that goal may not.

Small steps is where the shift gets really interesting, and where I'm doing the most work on my own thinking.

For years, I've advocated for breaking down code problems into smaller pieces. Short methods. Small classes. Narrow, focused functions. The reasoning was sound: small steps leave room for learning, preserve optionality, and make it easier to change direction when you discover something new. That reasoning hasn't become wrong. But I need to be honest that a significant portion of that argument was also rooted in human cognition. Small pieces are easier for people to reason about, test, and modify.

The bottleneck of developer speed is disappearing. Agentic systems can produce robust solutions quickly in ways that would have taken a team of humans weeks. The friction that made "build it small so you can change it easily" such critical advice is evaporating.

But the principle behind small steps was never really about code size. It's about maintaining optionality and creating feedback loops. That part isn't going anywhere. It's just shifting in emphasis.

Small steps: More problem space, less implementation technique

Small steps was always about both the problem space and the implementation technique. But the balance is shifting.

The question used to lean heavily toward: can we break this code problem into smaller pieces so we can learn as we go? The question now leans toward: can we break this problem space into smaller deliveries so we can validate what the market actually needs as we go?

Understanding has always been the primary constraint. Do we know what problem we're solving? Do we know if our solution is actually solving it? Are we building the right thing? Developer speed compounded that constraint by slowing down the feedback loops we depend on to learn. Slower development meant slower delivery, which meant slower validation, which meant slower understanding. The two constraints were entangled.

Now the developer speed piece is evaporating. Which means understanding is exposed, nakedly, as the thing that actually limits us. And agentic coding makes it faster and cheaper than ever to build the wrong thing at scale. That changes the risk profile considerably. The answer isn't to slow down. The answer is to stay disciplined about incrementalism at the product level: releasing smaller, validating more often, treating assumptions about what users need as hypotheses to test rather than requirements to build.

I still want teams creating simple things in small steps. The "simple things" part is unchanged. Unnecessary complexity is still unnecessary complexity, regardless of how fast you can generate it. But the emphasis of "small steps" is shifting from breaking down code problems into smaller chunks toward breaking down the problem space into smaller deliveries that each teach us something.

What should we actually optimize for now?

I don't have a complete answer. I'm not sure anyone does yet. But here's how I'm thinking about it.

The questions themselves have shifted. "Can a human read this easily?" isn't the wrong question, but it's now clearly insufficient on its own, and its importance is waning. The more pressing question is: when something goes wrong, and something will always go wrong, can we tell what happened fast enough to fix it? Can we observe the system's behavior without having to read every line of code it contains? Can we understand what it's doing at a level that lets us take responsibility for it?

That last part matters more than ever. We're still the ones who care when the system fails. We're still the ones who need to understand what happened and decide what to do next. And there are decisions being made in software that have real consequences for real people. Humans need to be able to understand those decisions well enough to own them. That's not a coding standards question. It's an ethics question. But it touches code directly.

Observability and accountability at the system level: those are becoming more important, not less. Here's why. When humans write and maintain code, they're in it. They develop intuition about the system through direct contact with its internals. When agents write and maintain code, humans move up a level. We're above the code, not in it. We lose that intuitive feel, and we need the system to compensate by telling us what it's doing clearly and loudly, through logs, metrics, alerts, and behavior we can trace without reading every line. That's not a weakness of agentic coding. It's just a different relationship with the codebase, and it requires us to be more deliberate about observability than we've ever had to be before.

The risk isn't caring less about quality

This isn't an argument for lowering standards. It's an argument for examining which standards are actually serving the outcomes we care about and which ones are serving a context that's changing.

The instincts developed over years of caring about code quality aren't suddenly wrong. But they need to be held a bit more loosely. The specific rules we've derived from those instincts were derived in a particular context. That context is changing faster than most of us expected.

The risk isn't that we stop caring about quality. The risk is that we confuse the rules for the reasons behind the rules. Rules change. Reasons evolve. Holding the reasons clearly is what lets us figure out what still applies and what needs to shift.

I've spent 35 years believing that fit for purpose and fit for humans were both non-negotiable. I still believe that. I'm just starting to reckon with the possibility that "fit for humans" is going to mean something meaningfully different in the next few years than it has for the last few decades.

Agentic coding isn't the end of craft. It's the beginning of a significant renegotiation of what craft means. And some of what gets renegotiated is going to be uncomfortable, especially for those of us who've built our professional identity around certain convictions about how good software is made.

The sooner we lean into that conversation, the better positioned we'll be to shape where it lands.

So what does "good" mean now? We're going to find out together. And we'll get to a better answer faster if we stop pretending the question isn't changing.

Doc Norton is Vice President of Delivery at Test Double and has extensive experience in building great software and great teams, including focuses within his career in software development, software architecture, product management, technology leadership, agile coaching, organizational agility, and systems thinking.

Trying to figure out what software craft is now?

Approaches may feel different. Your team may be resistant. We can help.

Find out more

Related Insights

🔗
This has happened before. It's happening again.
🔗
Quality you can’t generate: AI is only as good as your constraints

Explore our insights

See all insights
Developers
Developers
Developers
Red-Green-Refactor your context

You committed the fix. But everything you learned along the way went nowhere. A practice borrowed from TDD's red-green-refactor loop gives that hard-won knowledge somewhere to land.

by
Rick Reilly
Leadership
Leadership
Leadership
This has happened before. It's happening again.

Doc Norton has navigated two genuine sea changes in software development—mainframes to PCs and desktop to web. Agentic coding is the third, but unlike the prior shifts that expanded the profession, this one compresses it. The timeline to move is shorter than you think.

by
Doc Norton
Developers
Developers
Developers
Three Amigos with AI: Stop building the wrong thing faster

AI coding assistants are remarkably fast at building the wrong thing. This post adapts the Three Amigos practice to give AI an unambiguous contract—before the first commit, not after the fifty-seventh.

by
Andy Vida
Letter art spelling out NEAT

Join the conversation

Technology is a means to an end: answers to very human questions. That’s why we created a community for developers and product managers.

Explore the community
Test Double Executive Leadership Team

Learn about our team

Like what we have to say about building great software and great teams?

Get to know us
Test Double company logo
Improving the way the world builds software.
What we do
Services OverviewSoftware DeliveryProduct StrategyLegacy ModernizationPragmatic AIDevOpsUpgrade RailsTechnical RecruitmentAssessments
Who WE ARE
About UsCulture & CareersGreat CausesEDIOur TeamContact UsNews & AwardsN.E.A.T.
Resources
Case StudiesAll InsightsLeadership InsightsDeveloper InsightsProduct InsightsPairing & Office Hours
NEWSLETTER
Sign up hear about our latest innovations.
Your email has been added!
Oops! Something went wrong while submitting the form.
Standard Ruby badge
614.349.4279hello@testdouble.com
Privacy Policy
© 2020 Test Double. All Rights Reserved.

Trying to figure out what software craft is now?

Approaches may feel different. Your team may be resistant. We can help.

Find out more