Lee Painter Music for the Soul. Code for the Bowl.
22 October 2026 / Uncategorised

AI Didn’t Invent Bad Code. We Did.

AI is making it easier than ever to build software. But writing code and engineering software have never been the same thing.

I’ve spent more than 25 years building software and running a software company. Over that time, I’ve seen development languages, frameworks, methodologies and tools come and go, each promising to make software development faster, easier or more accessible.

Some delivered. Some didn’t. Most simply moved the complexity somewhere else.

Artificial intelligence is different.

I say that as someone who now writes remarkably little code himself, despite continuing to develop several software products. Much of the implementation work I once performed manually is now done by AI.

That would have been a fairly extraordinary statement for me to make just a few years ago.

It doesn’t mean I’ve stopped engineering software. In many respects, I’m doing more engineering than ever.

And that’s why I find some of the current criticism of AI-generated code frustrating. Not because the criticism is necessarily wrong, but because it often confuses writing code with the much broader discipline of software engineering.

AI can produce terrible code. It can introduce security vulnerabilities, make questionable architectural decisions, misunderstand requirements and confidently insist that something works when it doesn’t.

But none of those problems were invented by AI.

We’ve been producing bad software perfectly successfully on our own for decades.

The framework I spent years building

Before AI became a practical development tool, I had been working on an application framework within our company.

The idea was relatively straightforward, even if the implementation wasn’t.

I wanted developers to be able to describe their application’s data structures and behaviour through source-code annotations, with the framework automatically generating user interfaces and handling database persistence.

It was my attempt at building an intelligent development platform before we had intelligent coding assistants.

The long-term ambition was to reduce the amount of repetitive programming required to build an application, perhaps eventually approaching something that could generate complete applications without conventional coding.

I got surprisingly close.

When AI coding tools became useful, my first instinct was to use them to improve the framework. After all, I’d already invested considerable effort in it. Why wouldn’t I use AI to finish what I’d started?

But eventually I realised something uncomfortable.

AI was already solving many of the problems I’d spent years trying to abstract away.

I was using artificial intelligence to help develop a framework intended to eliminate programming work that artificial intelligence could increasingly perform directly.

So I made a decision.

I abandoned the framework for two projects, SSH Teams and Vork, and started again in fresh development environments, using AI as the primary implementation tool.

The original versions had involved weeks and months of manual development.

The new versions came together over a weekend each.

Not one weekend for both, I should add. I’m not quite that productive.

Now, before anyone interprets that as evidence that AI can build commercial software over a weekend, there’s an important qualification.

Those weekends produced working foundations, not finished commercial products.

I’ve subsequently spent months refining, extending, testing and challenging what AI produced.

And that’s the distinction that seems to be missing from so much of the discussion.

AI has dramatically accelerated the process of creating working software. It hasn’t eliminated the work required to make that software reliable, secure, maintainable or commercially useful.

Faster doesn’t necessarily mean better

In both cases, I believe the resulting products are better than their original implementations.

But I want to be careful about what that means.

AI didn’t magically produce superior architectural decisions.

Consider SSH Teams, our platform for managing short-lived SSH certificates.

One of its security mechanisms involves Demonstrating Proof of Possession, or DPoP, an established standard for proving possession of a cryptographic key.

In the original implementation, I had built something conceptually similar without using the standard itself.

Why?

Because I didn’t fully understand DPoP at the time.

It was a classic engineering mistake: solving a problem yourself without sufficiently investigating whether somebody else had already solved it.

A colleague, Brett, challenged the approach and pointed out that we could use an established standard rather than reinventing the wheel.

He was right. As usual.

We made the decision to adopt DPoP, and AI helped rebuild the implementation accordingly.

That was an improvement. Using an established, reviewed standard provides a stronger foundation than inventing a proprietary equivalent, particularly in security-sensitive software. It doesn’t automatically guarantee a secure implementation, of course.

But the architectural improvement came from human discussion and engineering judgement.

