[]Developer Philosophy26 min read

$ Harness-Driven Development: More Than Source Code

Software is no longer just its source code. Its harness — the tools, memory, skills, and rules guiding the agents that build and run it — is now first-class.

Cover image for: Harness-Driven Development: More Than Source Code
// cover_image.render()

A few days ago I stopped and actually read the file tree of my most active repository. Not the src directory — the rest of it.

There was a CLAUDE.md at the root. A .claude directory holding agents, skills, and commands. A publishing-cadence document. A roadmap. Research notes from an architecture debate two agents had while I was asleep. Migration plans for migrations that already shipped. Critique outputs from a review loop. Scratchpads full of intermediate thinking that fed decisions I now take for granted.

Very little of it was written for the people who use what the repository builds. Surprisingly little of it was even written for me. Most of it was written by agents, for agents — instructions, memories, and working notes that exist so the next session starts smarter than the last one.

I tried to imagine actually deleting it. The site would still build. The tests would still pass. Nothing a user touches would change. And yet the project would get worse in a way I could feel but not point to — the next review would miss context, the next migration would re-derive settled decisions, the next agent session would ask questions the repository had already answered.

My first instinct was to tidy it all away. My second was that I was looking at the software.

We have named ways of working before — object-oriented programming, domain-driven design, test-driven development. I think the way we work now deserves a name too, and the honest name centers neither the model nor the code.

I’ve started calling it harness-driven development — though I don’t claim the phrase. Others have been reaching for the same language this year, and OpenAI now writes about “harness engineering” outright. What I want to add is a specific shape: an outer harness that builds the system and an inner harness that operates it, and the argument that this environment is now a first-class part of the software rather than scaffolding around it.


The repo grew a second layer

Look at any repository that has been through a few months of serious agent-assisted development and you will find the same thing: a second layer growing alongside the source code.

Part of that layer is native to the tools: skills that teach the agent a procedure, rules that constrain it, hooks that fire around its actions, and memory files that persist what it learned. Context documents — CLAUDE.md, AGENTS.md, and their cousins — tell it what this project is and how to behave inside it.

The other part is produced: research summaries, design debates, migration plans, review critiques, scratchpads of intermediate reasoning. Documents the agent wrote because it needed them to think, and kept because the next agent will need them to think too.

We used to complain that engineers don’t write enough documentation. That problem quietly inverted. There is now far more of it than we ever wrote by hand, scattered exactly where the work happened, and most of it was never meant for human eyes.

I wrote before that your docs are not the prompt — descriptive documentation is a weak signal for a coding agent next to the prompt and the code. That is still true, and it is why this second layer is a different animal. A README is a brochure: it describes the software and hopes someone reads it. A skill, a rule, or a memory file is a control surface: it steers the machinery that builds the software.

Which means the old reflex — treat everything that isn’t source code as clutter or ceremony — now deletes real capability. Remove a rules file and behavior changes. Lose a research document and the next session pays to re-derive it. Let a skill drift out of date and every workflow that loads it degrades. These files carry the operational weight we used to reserve for code, and they deserve the same treatment: version control, review, deliberate design.

This layer doesn’t document the software. It is the software.

You are not using a model, you are using a harness

We say “I’m using Claude” or “I’m using GPT” the way we used to say “I’m on Java.” It’s shorthand, and it hides the thing that actually matters.

You are not using a model. You are using Claude Code, or Codex, or Cursor. You are using a harness.

A harness is everything wrapped around the model that turns raw intelligence into a working agent: the tools it can call, the memory it carries between sessions, the skills it can load, the rules that constrain it, the runtime it executes in, the processes it follows when it plans and reviews, and the interfaces through which a human steers it. The model is one component. A decisive one — a harness is only ever as good as the model it harnesses — but still one component.

