There’s No Such Thing as Perfect Software

Every engineer eventually learns this truth: good code does not always make good software, but bad code will always catch up with you. Somewhere between sprint deadlines, shifting requirements and late-night debugging, we discover that the real craft of software development is not only technical, but deeply human. 

That is where vibe coding comes in. 

What began as curiosity about how AI could fit into my workflow became something more meaningful: a lesson about structure, discipline and what it really means to build quality software. 

Why “Vibe Coding”? 

AI has become part of every developer’s conversation. Tools such as GitHub Copilot, Cursor and ChatGPT are reshaping what “pair programming” looks like. For me, that raised an important question: is this the future of software engineering, or just the latest hype? 

I decided to find out for myself. Armed with curiosity and a touch of scepticism, I set out to build a few projects using Angular and C#, letting AI assist, and at times, take the lead. 

The goal was never to replace myself as an engineer. It was to explore how these tools could make me a better one. 

 

But First! What is Vibe Coding? 

According to the internet, the simplest definition of vibe coding is that it’s a software development approach where a user describes the desired functionality in natural language, and an AI generates the code to fulfill the request. 

It’s a hotly contended practice because while it makes coding accessible and can boost productivity through speed, AI-generated code can lack structure, is often difficult to maintain and can actually be a time-suck when it comes to debugging. 

In short, like any instrument, it’s only as good as the person who wields it.  

The Chaos Phase 

The early stages of my experimentation were unpredictable, to say the least. 

The AI produced code that looked right, sounded right and even felt right, until it was not. It hallucinated, lost context and confidently generated what it called “enterprise-grade” code that fell apart at runtime. Debugging with it felt like teaching recursion to a parrot. 

But as the chaos unfolded, I realised something: none of this was new. 

  • Scope creep? That is why we define “Ready” and “Done.” 
  • Poor code quality? We already use reviews, linters and tests. 
  • Lost context? That is what documentation and naming conventions are for. 
  • Security issues? We have scanners and static analysis tools to catch those. 

AI was not introducing new problems. It was simply holding up a mirror to the weaknesses already present in my own process. 

 

Understanding the Machine 

To work effectively with something, you need to understand how it thinks. 

Large Language Models (LLMs) are, at their core, sophisticated autocomplete systems. They do not truly understand code; they predict the next most likely word or token based on what has come before. That process repeats until the response appears coherent and complete. 

This means two things matter most: 

  1. The data they are trained on. 
  1. The clarity of the instructions they are given. 

If an LLM simply reflects the structure and precision of my input, then the only way to achieve good output is to ensure my own system only allows quality to flow through. 

 

Turning Vibes into Systems 

That was when “vibe coding” evolved from chaos into discipline. 

By applying traditional engineering principles to the AI workflow itself, I began achieving far more reliable results. It was not about magic prompts. It was about creating a structured collaboration between human and machine. 

Some of the key practices that emerged included: 

  • Explicit over implicit: Always specify exact files, functions or patterns. 
  • Progressive disclosure: Start small and expand context gradually. 
  • Pattern libraries: Reuse working examples instead of re-describing them. 
  • Stateful workflows: Track progress to avoid backtracking. 
  • Verification checkpoints: Validate before moving forward. 

These were not AI tricks. They were engineering fundamentals, reframed for a new kind of teammate. 

 

Finding Balance 

Of course, I took it too far for a while. 

I over-engineered the process. Everything had structure, rules and checkpoints. It worked, but progress slowed dramatically. Eventually, I realised that the right amount of control depends entirely on the project. 

Sometimes you need a full suite of tests and governance. Other times, you simply need quick iteration and validation loops. Like most things in software engineering, it comes down to the familiar balance of cost, time and quality. 

Find that balance, and the LLM stops being a distraction. It becomes a multiplier. 

The Real Lesson

Vibe coding is not about letting AI take over your IDE. It is about allowing it to reflect your process back to you. 

If an LLM cannot follow your guidance to produce good software, the issue probably lies in your structure, not the model. When you bring clarity and strong engineering principles to the table, AI amplifies them. It helps you move faster, deliver more consistently and sharpen your own discipline along the way. 

For me, that is what vibe coding really means. It is not about replacing developers. It is about rediscovering what great software engineering has always been built on: clarity, curiosity and continuous learning.