AI implemented the decision.

That’s not a criticism of AI. It’s an accurate description of the relationship.

And it’s worth remembering that AI could just as easily have helped me build an increasingly sophisticated version of the original, non-standard approach.

A tool that can implement your good ideas quickly can implement your bad ones just as quickly.

Building things that previously weren’t practical

Vork offers a different perspective.

It’s an ambitious behavioural engineering platform, involving considerably more than conventional data entry screens and database operations.

I could not conceivably have implemented some of its capabilities in the time available using my previous development approach.

That’s not to suggest Vork is finished, or that the months I’ve spent orchestrating its development haven’t involved substantial work.

They have.

But AI has changed what’s achievable during those months.

This is where I think conventional productivity measurements miss something important.

People ask how much faster developers can write code using AI.

Ten percent? Fifty percent? Twice as fast?

Those measurements might be useful in controlled experiments, but they don’t necessarily describe what happens when an experienced engineer uses AI to pursue an ambitious product idea.

Sometimes the benefit isn’t producing the same application in half the time.

It’s being able to build an application that would otherwise have been too expensive or time-consuming to attempt.

For a small software company, that’s potentially far more significant than saving a few hours on routine programming tasks.

It changes which ideas are commercially feasible.

And it changes how willing you are to experiment.

When an implementation represents weeks of work, abandoning it is painful. When you can explore an alternative in hours, you’re more willing to question the original approach.

That doesn’t mean every experiment succeeds.

I’ve had plenty of occasions when AI has led me down an unproductive path. Sometimes I’ve discarded the changes in Git and started again. Other times I’ve had to work through a poor implementation because I hadn’t preserved an earlier state as carefully as I should have.

AI hasn’t eliminated development mistakes.

It’s changed their cost, frequency and sometimes their scale.

I barely write code anymore

Perhaps the biggest change is what I actually do during a working day.

On some projects, particularly those developed with AI from the outset, I spend most of my time defining requirements, directing implementation, evaluating behaviour, testing and deciding what needs to change.

I write very little code myself.

For someone who has spent most of his professional life programming, that’s quite a change.

But I don’t believe I’ve stopped being a software engineer.

The experience I’ve accumulated still determines whether I can recognise an unsuitable design, identify an incomplete solution, understand the consequences of a change or recognise when something that appears to work is likely to fail under real-world conditions.

And I don’t apply the same level of AI autonomy to every project.

Maverick, our established Java SSH implementation, is a very different proposition from an application developed with AI from its inception.

Customers depend on Maverick. It has a mature codebase, complex protocol behaviour, security-sensitive components and compatibility obligations.

When investigating customer support issues or correcting defects, I use AI extensively to explore the code, trace behaviour, identify potential causes and help develop regression tests.

But changes require considerably more scrutiny.

An AI-generated fix that appears plausible isn’t sufficient.

We need to understand the defect, reproduce it where possible, establish appropriate test coverage and consider whether the change could affect existing behaviour.

That was true before AI, and it’s true now.

The difference is that AI can help perform much of the investigative and implementation work.

The responsibility for the result hasn’t moved.

Could I build an application using technology I don’t know?

Yes.

In fact, I already have.

Fans.audio, another platform I’m developing, is a practical example of using AI to build software across technologies in which I didn’t previously have the same depth of experience.

A few years ago, attempting something like that would have required a substantial investment in learning the technology stack before I could become productive.

Today, AI can bridge much of that gap.

That doesn’t mean I suddenly understand every implementation detail, or that I can safely accept everything it generates.

It means I can apply the engineering experience I already possess while relying on AI for much of the technology-specific implementation.

I think this is one of the most underappreciated consequences of AI-assisted development.

Experienced engineers can work across a much broader range of technologies without spending months becoming proficient in every language, framework and tool.

There is a risk here, certainly. Understanding the principles of software engineering doesn’t make somebody an expert in every technology they encounter.

But the productivity gains are substantial.

And it raises an interesting question.

