(this post is designed to accompany my video “The Skill That Separates Good Developers from GREAT ONES“)
AI coding agents are rather good at coming up with syntactically valid, working code these days. It also does design, reasoning about how to arrange that code. So is there still any point in learning to code? Could I just learn to do design and leave the coding to the AI – or perhaps learning either coding or design is pointless when the AI can do it? Actually, I don’t think so! Let me explain how I reason about what is the most important thing to learn – Design or Coding.
The Code is the Design
Design and Coding are difficult to separate. The sourcecode is the design of the software. It was Jack W. Reeves writing in the 1990s who first pointed this out. If you look at other kinds of engineering, once a complete design is defined, for like a car or a bridge or a chemical substance, a manufacturing team can come along and use the design to build the product, lots of it, without any further intervention of the designers. In software, that’s what the compiler and the build pipeline does. The engineering design equivalent for a software product is the source code because it has the same function. The production step is deterministic, no further intervention from the designer is needed.
When you write code, you are designing the software. In fact, testing and debugging are also part of the design process, this is how we evaluate and refine the design. Design is happening all the time while we develop software. It doesn’t really make sense to try to learn coding without also learning design.
I mentioned earlier that AI tools do both coding and design, and I think I need to qualify that – it definitely does coding but design is actually more than only the code in the repository. I’m going to say more about how to learn to use AI tools in development, but first I want to develop the idea of what design is more than code.
Design is for Humans
Peter Naur has a great take on this. He was one of the pioneers back in the day, a really influential computer scientist. In 1985 he proposed that what actually goes on when we write code, is theory building.
“Programming… should be regarded as an activity by which the programmers form… a certain kind of insight, a theory.”
He means something very specific by a theory and it includes being able to:
- explain why each part of the program is what it is
- answer queries intelligently
- argue about it
- modify that program correctly
It basically means you can add new features at a reasonable cost. An important part of design is what’s in the heads of the people working on the software – not only what’s in the sourcecode. It’s the biggest determinant of how easy it is to update the software.
Constantine’s Equivalence
Perhaps this is where Software Engineering differs from other kinds of engineering. The design of software is changing all the time. Particularly in Continuous Delivery where we are building a new production ready version of the software at least daily. Every time the build pipeline runs successfully we have a new design that’s ready to go. Kent Beck has a good take on this, which he calls Constantine’s equivalence.
In software the overall cost is dominated by the cost of change. It’s not the cost of initially designing the system that matters, it’s how easily we can change it. This is Constantine’s equivalence written as an equation:

That is, design is important because it allows us to lower the cost of change. The biggest factor is coupling – closely related to how much of the design you can hold in your head at once without losing important detail.
Mental Models
Going back to Peter Naur’s theory building model of software design – It’s a bit like this – we build up a mental model, or theory of what the software should do, then transfer that into the code we write. The closer the match between the mental model and the actual model in the code, the better the software will be. That’s largely the premise of Domain Driven Design, too.

The next programmer who comes a long to modify the code will do better if they can also build up the same theory or mental model of the code. It helps if they already understand the language of the domain before they look at the code.

Sometimes it’s really hard to read code that someone else wrote, because we struggle to re-create their mental model of it. They didn’t leave us enough clues in the code and we can’t easily grasp it. If you have the wrong mental model of how the code works, it’s very hard to change it without breaking something.

Good design is about putting yourself in the shoes of the reader of the code and making it as easy as possible for them. Often this is about putting more words into the code. More domain language. And it’s about reducing coupling so that you can reason about this part without needing to hold that other part in your head at the same time.
One example of this is when you do the refactoring Extract Function. You take a section of code from within a larger context and make a new method out of it. You’re creating a new abstraction, a new box in the code corresponding to a part of the theory you have in your head. Crucially the new method has a new name – words that represent what it does and make it easier for someone to read that code and construct that theory of how it works.
If you’ve done that well, people coming to that code might not need to read the body of the new method – the name should be enough for them to construct the right theory in their head for what it does.
AI Assistants
Let’s circle back to this question of the role of an AI Coding Assistant in design. If you delegate the task of designing code to an AI tool, you normally give it high level instructions in natural language, together with some constraints, like test cases and design guidelines. The tool will often produce a working solution. It designs code. However, if the AI writes the code, that theory doesn’t automatically get inserted into your head. The part in your head – the theory – is what you wrote in the prompts.
If you will subsequently need to change that code then either you need to do that purely through prompting, or you will need to read the code, engage with it, critique it, analyze how it achieves what it does and ideally be able to imagine how it could be written better. You will need to add it to the theory in your head.
That’s the same process you would go through for code written by another human. As Martin Fowler pointed out
Any fool can write code a computer can understand. Good programmers write code that humans can understand.
Even if you’re using an AI assistant, this is still good advice.
AI Assistant Counterexample
Having said all that, Some good friends of mine who I respect as excellent engineers are making very good use of these AI tools and don’t always find they need to read the code it produces. I’m thinking in particular of a story told to me by Llewellyn Falco – he built some software with a few others over a few days in close customer collaboration, iterating frequently on the specification. All the code was generated by AI tools, to the extent that at the end when the system was delivered and in production, he couldn’t even remember if it was written in Javascript or Typescript. His whole focus had been on engineering a setup where that didn’t matter.
What I think has happened here is that Llewellyn and his colleagues have managed to raise the abstraction level so they are programming in prompts instead of in code, the AI is taking the role of the compiler. The theory of how the system is built is entirely written in natural language in text files that the AI uses to generate the code.
It’s an impressive story and I don’t doubt that Llewellyn is getting excellent results with AI tools – he is a brilliant engineer and coach. I’m currently skeptical about how this approach scales to larger systems and more updates, but it is intriguing.
The pattern I’ve noticed is that the engineering skills you need to get impressive results while using these tools are largely the same engineering skills that allow you to get impressive results without them.
Conclusions
So which is more important to learn – Design or Coding? Well, logically the answer is both since all sourcecode is design. However part of the design is not in the sourcecode. Peter Naur’s insight is that humans need to form a mental theory of a design before they can change it safely, and Constantine’s Equivalence says that this is really important, since the cost of software is essentially the same as the cost of changing it.
If an AI writes the code you are still doing the design, because you must have that theory in your head to be able to change and update the software safely.
Design is a hard skill to learn, and the way people normally learn is by lots of experience writing code. So actually, My advice, especially early in your career, is to focus on learning to write code and with that, to design, without AI. Adding specific prompting techniques later on should go pretty quickly. What makes a good prompt is still changing rapidly as AI tools develop. Good design skills will always be needed.
Happy Coding – and Designing!