I’ve watched this play out in a single afternoon. Give the same frontier model the same task three ways — through the raw API, inside a full coding harness, and inside a purpose-built agent with curated tools — and you can get three very different engineers. Same weights, comparable knowledge, sharply different competence, because competence, for an agent, is mostly a property of what surrounds it.

The distinction shows up even inside one product. Codex runs across several surfaces — a terminal CLI, an IDE extension, a cloud task runner, and the ChatGPT app — and OpenAI has described the same core agent loop sitting under all of them. What each surface changes is the envelope around that core: which tools are wired in, which permissions are granted, what runtime it can reach. On a locked-down surface it can read and suggest; give it a sandboxed machine and write access and it can run a test suite, edit files, and open a pull request. The model and the core loop never changed — the harness wrapped around them did, and with it the whole range of what the agent can actually do.

The same model in a different harness is a different engineer.

The frontier labs clearly understand this, because they stopped behaving like model vendors. The API business — here are the weights, build what you want — still exists, but the strategic energy goes into the harnesses: the coding tools, the desktop apps, the migration paths that pull your existing skills and setups from one ecosystem into another. They are no longer competing only on who has the smartest model this quarter. Increasingly, they are competing on who owns the environment where the work happens.

Once you see this, the benchmark conversation gets less interesting and the harness conversation gets much more interesting. The question stops being “which model is smartest” and becomes “which environment lets the intelligence I already have actually reach my problem.”

The editor became the least important part of the IDE

If the labs are competing on the environment, the development tools show what that competition looks like up close.

Cursor made the early move, shifting from tab-completion — the model as a faster keyboard — toward the agent window as the primary surface. Claude Code skipped the editor entirely and moved into the terminal. OpenAI folded Codex into ChatGPT on the Mac and made the conversation itself the development environment. VS Code grew an agent mode, Zed is rebuilding around one, and Antigravity arrived agent-first from day one.

Different products, different bets, one convergence: the text editor — the thing an IDE fundamentally was — is becoming the least important pane in the window. What these tools compete on now is orchestration: how well they let you direct agents, run work in parallel, review what comes back, and keep the whole thing steerable.

Look at where the feature velocity goes: plan modes and ask modes, permission levels that run from confirm-every-step to full autonomy for sandboxed work, background tasks, sub-agents, side chats, browser control with annotation and feedback, PR integration, automation hooks. None of these make typing faster. All of them make directing agents better. That is what the tools believe their job is now, and I think they’re right.

An IDE used to integrate tools for a human who writes. These environments integrate tools for agents that build, and a human who decides. Every serious development tool is becoming a harness, whatever its marketing says.

The Bash tool changed everything

If you want a single reason this generation of tools feels categorically different, it is not the context windows and it is not the benchmark scores. It is that somebody gave the model a terminal.

The Bash tool looks humble next to purpose-built integrations, but it is the strongest tool an agent has ever been handed. Everything a developer can do from a shell, an agent can now attempt: grep and find at the small end; test runners, linters, and build systems in the middle; and at the far end, your project’s own scripts, your company’s internal CLIs, your deployment tooling. The whole accumulated tool culture of computing, inherited in one move.

This is the Unix philosophy paying its oldest dividend: agents didn’t need a bespoke integration for every tool humans have built over fifty years — they needed the one integration that reaches all of them.

Which inverts an assumption we’ve carried for years. We used to ask what the model could do, and the answer was bounded by whatever tools someone had hand-wired into it. Now an agent’s reach is bounded by what its environment exposes: what’s installed, what’s permitted, what’s documented, what’s discoverable. Drop a frontier model into an empty container and it is clever but helpless. Drop the same model into your real project — real scripts, real access, real conventions — and it becomes startlingly capable.

The ceiling moved from the model to the environment.

The outer harness builds the system

Here is the distinction I keep coming back to, the one that turns this from an observation into a practice. Every project now has two harnesses to design, and they answer different questions.

