Vibe Coding’s Blind Spot: System Architecture
Introduction - The Illusion of Speed
I’ve always been a believer in tools that enable engineers to build software faster. Long before “vibe coding” became a thing, I had been following companies building no-code and low-code platforms, and I’ve actively used many of them while trying to build real systems that could scale. Over the years, I’ve tried most of the major platforms across multiple generations, from early no-code tools like Bubble and Retool to more enterprise-oriented platforms like Unqork. More recently, I’ve spent time with AI-native tools like Lovable and Bolt. Unlike some of my peers, I’m not skeptical of this direction. I genuinely like the concept, and I’ve used these tools enough to understand why they’re compelling. Speed matters, especially early, and these platforms deliver real value at that stage.
When you spend enough time building with these tools, a few common patterns start to emerge:
- The primary audience is non-technical or semi-technical builders, often in operations, support, or product, looking to automate workflows or ship simple to moderately complex applications.
- The system model centers on a frontend wired directly to a database, tightly coupling presentation and data with minimal abstraction in between.
- Complexity is intentionally hidden, as it lowers the barrier to entry but also hides important architectural decisions.
- Everything ships as a single unit, optimized for speed of assembly rather than long-term evolution.
However as an engineering leader who has built systems at scale and who is constantly thinking about platforms that process billions of dollars in daily transactions or serve millions of daily active users, these systems consistently fall short of delivering real long-term value. Almost all of these platforms produce a single, monolithic system with very little emphasis on system architecture, backend boundaries, or how the system should evolve over time. There is rarely a notion of modularity, clear ownership, or separation of concerns beyond what’s required to get something working. These platforms create an illusion of speed, but once a product gains traction, teams grow, and reliability and scale start to matter, that lack of architectural intent becomes a real constraint.
From Prototype to Production
Vibe coding tools have done an incredible job of replacing pitch decks with product demos. They’ve made it possible for both non-technical and technical founders to prototype ideas quickly, validate assumptions, and show something tangible far earlier than was previously possible. In many cases, that speed is enough to unlock the next step: early users, investor interest, and real capital. For founders trying to get a spot in the AI race, these tools often provide exactly what’s needed to get momentum and enter the race.
Reality hits once that momentum turns into reality. Funding is secured, customers start using the prototype, and expectations change almost overnight. I’ve lived through this transition more than once, and the same themes consistently come up in conversations with peers. Teams quickly realize that what they’ve built is difficult to scale and full of practical limitations. The non-deterministic nature of AI-driven code generation often makes things worse, sending teams into endless cycles of prompting, regenerating, and patching behavior that isn’t predictable or stable. At that point, speed stops compounding and frustration sets in. What most founders end up doing is familiar: they hire more engineers, fall back on traditional development practices, and start rebuilding the system with real system architecture, utilizing AI more selectively for things like code-reviews, testing, or productivity boosts. The prototype did its job, but getting to production still requires a different kind of system thinking.
Architecture Is What Makes Systems Durable
If you look at successful large-scale platforms like Amazon, Uber, or Shopify, none of them are built as a single frontend talking directly to a backend database. They are composed of many moving parts: well-designed data stores, backend services layered on top of those databases, queuing systems to decouple work, caching layers to absorb load, data pipelines for analytics and operations, and cloud infrastructure to run and scale it all. These components don’t exist by accident. They are intentionally architected and carefully orchestrated so that, from a user’s perspective, everything feels simple: a package arrives on time, a ride can be tracked in real time, or an online store stays responsive during peak traffic. What’s easy to miss is that this architecture isn’t primarily about handling more traffic. It’s about isolating change so that one part of the system can evolve without destabilizing everything else.
When you’re building something small, for a limited number of users or a single internal team, you can often ignore most of this and still be fine. A tightly coupled system can work surprisingly well at that scale. The problem shows up the moment you start thinking beyond that scope. As usage grows, requirements change, and multiple teams get involved, those shortcuts compound. Systems that weren’t designed with clear boundaries start to fall apart, and what once felt fast becomes fragile. This is usually when non-technical founders are caught off guard and begin scrambling to find a CTO or senior engineers to help them scale, break apart a monolith, or migrate to a more modular backend. Durability shows up operationally here: how risky a deployment feels, how much unrelated code needs to change, and how often teams are forced into rewrites instead of evolution. Architecture is what determines whether that transition is manageable or painful.
Founding Principles Behind plain
plain started almost by accident, out of personal frustration. Last year, I was in the same boat as many other engineering leaders, trying to understand how AI would actually change software development beyond demos and prototypes. If you’ve spent real time building with AI-assisted tools, you’ve probably felt this too. Prompting works well at first, but the moment you try to turn a prototype into something durable, you end up in a familiar loop: regenerate code, patch the output, discover new edge cases, and prompt again. I use AI development tools every day and genuinely like them, but pushing them into enterprise-grade systems keeps producing the same non-deterministic, hard-to-reproduce results.
What eventually became clear is that this wasn’t a limitation of the models themselves, it was a limitation of the interaction model. Production software can’t be built through prompting alone. It needs structure, constraints, and repeatability. That realization led to the idea of component factories. In plain terms, a component factory takes a clear specification as input and reliably produces the same production-ready software component every time. Instead of asking AI to rebuild the same logic over and over, you use it once to create a factory, and let that factory generate modular components predictably. That shift, from prompting systems to manufacturing components, is what shaped the principles behind plain.
My experience building scalable software for over two decades, along with my experiments and frustrations with AI-assisted development, shaped the founding principles behind plain.
Factories First, Modules Second
plain does not use AI to generate modules directly. Instead, AI is used to build deterministic factories that generate modules from clear specifications written in familiar formats like JSON, YAML, or DBML, making them easy to understand and review by both technical and non-technical users. The same specification always produces the same output, which shifts AI-assisted development from ad hoc prompting to a repeatable manufacturing process suitable for production systems.
Modules Are Deployment-Ready by Default
Every module generated by plain is designed for real production deployment from day one. Deployment across AWS, GCP, or Azure, and across multiple environments such as development, staging, and production, is a first-class concern and can be done with a single command. This removes the long, painful phase where teams take AI-generated demos and spend weeks or months making them suitable for real production use. In eCommerce terms, last-mile delivery is handled out of the box.
Modules Must Stand on Their Own
Each module generated by plain is designed to function independently, with clear boundaries and explicit contracts. Modules can be composed into larger systems generated by plain, but they are not coupled to the platform and can be used just as easily within systems built outside of plain. This ensures modules remain reusable, replaceable, and easy to reason about as systems evolve.
Modules Evolve Independently
Each module generated by plain is designed to evolve independently as business needs change. As long as a module continues to adhere to its external contract, its internal implementation can be modified, extended, or completely reworked without impacting the rest of the system. This allows teams to adapt quickly while preserving system stability over time.
Opinions Are a Feature, Not a Limitation
plain encodes strong architectural opinions based on hard-earned experience building production systems. These opinions represent tried and tested recipes that have worked in production time and again, helping teams avoid common failure modes and reduce ambiguity. By constraining choices where it matters most, plain enables teams to move faster without sacrificing long-term system integrity.
In plain, specifications are the source of truth. Code is a generated artifact that can be reliably recreated from those specifications at any point in time. This makes systems easier to reason about, review, and evolve, because changes happen at the specification level rather than through ad hoc code edits. When the same inputs always produce the same outputs, AI becomes a force multiplier for production systems rather than a source of uncertainty.
What This Enables at Scale
At plain, our goal is to help teams build enterprise-grade systems directly from specifications. Instead of starting with loosely defined code or ad hoc prompting, systems begin with explicit specs that describe data models, services, and behavior. Those specifications drive the generation of production-ready components that are deployable from day one. This removes the gap between design and implementation and eliminates the need for a separate rewrite phase once a product gains traction.
What this enables is speed without fragility. plain optimizes not just for time to market, but for systems that can scale and evolve as business needs change. Teams can move quickly early on, knowing that they are not accumulating architectural debt that will slow them down later. As requirements shift, components can be regenerated or evolved safely from updated specifications rather than patched through risky manual changes.
Different builders benefit from this in different ways. Founders can move from idea to working product quickly while retaining confidence that what they are building can grow into a real business. Engineering leaders get systems that support parallel team development, clear ownership, and predictable change, instead of constant rewrites. Builders and engineers spend less time reinventing infrastructure and more time focusing on the parts of the system that actually differentiate the product.
At a broader level, plain is about changing how software gets built. Instead of treating production systems as handcrafted artifacts that slowly accrete complexity, plain treats them as manufactured systems assembled from well-defined components. AI becomes a tool for leverage, not guesswork, and architecture is encoded upfront rather than rediscovered under pressure. The result is a development model where speed and durability are not trade-offs, but reinforcing forces as systems scale.
