Software engineering has a construction problem
— Software Engineering, Artificial Intelligence, Software Architecture, Future — 10 min read
Walk through a residential neighbourhood in New Zealand for long enough, and sooner or later you'll come across a construction site. The scene is usually familiar: a couple of utes (pickup trucks, for those outside New Zealand), a Toyota Hiace van parked by the kerb, and a construction fence separating the street from whatever is taking shape behind it.
Peek through the fence and you'll probably see a bunch of men, some barely out of their teens, operating mitre saws, cordless drills or swinging hammers.
New Zealand builds its houses much like Canada and the United States: using timber framing, manually assembled from relatively cheap pine. Constructing an average house requires hundreds, if not thousands, of pieces of timber to be measured, cut to length and assembled using brackets, nails and screws.
The process itself is remarkably simple. I'm convinced a nine-year-old could learn most of the individual steps within a few days. Yet, despite the simplicity of the measure-cut-mount cycle, completing a house takes many weeks, often months.
When I first observed this, I asked myself: Why are we still doing this? Surely, in the 21st century, there must be a more efficient way to construct a building.
Because that's how we've always done it
Ask around and people will tell you that New Zealand builds timber-framed houses because they're earthquake-resistant. There's some truth to that. Lightweight timber structures can perform well in earthquakes. But that hardly makes traditional timber framing the only viable approach, let alone the most efficient one. Christchurch demonstrated just how complicated seismic performance is, and how much depends on the design and execution of a building rather than simply the material used.
The historical explanation is much simpler: early settlers used the resources available to them. Timber was abundant and cheap, as was labour. Building with timber made economic sense. Mistakes were easy to fix - just cut another piece. Repairs and later alterations were relatively straightforward, too.
At some point, however, what started as a pragmatic response to local conditions became "the way things are done". And that's where things get interesting.
The early settlers were primarily looking for shelter from the elements. Their expectations of living comfort, energy efficiency and building performance were very different from ours today. Yet generations of New Zealanders have grown up with these construction methods and often don't question how their homes compare with modern European standards, particularly around insulation, airtightness and thermal comfort.
The irony is that while the basic construction process remains simple, it is no longer particularly cheap. Labour costs have risen. Timber prices have increased. Modern plantation forestry prioritises fast-growing trees, and the resulting timber requires more attention to treatment, moisture protection and structural grading than the durable old-growth timber once commonly available.
Meanwhile, successive changes to building regulations, combined with established industry practices and approval processes, have reinforced familiar construction methods. While alternative approaches exist, adopting them can be significantly more complicated than repeating what everyone already knows.
The result is an industry that keeps building variations of the same house, with surprisingly little fundamental innovation.
The illusion of specialisation
Another thing we've introduced over the decades is an elaborate system of professional qualifications and specialisations. Today, people choose whether to become carpenters, plumbers or electricians (affectionately known as "sparkies" here).
Of course, there are good reasons for requiring competency, particularly when work involves electricity, plumbing or structural safety. I wouldn't want an enthusiastic amateur experimenting with the wiring behind my shower. But how much of the actual work genuinely requires years of specialised training?
Many individual tasks in residential construction are straightforward and can be taught in weeks, sometimes days. The complexity lies less in the physical execution and more in understanding the overall system, planning the work, recognising exceptions and ensuring that everything complies with the relevant standards.
This is already reflected in how construction sites operate. Much of the physical work is performed by apprentices and less-experienced labourers under the supervision of qualified professionals. You don't need to be a licensed electrician to physically route a cable. You need one to ensure the electrical installation is designed, performed and verified safely and legally.
That's an important distinction.
We've built an industry around specialised people performing repetitive tasks, when the real expertise often lies in knowing what should be built, how the pieces fit together and whether the result is safe.
And yet, much of the work still revolves around manually measuring, cutting and assembling individual pieces.
I believe the construction industry has a problem that is difficult to see from the inside. To an outsider like me, the inefficiencies are striking. But when you've spent your entire career working within a system, it's easy to accept its constraints as inevitable.
Which brings me to the industry I actually work in.
Software engineering has the same problem
The software industry has a problem, too.
The first time I wrote code must have been around fourth grade, in 1997. I was typing examples from a Microsoft QuickBASIC book into a command-line text editor running on MS-DOS. Fortunately, the book was in German, so I could understand the instructions.
At the same time, I was learning some of my first English words: LET, IF, THEN and PRINT. I still remember how much fun it was once I got the hang of it.
A year later, I used my newly acquired skills to create a fake computer virus. I even applied what we'd now call social engineering, convincing my classmates to execute it. Fun times. Well, perhaps not for my teacher, who was understandably concerned that something had broken, or my startled classmates, whose computers suddenly started beeping while their screens flashed "You have a virus".
My apologies for the broken English of my eleven-year-old self.
Writing code is easy
To me, writing code is much like using a power drill to assemble IKEA furniture. You pick the right parts, follow a few rules and put everything together. In fact, I believe basic programming should be part of the curriculum for fifth graders, alongside languages, mathematics and the arts. The fundamentals simply aren't that difficult to learn.
And despite all the technological progress since my first encounter with QuickBASIC, surprisingly little has changed about the way I work. I'm still typing this article into a basic editor, using a monospace font, in an environment that could just as easily run in a terminal.
Come to think of it, I've spent most of my engineering career over the past three decades staring at terminals and monospace fonts. I couldn't tell you how many REST endpoints I've handcrafted during that time. Thousands, perhaps?
And that's precisely my point.
Software engineering hasn't changed nearly as much as we'd like to believe.
For much of my career, the work has followed a familiar cycle: copy, paste, modify, compile, repeat. None of those REST endpoints were masterpieces of engineering. Neither were the DTOs, services or controllers surrounding them. It was rinse and repeat, day in and day out.
Now multiply that by millions of people working in our industry. What an extraordinary waste of human time and money.
Of course, there are genuinely interesting problems to solve. Some engineers encounter more of them than others. Occasionally, you get to work on challenging algorithms, distributed systems or interesting mathematical problems. But a remarkable amount of our time has been spent doing fundamentally repetitive work.
The real intellectual effort often went into higher-level questions: How should we model the domain? How do we organise the system? How do we prevent the codebase from slowly deteriorating into the mess it inevitably seems determined to become?
That work requires experience, judgement and an understanding of the bigger picture. But the grunt work? Incredibly wasteful.
Fortunately, some people recognised this and created the open-source libraries and frameworks we've come to rely on. Still, building an application with Spring Boot isn't fundamentally less repetitive than it was with Java EE. And don't get me started on the Go developers, who apparently consider reinventing the wheel a cherished daily ritual.
We've changed the tools. We've introduced new frameworks, languages, architectures and development methodologies. But we've rarely challenged the fundamental approach.
Then came the large language models
That's why I wasn't particularly surprised when the first capable large language models demonstrated that they could automate much of this monkey business.
Our industry, particularly the open-source community, had spent decades producing what amounts to the perfect training material for generating code. Millions of repositories containing implementations of the same patterns, often accompanied by detailed documentation, descriptive commit messages and explanations of what the author was trying to achieve.
From "Initial commit" to "Project scaffolding", we've documented countless variations of essentially the same work. Why should it surprise anyone that a statistical model trained on all this material can reproduce the patterns?
It's not rocket science. Well, the technology behind these models is genuinely impressive. But the code they're producing often isn't.
We've spent decades demonstrating how to build REST endpoints, wire up dependency injection, create DTOs and implement CRUD operations. Now we've built machines capable of doing exactly that.
There's an amusing side effect, though. By the same mechanism, we've also taught these models how to gradually turn perfectly reasonable software projects into unmaintainable messes. After all, that's what we've demonstrated in countless repositories.
AI has learned our best practices, our worst habits and everything in between.
Faster at doing more of the same
So what have we gained? Quite a lot, actually.
We can scaffold entire applications in minutes, generate hundreds of REST endpoints in a week if we really want to, and identify bugs that generations of our esteemed colleagues have made over and over again. The patterns are all there in the training data.
We can overcome the blank-page problem, explore alternative designs and get inspiration for our domain models. LLMs can propose solutions by drawing on countless examples of prior art, combining patterns and concepts that would otherwise require hours of research.
I think that's great. It allows us to spend less time typing boilerplate and more time thinking about the problems we're trying to solve.
But there's a catch.
We're now exceptionally good at producing more of the same, faster.
And that doesn't necessarily mean we're getting better at building software.
We've automated the measure-cut-mount cycle. We can now assemble the metaphorical timber frame at unprecedented speed. But we're still building the same houses.
The problem was never writing code
Our industry's fundamental problem was never the speed at which we could write code. Nor was it how quickly we could review it. The problem is that we've rarely seriously questioned why software needs to be built this way in the first place.
Why do we keep handcrafting variations of the same abstractions? Why are we still translating business concepts into layers of boilerplate, interfaces, controllers and services? Why do we accept the continual accumulation of complexity as an unavoidable consequence of software development?
Why do we spend so much effort maintaining structures that we created ourselves?
To be fair, some people have tried to challenge these assumptions. A shout-out to the model-driven development nerds and code-generation enthusiasts out there. You were asking some of the right questions long before AI became fashionable.
Unfortunately, those approaches came with their own limitations and never achieved widespread success in replacing conventional software development.
But perhaps the real opportunity presented by AI isn't to make our existing development process faster. Perhaps it's to reconsider the process itself.
The construction industry could give every carpenter a robotic mitre saw and an automatic nail gun. Houses would probably go up faster. But that still wouldn't answer the question of whether manually assembling hundreds of pieces of timber on a construction site is the best way to build a house.
Likewise, giving every software engineer an AI coding assistant doesn't answer whether our existing architectures, abstractions and development practices are the best way to build software.
We've automated the tools without fundamentally questioning the craft.
So where do we go from here?