The first is the outer harness: the environment through which the software gets built. It is your coding tool — Claude Code, Codex, whichever you’ve chosen — plus everything your project layers on top of it: the skills, the rules, the hooks, the memories, the context documents, the scripts it knows how to run, the commands that only make sense in this codebase. The base tool is generic. The outer harness is yours.

Its job is everything we used to call development, in the widest sense of the word. Writing features, yes. But also researching options before anything gets written, planning, reviewing, testing, and debating designs — literally, setting agents against a question from different positions and reading the argument. Answering the questions your teammates would otherwise ask you in Slack. Producing the throwaway prototypes that inform the real build and then get discarded without guilt.

My favorite example of the outer harness at work is one I’ve written about before: opening pull requests you never intend to merge, because a PR is the outer harness’s native artifact — code, reasoning, preview, and conversation in one place. The outer harness doesn’t just produce merged code. It produces attempts.

A strong outer harness has a specific feel: you stop performing the mechanical parts of engineering and start directing them. The scarce resources — judgment, taste, accountability — stay with you; I’ve drawn that line before and won’t redraw it here. What changes is that everything else, the running and testing and researching and drafting, flows through an environment that has been deliberately taught how your project works.

And the teaching compounds. Every rule you write, every skill you add, every memory that persists makes next week’s work cheaper than this week’s. That is what it means for the harness to be part of the software: it accrues value the way good architecture does, invisibly, in the speed of everything that follows.

The inner harness operates the system

The second harness lives inside the product.

More and more systems now embed agents as features: a support flow that resolves issues instead of routing them, a back-office process that reconciles data overnight, an event handler that investigates an anomaly instead of merely alerting on it, a report that researches and writes itself on schedule. When your product can usefully run an agent — on a user’s request, as a background operation, in reaction to an event — that agent and everything around it is your inner harness.

It has the same anatomy as the outer one: a model, tools, permissions, memories, objectives. But the design constraints are harsher, because nobody is watching. The outer harness has you in the loop; the inner harness has your error budget. What tools does the embedded agent get? What is it empowered to do without a human? What does it remember between runs? How is its output checked before it touches anything real? Those are product decisions now, as consequential as any schema.

And the boundary between the two harnesses moves. Work migrates from outer to inner. A workflow you run by hand in your coding tool today — verify the deployment, triage the flaky test, regenerate the derived data — becomes, once it has succeeded enough times to be boring, something the system runs on its own. The outer harness is where processes get discovered and hardened. The inner harness is where they retire to.

The clearest example I can offer is running in the repository that publishes this essay. For months, keeping this blog healthy was outer-harness work I did in sessions: check that CI is green, hunt broken links, verify the RSS feed and the sitemap, look at what search engines and answer engines actually see, fix what’s trivial, file what isn’t. The procedure repeated until it stopped being interesting — which is exactly the signal to move it.

Today that procedure is written down as two persistent agents — one that sweeps for regressions, one that proposes discoverability improvements — with a scheduled workflow meant to run them weekly. I’ll be honest about the state of it: this example hasn’t yet earned the nobody-is-watching property I described above. The unattended schedule isn’t dependable yet. The recent scheduled runs have been failing, and for now I still kick the agents off by hand more often than not. What already works is the part that matters for this argument — the agents exist, their boundaries are defined, and the work has moved out of my head and into the harness. Turning that into reliable, unattended automation is exactly the kind of hardening that comes last, and I haven’t finished it.

The part worth studying isn’t the automation; it’s the decisions at the boundary. The QA agent may fix trivial regressions directly, but everything it ships still arrives as a pull request I review. The improvement agent may prototype, but only as draft PRs. And both operate under one hard rule: no agent touches the essays themselves unless I have explicitly opened a writing session — content is the part of this system where my hands stay on the wheel. Tools, permissions, memories, objectives, and a human gate exactly where the error budget is thinnest. Strictly speaking these agents still do development work — they open pull requests, not support tickets — and they’re designed to run outside my sessions. They are the blog’s back office, and the boundary keeps creeping: every time one of their filed issues teaches me a procedure, that procedure becomes a candidate for the next migration.