If I can build a commercial software platform while writing very little of its code, what exactly is the expertise I’ve spent my career acquiring?

I’d argue that it’s the ability to engineer software, rather than simply the ability to write it.

Those things have always been related.

They’ve never been identical.

The dangerous confidence problem

This brings us to what I consider the biggest genuine risk of AI-generated software.

It’s not that AI produces bad code.

It’s that AI can produce convincing software for people who don’t have the experience to recognise when it’s wrong.

A developer with limited experience can now generate an application, connect it to a database, implement authentication, deploy it to the cloud and demonstrate something that looks remarkably complete.

And it might work perfectly well during a demonstration.

But does the developer understand its security model?

What happens when two users modify the same data?

Are database operations transactional where they need to be?

Can one tenant access another tenant’s information?

What happens when a dependency fails?

What happens under sustained load?

Are secrets being handled correctly?

Can the system recover from an interrupted operation?

These aren’t obscure edge cases invented to make software development unnecessarily complicated.

They’re the kinds of questions experienced engineers learn to ask, often because they’ve previously encountered the consequences of not asking them.

AI can compensate for missing implementation knowledge.

It cannot reliably compensate for missing engineering judgement.

And, importantly, this isn’t exclusively a problem for inexperienced developers.

My original SSH Teams implementation demonstrates that experienced engineers can also make questionable decisions when working outside their areas of expertise.

The difference is not that experience makes you infallible.

It’s that experience should make you more willing to question assumptions, seek review, investigate alternatives and recognise the limits of your own understanding.

AI can be an excellent collaborator in that process.

It can also become an extraordinarily effective accomplice to your mistakes.

AI didn’t create the software quality problem

I recently watched an interview with technology critic Ed Zitron on The Diary of a CEO, in which he raised serious concerns about the economics of the AI industry and the quality of software being produced with AI.

Some of those concerns deserve attention.

There is a genuine question about whether the enormous investment in AI infrastructure can produce adequate financial returns.

There are legitimate concerns about the reliability of AI-generated code.

And there’s an entirely reasonable debate about whether companies are prioritising the quantity and speed of software production over quality.

But I find the suggestion that deteriorating software quality is principally an AI problem unconvincing.

Anyone who has worked in this industry for a reasonable length of time knows that software quality problems existed long before generative AI.

We’ve had poorly designed systems, rushed releases, inadequate testing, fragile architectures, technical debt and questionable management decisions for decades.

Large software organisations didn’t suddenly discover how to produce unreliable software when coding assistants arrived.

And blaming AI for a deterioration in a product doesn’t establish causation.

Was the problem introduced by AI-generated code? Was it a management decision? An architectural compromise? Inadequate testing? Cost cutting? An aggressive release schedule?

Those distinctions matter.

If AI is responsible for a particular defect, we should establish that and understand why it happened.

But treating every software failure as evidence that AI coding is fundamentally defective isn’t serious engineering analysis.

There’s a more credible concern hiding underneath the rhetoric.

AI makes it possible to produce much more software without a corresponding increase in the number of people capable of properly evaluating it.

That could absolutely make software quality worse.

Not because the code is written by a machine, but because the volume of implementation can overwhelm the processes and judgement required to validate it.

The bottleneck moves.

And if organisations respond by reducing experienced engineering oversight because AI can now generate the code, they’re likely to discover that generating software was never the hardest part of the job.

What happens to the next generation of developers?

There’s another uncomfortable question.

If experienced engineers can increasingly delegate implementation to AI, how do inexperienced engineers acquire the experience necessary to exercise good judgement?

Much of what I know came from writing software, debugging mistakes, maintaining existing systems and discovering that an apparently straightforward solution could have consequences I hadn’t anticipated.

Those experiences weren’t always enjoyable.

But they were educational.

If AI removes much of that work, we can’t simply assume that the same engineering knowledge will emerge through a different route.

A junior developer who can generate a complete application may appear extraordinarily productive.

