Lee Painter Music for the Soul. Code for the Bowl.
21 September 2026 / Code

I’m Not Vibe Coding. I’m Orchestrating.

There’s a lot of talk about vibe coding at the moment.

And I understand the term.

Someone who has never written software can sit down with an AI agent, describe something they want, and potentially end up with working software they could never have built before.

That’s extraordinary.

But I don’t think that’s what I’m doing.

I’ve been writing software for around 30 years. I can read the code. I can write the code. I can debug the code.

I’m increasingly choosing not to.

I stopped coding

A few months ago I started the current iteration of Vork.

There had been an earlier version, built on the Jadaptive framework. I coded that myself. I wasn’t really using Copilot.

Then something changed.

I’d been dabbling with AI-assisted development for a while, but with the current Vork I effectively switched overnight.

I stopped coding.

The current iteration started as an AI project and has remained an AI project.

Today, when I decide Vork needs a feature, I normally start in a text editor.

I describe what I’m trying to achieve.

The flows.

The features.

The menu options.

How someone interacts with it.

What should happen.

I don’t generally start by specifying classes, methods or implementation details. I’m describing the behaviour I want from the system.

Then I give that to Copilot and ask it to plan.

I read the plan.

I question it.

I change things.

And when I’m satisfied that it understands what I’m trying to achieve, I tell it to start implementing.

That’s now software development for me.

But I’m not just pressing Go

I watch it work.

Pretty closely, actually.

Sometimes I’ll set it going and leave it alone, but usually I’m watching what it’s doing. And occasionally I’ll see something that makes me stop it.

What are you doing there?

I had one recently while implementing Slack channels for authorization and requests for information.

Somewhere in the implementation, Copilot had added logic that effectively said:

If this channel is a user, do this.

If it’s Slack, do something else.

It would probably have worked.

But it was wrong.

The thing sending the notification shouldn’t know what kind of channel it’s sending to.

It has an address.

It has a message.

Send it.

The channel service should work out whether that means a user, Slack, email or whatever else we support in the future.

That’s the abstraction.

So I told Copilot what was wrong, explained what I thought the architecture should look like, and let it fix it.

I didn’t fix the code.

That’s an important distinction.

I’m still making the engineering decision. I’m just no longer expressing that decision by writing the implementation myself.

How much code do I actually read?

Very little.

That will probably horrify some developers.

But I think we need to ask why.

If I’m no longer writing the implementation and I’m no longer maintaining the implementation by hand, what exactly am I trying to achieve by reading every line of generated source?

I need to know what the agent has done.

I need to understand the decisions it has made.

I need to know that the system behaves correctly.

But that doesn’t necessarily mean I need to inspect every implementation detail.

My attention has moved somewhere else.

To the outcome.

And, increasingly, to the evidence that proves the outcome.

Show me the tests

This changes testing enormously.

If you’re going to stop inspecting every line of source code, you need confidence from somewhere else.

For me, that’s testing.

Right now a lot of that is still me using Vork.

The next stage of development is dogfooding it properly: implementing real things inside Vork, telling it to perform real work and watching what happens.

At the same time, those user flows will increasingly become automated Playwright tests.

Then there’s security testing.

That needs to be as comprehensive as I can make it.

Because if I’m not checking every implementation detail, I need thorough evidence that the flows through the system behave correctly and securely.

In some ways, I think this makes testing more important, not less.

The review question changes from:

Show me your code.

To:

Show me that it works. Show me the tests.

What about junior developers?

I don’t think this way of working should be reserved for senior developers.

I’d give a junior developer exactly the same process.

But I’d supervise them.

I’d check their output.

I’d verify what they had built.

Which, when you think about it, is exactly what we should have been doing with junior developers anyway.

A junior developer manually typing every line of code doesn’t somehow guarantee good architecture, secure software or correct behaviour.

The difference may simply be what the senior developer reviews.

Instead of concentrating primarily on the code the junior wrote, perhaps we’re increasingly reviewing the specification, the plan, the tests and ultimately the outcome.