That migration is what I mean when I say these systems can begin to operate themselves. Not autonomy as a slogan — a pipeline. You develop a process with an agent, you watch it hold up, you move it inside, and your attention moves to whatever isn’t automated yet. The back office of software that keeps getting predicted isn’t a new department. It is this migration, repeated until it stops being remarkable.

Not every product needs an inner harness today. Some never will, and an outer harness alone already changes how a team moves. But I would bet that most serious systems end up with both — because once operating the system is a written-down process, and processes are things agents run, keeping a human on the crank becomes a choice you have to justify rather than a default you inherit.

The stack now has five layers

Put the pieces together and you get a stack. It is worth drawing, because the interesting part is which layers get the attention.

flowchart TD
  M[Foundation model<br/>capabilities, no process]
  B[Base harness<br/>Claude Code, Codex, Cursor]
  O[Project harness<br/>skills, rules, hooks, memories]
  I[Inner harness<br/>agents inside the running product]
  A[Application<br/>what users touch]

  M --> B --> O --> I --> A

  classDef strong fill:#0A0A0A,stroke:#00FFFF,color:#E0E0E0
  classDef weak fill:#0A0A0A,stroke:#666,color:#999
  class B,O,I strong
  class M,A weak

In plain text, the diagram is a five-layer stack, each layer specializing the one beneath it. At the base sits the foundation model. The base harness — Claude Code, Codex, Cursor — wraps the model. The project harness, your skills, rules, hooks, and memories, wraps the base harness. The inner harness sits above that, running inside the product itself. The application users touch sits on top. The three middle layers — base harness, project harness, inner harness — are the harness itself.

The two gray layers are the ones everyone talks about. The model, at the bottom, is the layer you control least — you pick one, and a frontier lab does the rest. The application, at the top, is the output: it is what users touch, but it sits downstream of everything below it.

The three layers in the middle are where your leverage actually lives. The base harness you choose. The project harness you accumulate on top of it — every skill, rule, and memory that specializes a generic tool into your team’s tool. Those two together are what I’ve been calling the outer harness. The inner harness you embed in the product itself. Each layer specializes the one below it, and each is a body of decisions that belongs to you, not to a lab.

Teams argue endlessly about the gray layers and improvise the middle three. I think that ratio is exactly backwards.

We used to reuse code, now we reuse process

Every previous era of software engineering had a unit of reuse. Functions. Objects. Libraries. Packages. Services. The entire open-source economy is built on the insight that solved problems should stay solved, and code was how we shipped solutions to each other.

Harnesses introduce a new reusable unit, and it isn’t code. It’s process.

A skill that runs a design review to your team’s actual standards. A security critique that knows what your threat model cares about. A migration workflow that plans, executes in slices, and verifies each slice before the next. A research loop that fans out, reads, and synthesizes with citations. A debugging procedure that instruments the code instead of guessing at it. None of these are libraries. You don’t call them; agents perform them.

And they travel in a way code never quite did. Code reuse always came with friction — a library assumes a language, a framework, a runtime, a particular shape of problem. A well-written process assumes almost nothing. One of the skills in this repository audits documentation for drift against the code it describes; nothing in it knows about Astro, or blogs, or the web. Point it at a Rust CLI with a stale README and the procedure runs unchanged — only the nouns are different.

The honest caveat is that process reuse has none of the verification infrastructure code reuse earned over decades. A schema can validate a skill’s shape — that its fields and frontmatter are well-formed — but it cannot prove the procedure is still sound: that the steps it encodes remain the right ones, that the standard it reviews against hasn’t quietly moved. No test fails when a review procedure goes stale — it simply starts reviewing for the wrong things. Which is one more reason the operational layer needs the discipline I argued for earlier: version control, review, deliberate design. The process artifacts are load-bearing precisely because nothing else checks them.