But productivity and competence aren’t the same thing.

And if that developer doesn’t understand the software they’ve generated, the apparent productivity gain may conceal a considerable future liability.

That doesn’t mean junior developers should be prevented from using AI.

Quite the opposite.

They should use it to explore unfamiliar technologies, understand alternative approaches, investigate failures and learn how systems work.

But we need to ensure AI is helping them develop engineering judgement, rather than allowing them to avoid developing it.

I don’t think the industry has solved that problem yet.

Will AI-generated code eventually make AI worse?

Another concern is the increasing volume of AI-generated code entering public repositories and, potentially, future training datasets.

If models are repeatedly trained on the output of earlier models, could software quality deteriorate over time?

It’s a legitimate research question.

There is evidence that indiscriminate recursive training on generated data can degrade model performance under certain conditions.

But that doesn’t establish that coding models are inevitably getting worse because AI-generated code exists.

Software has an advantage over many other forms of generated content: much of it can be subjected to objective checks.

It can be compiled, tested, analysed, benchmarked and evaluated against expected behaviour.

None of those processes guarantees good architecture or secure software, but they provide useful signals for distinguishing working implementations from plausible-looking nonsense.

The real question is whether future model developers use those signals effectively.

I wouldn’t dismiss the risk.

I also wouldn’t present an uncertain future outcome as an established fact.

What if AI becomes five times more expensive?

The economics of AI deserve a separate discussion from the engineering.

One argument made by critics is that today’s usage is supported by unsustainably low prices, and that demand will disappear if providers eventually have to charge the true cost of delivering their services.

There may be something to that.

If the price of AI coding tools increased fivefold tomorrow, I would certainly change how I used them.

I’d be more selective about which models I used. I’d think harder about which tasks justified the expense. I’d probably reduce some of the experimentation and indiscriminate generation.

But would I stop using AI?

No.

Not remotely.

The productivity gains are too significant.

That doesn’t prove the current valuations of AI companies are justified, or that every provider has a sustainable business model.

A technology can be commercially valuable to its customers while the companies supplying it are making poor investments.

The dot-com era demonstrated that distinction rather effectively.

The internet didn’t disappear when the bubble burst.

But plenty of companies that invested in the internet did.

AI may experience a similar financial correction. I wouldn’t pretend to know when, how severe it might be or which companies would survive.

What I do know is that the value AI already provides to my engineering work wouldn’t suddenly disappear because investors reassessed the price of the infrastructure behind it.

The economics of the suppliers and the economics of their customers are related, but they’re not the same thing.

The real change isn’t code generation

I don’t believe AI is making experienced software engineers obsolete.

I do believe it’s changing what experienced software engineers spend their time doing.

There are trade-offs.

Writing code is itself a form of reasoning. Implementation exposes details that high-level planning can overlook. Delegating too much can distance engineers from the systems they’re responsible for.

And there are real risks in allowing developers to become dependent on tools they cannot adequately challenge.

But the alternative isn’t to dismiss AI because it sometimes produces bad code.

It’s to develop better engineering practices around a technology that has already changed what’s possible.

We need stronger validation, not weaker oversight.

We need to teach engineers how to challenge AI-generated implementations, not merely how to prompt for them.

We need to recognise that an application which works in a demonstration is not necessarily ready for customers.

And we need to stop confusing the speed at which software can be generated with the quality of the engineering behind it.

I’ve spent years trying to make software development faster by eliminating repetitive implementation work.

Eventually, AI made much of that possible through an approach I hadn’t anticipated.

I abandoned a framework I’d invested years in developing, started again, and found myself able to build products that would previously have required considerably more time and resources.

I also found myself spending less time writing code and more time deciding what the code should do, whether it was doing it correctly and whether the resulting software was fit for purpose.

That’s not the end of software engineering.

It’s a change in where the engineering happens.

AI didn’t invent bad code. We did.

And if we use AI to produce bad software faster, the responsibility will still be ours.