Spec Driven Development Looks Like Programming If You Do It Right
The rise of AI coding assistants is making software developers think hard about their relationship with programming. It has led some to write essays with titles like “We mourn our craft” while others are leaning towards “How I Built a Full-Stack App in 6 Days with the Help of AI”.
It’s all because AI is changing programming. And the change will be the biggest that has hit programming in 70 years.
Programming in a nutshell
Computers are, at heart, machines. We could build computers out of gears and cams but they would cost too much and run too slow. So we use silicon.
But they are still machines, and programming is built out of 3 primitive actions that can all be mechanised:
- Loop
- Branch
- Process
When you’re writing a program you’re only ever repeating work, choosing what work to do or doing the work (and “work” for computers is basic math or moving data between memory and the CPU). It’s so simple there is a whole category of One Instruction Set Computers – a single action that a silicon machine can perform. Programmers have created operating systems for and ported Doom to these simple architectures.
The first computers had small instruction sets for implementing those 3 primitive actions. For example, and out of interest, here is the instruction set for the Manchester Small Scale Experimental Machine from 1948:
- JMP (Jump): Set the program counter to the address, allowing for loops.
- JRP (Jump Relative): Add the value at the address to the program counter.
- LDN (Load Negative): Take the number at the address, negate it, and load it into the accumulator.
- STO (Store): Copy the accumulator content to the address.
- SUB (Subtract): Subtract the value at the address from the accumulator.
- SUB (Alternate): Similar to above, used for arithmetic.
- CMP (Compare): Skip the next instruction if the accumulator is negative.
- STP (Stop/Halt): Stop the program.
Note that it only subtracts numbers. They had to write extra code to perform addition.
At this point in time programmers had to enter programs by setting switches, 32 of them for each machine code instruction in a program, and flick a couple more switches to copy it into memory and prepare for the next 32 switch settings.
Things were made a bit easier when they hooked up a punch card reader. Instead of flipping switches programmers could use a keypunch machine to enter the codes the machine understood and the punch card reader mechanised the input. Now the whole team could be writing programs at the same time.
Once they had computers running programs in the late ‘40s, they realised that they could use the computers to make programming easier.
Assembly language was the first step – replacing the numeric values of machine code with short text strings, like in the list above. The first assembler was created by Kathleen Booth in 1947. It converted the more easily remembered short text strings that could now be used to write programs into the machine code the computer needed.
Ten years later, in 1957, FORTRAN appeared – the first high level language. Instead of programmers writing commands for the basic mechanics of moving data in and out of memory and adding numbers, they could work at a higher level to implement the loop, branch and process primitives. It looked like this:
INTEGER I, SUM
SUM = 0
DO 10 I = 1, 10
IF (MOD(I,2) .EQ. 0) SUM = SUM + I
10 CONTINUE
PRINT *, ‘SUM OF EVEN NUMBERS 1..10 =’, SUM
END
There have been other paradigms, but this move to a higher level of abstraction to write programs has not changed since 1957. New languages and new language extensions have been created in attempts to make programming faster and less error prone. This has included things like taking memory management out of the hands of the programmer and generating the code to manage memory automatically.
But despite all the languages introduced since FORTRAN, they each still boil down to generating machine code that implements the loop, branch and process primitives.
AI has changed that.
What AI has changed
Each step up in abstraction:
switches → punch cards → assembly language → high level language
was built on programmers exploring what computers could do and the best methods for using them. Time and practice across the growing number of computers and programmers allowed strategies to coalesce and best practices to appear, and these, being based on driving machines, could be mechanised themselves.
The introduction of AI coding assistants was made possible by the same collection of strategies and best practices alongside the creation of the LLM.
The Internet led to the creation of services like GitHub, where the free hosting of software projects created a library of publicly accessible source code. Estimates place it at 500 million+ projects taking up 20-30 petabytes of storage representing almost a trillion lines of code (that storage includes the full history of every project so don’t worry about the lines→storage math).
It still seems miraculous, but if you train an AI model that has hundreds of billions of parameters on hundreds of billions of lines of code, it becomes quite good at coding. It picks up the craft of coding – the structures, the idioms, the techniques – that millions of programmers have created and used.
It can write the branch, the loop and the process in any number of languages. It can combine them into functions, and can combine functions into modules that implement any functionality that it has seen enough times in its training.
And that “has seen enough times” is why we will always need programmers, but how they will program will change.
Even if an AI coding assistant has never seen your type of application before it is still built from the programming languages it knows, and it will still be composed of millions of loop, branch, process structures. Except now you don’t have to type out those millions of structures yourself. You can tell the AI to.
And that’s what the spec is for.
The Specification as the new source code
While AI can write code, it can’t read minds and it can’t intuit priorities. This makes it bad at architecture and design – both in coding and generally. Yes, it can recreate patterns it has seen enough times, and possibly tweak them a little if provided enough context, but it will never have your level of understanding.
Experienced developers can leverage that understanding in creating the specification that is given to an AI coding assistant to convert – to compile – into code. The spec becomes a higher level declarative method of programming, specifying what to build (and how to verify), rather than the traditional imperative style of programming that explicitly instructs the computer how to do each and every single step.
This is where the two types of programmers in the introduction diverge. One type enjoys working out every single step to create an elegant solution. The second type just wants the computer to do the stuff as quickly and easily as possible and not having to type out every single tiny step, every single loop, branch, process, is a relief and a joy.
AI coding assistants don’t remove the need to think. They instead tend to concentrate the amount of thinking a developer needs to do as they take over most of the rote work of coding. Every action left is making decisions about architecture, how features should function, confirming correct operation, etc.
A huge amount of thinking is front-loaded into creating the spec the AI coding assistant will follow. This can be done with the assistance of the AI coding assistant. They can research best practices, other implementations, similar use cases, and anything else that needs to be considered as part of the design process.
One of the current clear proofs of this method is the Attractor spec from StrongDM’s Factory – their take on AI powered software development. They have gone all in on AI, including suggesting that if your developers aren’t burning through $1000 in tokens per day you might not be using enough AI. The bulk of their token usage is spent on automated verification and testing.
If you read through the Attractor spec you will see the level of architectural detail they have found necessary to successfully direct AI to generate code.
They take it even further – the Attractor spec is described as a software release. To build it you just give it to an AI to implement. At least one person has already successfully done just that, Dan Shapiro’s Kilroy on Github.
The future will be different but familiar
Reading the Attractor spec and working with AI coding assistants makes the “jagged frontier” very apparent.
AI coding assistants, even with the latest models, still make dumb mistakes. Yet they can implement complex products given enough details.
Software is still going to be designed. Someone still needs to decide what pieces to put together and how. Turning a design into working code will still involve iteration and problem solving.
But for most software, it is all going to happen at a much higher level of abstraction than the loop, branch, process programmers have been typing out since the 1940s.