Skills are already shared and versioned like packages. Plugins bundle whole domains of process. Engineering know-how — the part that used to live in senior engineers’ heads and in tribal review culture — is becoming an artifact you can write down, refine, and hand to the next project.

We used to share libraries. Now we share behaviors.

A capability is not a process

There is an easy mistake to make when comparing these tools: reading the capability list and thinking you’ve read the harness. Image generation, browser control, code execution — those are capabilities. A harness is what turns a capability into a dependable outcome. The three examples that follow I’ve only watched from the outside, as a user, so treat the mechanics as observed behavior rather than inside knowledge.

Claude Design is the cleanest example I know. Its raw capabilities are nothing exotic — the same models everyone can call. What makes its output striking is process: it builds a design system before it designs, prototypes inside that system, critiques its own work, checks the result across viewports, and iterates until it’s satisfied. You could try to replicate each step locally and still struggle to match it, because the hard part is the accumulated judgment about sequencing, evaluation, and when to stop.

A custom pet-hatching skill I watched run on Codex makes the same point from a playful direction — and to be clear, this is someone’s hand-built workflow, not a shipped Codex feature. On the surface it’s a toy: hand it a reference, receive an animated companion. Underneath is one of the more rigorous pipelines I’ve seen — grounding the character’s identity from reference material the way an artist builds a mood board, iterating sprite sheets through sub-agents, checking its output against consistency rules, looping until the result clears the skill’s own bar. It runs long, and it earns the runtime. The impressive part was never the image model.

Antigravity rounds out the pattern from the opposite side. It leans on Google’s image and video models rather than generating pixels itself, so visual work flows through it as process. The capability lives elsewhere; the harness contributes the knowing-how — which model to call, with what grounding, checked by what loop.

A capability is a fact about what is possible, a process is a commitment about what will reliably happen. Users only ever experience the second one. Source capabilities wherever they’re strongest, but understand that the process is the product — and the process is exactly the part you can design.

Don’t build your own base harness

At some point in this journey, every engineer gets the itch: I understand the loop now — model, tools, memory, repeat. I could build this.

You should scratch that itch once, as education. Building a small harness teaches you more about how these systems actually behave than a year of merely using them.

Then you should stop, because a production-grade base harness is one of those artifacts that looks simple and is actually thousands of accumulated decisions: permission models that are safe without being suffocating, context management under real limits, failure recovery, tool sandboxing, evaluation, parallelism, the interface through which a human stays meaningfully in control. The frontier tools have burned years of iteration on those problems, and their feature velocity is brutal.

The leverage sits one layer up. Take the base harness as given and pour your effort into the project harness — the layer of skills, rules, memories, and workflows that no vendor can ship, because it encodes your system, your standards, your taste. That layer is small enough to own and valuable enough to matter.

Two trends make this an even easier call. Base harnesses are turning into infrastructure — frameworks are beginning to ship agent harnesses as part of the platform, usable as an outer harness for building or embedded as an inner one, rather than as a separate product you visit. And they are getting cheaper: capable low-cost stacks are forming around strong open-weight models, including Qwen’s coder models, so the entry cost of running serious agent processes keeps falling. Exact prices move month to month; the direction — downward — is the durable part.

The liabilities are real too. Committing to a harness is a platform dependency like any other: every skill written against one tool’s conventions is a small piece of lock-in. Harnesses drift — rules go stale, skills rot, memories accumulate contradictions — and the operational layer needs maintenance exactly like the code layer does. And there is a failure mode I have already watched from close range: teams so absorbed in tuning the harness that the product starves. The harness carries half the weight of the software. It is not the point of the software.

Harness decisions are architecture decisions

Architecture has always been the discipline of decisions that are expensive to reverse. Which services exist. Where the data lives. What owns what. We treat those with respect: documents, reviews, structured debate.

