Vibing
Man, vibe coding is awesome.
I think I probably come across as being a little anti-AI-generated-code at times, but I really am not. I've been vibe coding since Aider, through Cursor and Windsurf (I have a t-shirt!) and now into Claude Code.
I do still have a reluctance to let it run totally wild in production though.
The Vibe Coding Experience
This blog (the app, not the content) was 100% vibe-coded, and it was as easy as it gets. I opened Claude Code and started things off by asking it to plan out the creation of a blog. Then I asked it to build the blog, and it spent an hour or so coding and at the end I had this blog. Even after 3 years of AI, it's still magical!
But, the magic wears off a bit when you actually look at the code that was written, which is as close to "big ball of mud" as it gets. When I ask for edits, the model basically sticks another blob onto the ball, patching extra pieces of code on without much consideration of what might have to happen in future, and in some cases maybe ignoring some what was already built.
Imagine building a house by starting with the drywall and doors, only stopping to add framing when you realise you need to support the ceiling, and then adding a foundation later when everything starts to sink into the ground. Software isn't quite like building a house, because you have a lot more freedom to change things as you go, but even in software it makes things much easier when you follow an overall strategy.
Building without that kind of strategising is perfectly fine for an app like a blog. This a straight-up CRUD app, which means that there's a database, and all the app does is basically Create, Read, Update and Delete blog posts and format them. There's next to no business logic, and not really much to figure out. Any issues are basically aesthetic: changing colours, sizes, fonts etc.
But, it doesn't make sense to limit the use of AI to super basic apps.
The Edge
Instead, we need ot find the "bleeding edge" where the technology is maturing, using the matured elements of it and waiting for the edge to progress to more advanced areas. Typical technology adoption follows some kind of process such as Moore's version in Crossing the Chasm1:
- Innovators: enthusiasts willing to try unproven products.
- Early Adopters: Visionaries who will tolerate imperfections for strategic advantage.
- Early Majority: Pragmatists who need complete, proven product.
- Late Majority: Adopt "industry standard" technology.
- Laggards: Skeptics who adopt only when forced.
When it comes to AI in general, some versions of it are at a more advanced stage than others. ChatGPT is probably still working it's way through the early majority phase, while OpenClaw is still in wild west territory.
This is true of vibe coding too: some parts of it perform quite well with minimal supervision, while some parts of it are not quite ready for commercial use unattended. In order to make the most of AI in software development, you have to find out what AI can do well and what it cannot, and try to create a structure that allows you to get the most of what AI can offer.
Complexity Explosion
In addition to delivering a product to your users, when you are building anything more than a pilot or demo application, you have to keep some additional constraints in mind2 like security, reliability, testability, scalability, etc.
For simple apps, like this blog, each of these considerations is very shallow. When they start to get complex, with complicated business logic and multi-layered access controls, each of these dimensions gets complicated on its own, bit this is compounded by the fact that there are combinatorial relationships between the elements, e.g. business logic might be different for different users. LLM's aren't able to consistently remember the details of each dimension simultaneously.
Solutions for this involve creating environments for the AI to operate within, with batteries of tests and extensive specification documents. An additional consideration that I like to focus on is architecture.
Pushing Complexity Around
Human coders have had the same kinds of issues with complexity that LLMs are facing now, and over the past decades we have come up with ways to deal with it. In general, the approach is to take something very complex and break it down into smaller and smaller pieces until the complexity is manageable. The atomic components of complexity might be called packages, modules, classes, functions etc., but at the fundamental level, they are complexity atoms, where we have split a problem down continually until we are able to easily reason about it.
We can do the same with LLMs. For example, an easy split to make is frontend v. backend. It's straightforward to create an API interface to hide things like security and business logic in the backend, so that a frontend only has to deal with translating data blobs into user interface components.
In the backend then we can split an app into modules, each having a more limited functional scope. We should aim to follow the principles of high cohesion and low cohsion introduced by Constantine in the 1960s (as described in his 1978 book3):
- High Cohesion: a measure of how well the elements of a module relte to each other, e.g. we often hear that a module should "focus on doing one thing well".
- Low Coupling: a measure of how litte each module is connected to others. The more interconnected modules are, the harder it is to reason about them.
What to Do
Ultimately, the pace of change is so fast that it won't be long before AI can handle architecture just as well as it can handle writing code. Until then, to build systems that are reliable, secure and stable, we should take a more active hand in setting the architecture that the models build within. That requires following the standard ideas in software engineering, such as high cohesion and low coupling.
References
Footnotes
-
Geoffrey Moore (2014). Crossing the Chasm 3rd Ed.. https://geoffreyamoore.com/book/crossing-the-chasm/ ↩
-
Richards & Ford (2025). Fundamentals of Software Architecture 2nd Ed. https://www.oreilly.com/library/view/fundamentals-of-software/9781098175504/ ↩
-
Yourdan & Constantine (1978). Structured Design. https://vtda.org/books/Computing/Programming/StructuredDesign_EdwardYourdonLarryConstantine.pdf ↩