But what if the code is ugly?

I don’t care.

That sounds almost sacrilegious after spending most of my career writing software.

But I don’t need to see beautiful code.

I need to see functional code.

Historically, maintainability mattered enormously because eventually a human being had to open that source file again.

They had to understand what we’d done.

They had to modify it without breaking everything around it.

So we developed conventions, patterns, styles and practices that made software easier for humans to maintain.

But I’m not maintaining this code anymore.

The AI is.

If I need a behaviour changed, I describe the new behaviour and the agent changes the implementation.

That raises an uncomfortable question.

Do we care if the assembly language underneath Java is inefficient?

Most Java developers don’t routinely inspect the machine code generated underneath their applications.

Why would they?

That’s below the abstraction layer at which they’ve chosen to work.

If the application becomes slow, then perhaps we descend.

If memory usage becomes ridiculous, we investigate.

If something behaves incorrectly, we investigate.

But we don’t inspect the generated machine code simply because we’d like to see whether it’s beautiful.

Perhaps source code is beginning to occupy a similar position.

Not irrelevant.

Not inaccessible.

Just increasingly below the abstraction layer at which some of us are working.

The right to descend

That doesn’t mean I’ll never read code again.

There are times when the abstraction fails.

If I ask the AI to fix something and it can’t, or it fixes one bug and introduces another, then fixes that and introduces something else, eventually I’m going to stop.

At that point, I’ll descend into the implementation and work out what’s actually happening.

That’s important.

The abstraction is useful precisely because I retain the ability to break through it when necessary.

I don’t need to live down there anymore.

But I still need the right to descend.

And perhaps experience increasingly becomes knowing when to descend.

Garbage in, garbage out

I had a wonderful reminder of this recently.

I was implementing MCP support.

I’d been given a specific version of an SDK to use.

Copilot spent hours fighting with it.

It found what appeared to be bugs. It upgraded things. It changed implementations. Eventually it started heading towards implementing its own MCP client.

It was going around in circles.

The following morning I created a clean workspace.

This time I didn’t tell it which version to use.

I simply pointed it at the project.

It found the current version and had a working implementation running within minutes.

After all these decades of advancement in software engineering, apparently one of the oldest rules in computing still applies:

Garbage in, garbage out.

The agent wasn’t necessarily the problem.

The constraint I’d given it was.

And that’s part of orchestration too.

When the agent keeps struggling, sometimes the correct response isn’t to give it increasingly detailed instructions about how to escape.

It’s to question the assumptions you put into the problem in the first place.

This isn’t vibe coding

None of this is intended as criticism of vibe coding.

Quite the opposite.

The fact that someone without software development experience can now create useful software by describing what they want is remarkable.

But I think we’re using one phrase to describe two different things.

There is creating software with AI because you don’t know how to implement it yourself.

And there is deliberately choosing to operate at a higher abstraction layer because that’s where your experience is now most valuable.

I’m doing the latter.

My workflow increasingly looks like this:

Specify → orchestrate → observe → test → correct → repeat.

I retain the ability to descend into the implementation when I need to.

I just don’t spend my working life there anymore.

What is software development in 2026?

We’ve been moving up abstraction layers since software development began.

Machine code.

Assembly.

Higher-level languages.

Managed runtimes.

Frameworks.

Declarative infrastructure.

Each layer allowed us to spend less time thinking about the mechanics underneath and more time thinking about what we were trying to create.

AI might simply be the next abstraction layer.

Admittedly, it’s a rather strange one.

It talks back.

It makes decisions.

It occasionally does something that makes me shout “What the hell are you doing?” at my monitor.

But I’m not convinced the amount of source code I personally type is a useful measure of whether I’m still doing software engineering.

The engineering judgement hasn’t disappeared.

The architectural decisions haven’t disappeared.

The responsibility hasn’t disappeared.

They’ve moved.

Perhaps we should stop measuring developers by how much implementation they personally produce and start asking where their expertise actually creates value.

For me, the answer has become surprisingly simple.

What is software development in 2026?

Behaviours and outcomes!