The harness has joined that list. Consider the decisions a serious project now embeds, whether or not anyone writes them down.

Which harness owns which class of task. Which model backs which agent, at which reasoning effort — because effort is a dial now, and every position of that dial is a cost-quality tradeoff you are making whether you notice or not. What each agent may do autonomously, and where a human gate stays. What persists in memory versus what gets re-derived each session. Which evaluation loop checks which output.

That last one deserves more attention than it gets, because evaluation is where harness design succeeds or fails. A test suite is the right gate when correctness is checkable — it is cheap, objective, and merciless. A critic agent is the right gate when the failure is qualitative: a review that reads the diff the way a colleague would, catching what compiles but shouldn’t ship. A judge model is the right gate when you are choosing between attempts rather than validating one. And you are the right gate when the cost of being wrong exceeds the cost of your attention. Most harness failures I’ve seen reduce to a mismatch here — qualitative output gated by nothing, or trivial output gated by a human who becomes the bottleneck.

Nothing says you settle on one harness, either. The same project can run different harnesses for different classes of work — one SDK powering the inner agents, another driving a specialized pipeline — with the outer harness orchestrating them the way it already orchestrates compilers and test runners. “Which harness owns this task” is a real architectural question now, and pretending there is one answer for a whole system is as naive as pretending there is one database.

Get these decisions right and the whole development motion compounds: work parallelizes, quality gates hold, trust in the agents grows as the harness accumulates knowledge. Get them wrong and the failures are distinctly modern. The over-permissioned agent is the canonical one: give an agent broad write access and a vaguely worded objective, and it will pursue that objective with the confidence of a system that has never been embarrassed. Nothing about the model malfunctioned — the harness decision that let it act without a gate is the malfunction. The same anatomy repeats in the quieter failures: the agent hallucinating around a tool nobody provisioned, the unevaluated loop generating slop at scale. Again and again, the postmortem points at a harness decision, not a model.

These are not implementation details, and they are not tool configuration. They are the same kind of decision as a service boundary: they determine what the system can safely become. They deserve the same treatment — written down, reviewed, revisited.

Which brings me to the discipline hiding under the name. Harness-driven development is, at bottom, one commitment: nothing about how the software is built or operated is left outside a harness, and the harness itself is engineered, not improvised. Your outer harness should be able to run the project, test it, extend it, research for it, critique it, and debate it. If some part of your engineering practice can’t flow through it yet, that is your backlog.

The harness is half the software

Object-oriented programming organized code around objects. Domain-driven design organized it around the business. Test-driven development organized the act of building around feedback. Each one earned its name by moving something to the center that had been treated as incidental.

Harness-driven development moves the environment to the center. Not the model — the environment that surrounds the model: the tools, the memories, the skills, the rules, the processes, the permissions, the evaluation loops. The claim is simple to state and slow to sink in: that environment is now a first-class part of your software, as deliberate and as maintained as the source code, because it determines how well every future change gets made — by you, and increasingly by the system itself.

I don’t know if the name sticks. Names rarely survive contact with an industry, and I hold this one loosely. The practice is what matters, and the practice is already here — sitting in your repository, in files you may still be tempted to call clutter.

I suspect that before long, “works well with agents” will stop being a differentiating skill, the same way “uses version control” quietly stopped being one. The harness will dissolve into the frameworks, the way good ideas do. But between now and then there is a real window, and in that window the teams that engineer their harnesses — deliberately, on both sides of the product boundary — should ship faster, gate with tighter evaluation, and trust their systems with more of the work than the teams that don’t.

Every line of code should arrive through a harness — the outer one while we build it, the inner one while it runs.

Further reading

A few primary sources that shaped the framing here, chosen because they should stay useful as products churn:


Written and developed with Claude. The arguments are mine; the drafting was collaborative.

//WAS THIS HELPFUL?