Simple Architecture Principle — on Paper
⚡ TL/DR: The Simple Architecture Principle Toolkit
The goal of the Simple Architecture Principle is not just to write reusable code, but to forfeit control to gain ultimate control. It’s a toolkit designed to achieve flat component structures and maximum reusability by strictly controlling where complexity is allowed to reside, ensuring your components are pure and not beholden to the whims of feature creep.
Here are the five core techniques we use to fight architectural entropy:
Content Projection (Slots/ng-content): This is the ultimate weapon against complexity. It’s used to offload structural and UI complexity from a Dumb Component to its Smart Parent, turning the Dumb Component into a pure, flexible container or frame.
Minimum Viable Behavior (MVB): The strict law that limits a component’s functionality to the absolute minimum necessary to fulfill its name. It ensures the component is truly reusable and undefiled by external logic.
Smart and Dumb Component Architecture: The crucial division of labor where Dumb Components provide reusable presentation (MVB), and Smart Components manage state, data fetching, and orchestration.
Composable Behavior: Advanced techniques used to surgically extract complex, reusable logic (services, custom hooks, or composables) out of the Smart Component, keeping the orchestrator component light and readable.
Subject Based Components: An organizational file structure that groups related Smart Components and their unique logic by domain topic, rather than by their position on the screen, creating clear, maintainable boundaries.
Hey, folks! Quick confession: I’m officially old. 👴
I just turned 30, which, in developer years, means I started before the Big Bang. I’ve spent 17 years of my life either studying to become a Software Engineer (shoutout to the Technological School Electronic Systems, aka TUES Mafia, skrrr skrr!) or working as one.
My professional career officially kicked off on April 1st, 2015. (Looking back, I guess the universe was giving me signs from day one about what a massive, complex joke my career would occasionally be.) I’m an 11-year software engineering veteran. I’ve been wrestling with Angular, Vue, and React for most of my career.
A lot has changed since then — at least for some people.
🤯 The Hype Cycle Merry-Go-Round
I remember my first non-technical PM who talked nonstop about Agile, Scrum, Waterfall, and life coaching. He then started being a massive Crypto advocate, became an NFT enthusiast (who still remembers that?), and is now riding the AI hype train.
Weirdly enough, he still hasn’t learned a single thing about software engineering.
For me? Nothing has fundamentally changed since my first day. I’m simply madly in love with what I do outside of 2 four-year-long stints with crippling depression.
Tired, Boss. Tired of the Fight.
Although I still love my job, I am getting old, as I said, and I’m tired, boss.
- Tired of fighting depth complexity.
- Tired of rewriting poorly written, AI-generated code base garage software, where issues are fixed not through technical self-improvement but through endless Sprint Retrospective meetings and spinning the AI slot machine for a quick fix.
- Tired of always joining a team where my first job is to untie the mess. Last go around it took me 2 months.
That exhaustion — the one that comes from tight deadlines, the complexity that makes you lose sleep, and the sheer unpredictability that affects your personal life because you don’t know if you’re right about to enter a three-month stretch of 16-hour workdays due to wrong estimations — is precisely why I wrote this.
The solution to escaping that purgatory is NOT Better Sprint Planning, or more meetings with your Non-Technical PM (who would have guessed?).
The answer is the Simple Architecture Principle.
What Are We Talking About? 🤔
Okay, so if the answer isn’t another set of meetings with your Non-Technical PM, Your Department Lead(who has been in cruise control mode for the last 25 years and still speaking in JSP and JSF terms) or waiting for the AI slot machine to give you clean code, what is it?
It’s a return to fundamentals, folks.

This is not some proprietary architecture I pulled out of the air — this is just a collection of solid, refined ideas, mostly developed by people far smarter than me, filtered through 11 years of debugging sessions.
The goal of these concepts is singular: to achieve a flat component structure with lower complexity.
It sounds complicated, but trust me, these five core techniques are so intertwined that if you master two or three of them, the rest will unlock themselves automatically.
The Five Core Techniques to Fight Complexity
Here are the heavy hitters, the techniques we will use to build truly reusable code, fight depth complexity, and finally get some sleep:
- Minimum Viable Behavior (MVB) Principle — Vasilen Lyubomirov
- Smart and Dumb Component Architecture — Dan Abramov
- Content Projection — Miško Hevery
- Composable Behavior— Evan You
- Subject Based Components — Dan Stephenson
In short: we’re ditching deep component trees, we’re banning components that try to do too much (looking at you, “The GUY who built the Smartest Text Area In the World”).
Before we begin dissecting the eternal conflict of nesting vs. projected content, we need to take a step back and define the battlefield.
⚖️ The Component Paradox: Big, Small, and Always Broke(the n is silent)
In the world of Angular, Vue, and React, what exactly is a component?
It’s a bit of a paradox, honestly. For me, a component is simultaneously the Largest but also the Smallest building block we can use to construct modern web applications. It’s weird being the biggest and the smallest.
The second, and perhaps most crucial, characteristic is that by its very nature, a component should be reusable.
This means we, the engineers, hold all the power. Components can be Big or Small, Simple or Complex. It is entirely in our hands to decide how convoluted or how simple a component could be. We are the architects of our own sleepless nights. (Strangely, this “architect” title rarely comes with the same paycheck as Lead Agile Manager.)
Now, my personal contribution to architecture — the concept of focusing on Minimum Viable Behavior (MVB) — is something I’m genuinely proud of.
But let’s be real, Simple! and MVB are not even remotely close to my biggest achievements. My more important achievements are that people who I mentored are now my superiors and even 2 of them are now multi-millionaires.
So, if my purpose in this universe is clearly to generate wealth for others, let me at least generate simplicity for you.
🧑🍳 The Simple! Recipe: Mix and Match
For all modern experiences, we need to strike a careful balance. The goal is not to eliminate large components, but to make sure they are assembled correctly:
- We need a lot of small components that hold just enough behavior — the MVB — so they are highly reusable. These are our Dumb Components.
- We need a few smart components that are created from many of these small MVB components.
The Smart Component is the overseer — it is usually responsible for managing state and orchestrating which MVB components to display and how they should behave.
This division of labor is the key to escaping the complexity caused by deep nesting, allowing us to create flexible UIs.
🥊 The Conflict: Who’s Job Is It Anyway?
We’ve established that the Simple Architecture Principle aims for simplicity, and that the Smart Component is the orchestrator responsible for managing state and coordinating which MVB components to display.
Flatness is a major benefit of this approach, but let’s be realistic: as products grow in terms of functionality, the urge to nest components will increase. Nesting is an important tool in our belt, but I challenge you to use it only as a last resort, after you have exhausted all other techniques.
Therefore, the core decision we face every day isn’t just if we nest, but how. It’s about determining who is responsible for what. This is where the choice between nesting responsibility and using content projection (ng-content in Angular or slots in Vue.js) becomes critical.
Now, let’s be clear: nesting by itself is not bad. What is bad is when you nest complexity inside components that have no reason to hold that responsibility.
Why do I lean so heavily on projection? Because irresponsible nesting usually results in increased complexity due to depth from day 1. (Seriously, there is no reason why your 6-month-old greenfield project should already have 20 levels of nesting just in the sidebar.) This complicates state management when a component from the top needs to communicate with content deep at the bottom. This leads to reliance on services to orchestrate components remotely, which reduces reusability and hides dependencies because all requirements are not visible in the input/output props.
Let’s look at the ultimate consequence of ignoring this rule.
👹 The Anti-Pattern: The Smartest Text Area in the World(and shoutout to Bro)
I once worked on an AI tool that contained multiple pages, each housing different AIs with large functionality disparities. Smack in the middle of this entire system was a component I sarcastically dubbed “The Smartest Text Area in the World.”
This component was technically reused on every page, but it was super complex to use. It embodied the exact opposite of MVB:
- Over-Responsibility: The text area, where the user wrote their message, contained dependency logic for sending the message, sending images, or choosing a different AI model. Each AI page was configuring this single component by setting different combinations of inputs related to utility buttons responsible for settings, model pickers, image upload, CSV upload, and deep research features.
- Service Dependency Hell: The text area’s functionality was dependent on a multitude of services related to submitting the prompt. This meant a glorified text area (literally named
text-area) was responsible 99% of the time for things not related to a text area. - Complexity Spiral: Extending this component was agonizingly difficult. It was full of
if/elsestatements, leading to a constant stream of corner cases and issues where updating one page would wreck the functionality of another. Trying to write tests for this mess required an Agile Expert Major Degree.
This disaster proves that deep coupling causes components to become dependent on hidden services, hiding dependencies, and killing re-usability.
✨ The Solution: Embracing Content Projection
We recognized this monstrous component was a failure of principle. The key solution here, fittingly, was Simple! — get it?
The necessary fix required us to immediately enforce Minimum Viable Behavior (MVB) and utilize Content Projection.
- Enforcing MVB: We rewrote the control, making it only responsible for writing text. That was its single, solitary job.
- Defining Extension Points: Everything else — all the buttons, settings, and submission logic — was moved outside the component and then pulled back in using Content Projection via
ng-content(for my Angular users) orslots(for my Vue boys out there).
This radical shift meant the core text input component was not holding any complexity and provided extension points rather than absorbing endless logic.
The crucial architectural benefit was the reassignment of responsibility:
- The AI Chatbot Component(the Parent/Smart Component) became responsible for defining and projecting the exact behavior it needed.
- This means the parent is responsible for the behavior of the component related to those utility features.
This solved the corner-case issues: for some pages, projecting Submit and Model Select was enough; others needed image and CSV upload; and others needed persistent or incognito features. The humble text area is no longer also responsible for submitting AI prompts.
Now that you’ve completed the first three sections, you are completely geared to build on top of the principles you just learned: Minimum Viable Behavior (MVB), Smart and Dumb Components, Content Projection.
😅 The Next Challenge: Managing Smart Component Complexity
We successfully executed a massive architectural shift: we moved complexity out of the Dumb MVB Component and into the Smart Parent Component (the orchestrator).
If we stop here, that Smart Parent Component will become unwieldy quickly. Give it like 1 month.
While we solved visual reusability and created MVB components that use simple input/output properties, the orchestration logic — the state management, API calls, and conditional display — now resides entirely within the parent. If multiple Smart Components use the same MVB components in different configurations, those parents will soon be drowning in complexity and full of code duplications.
This is how the orchestrator itself becomes the next anti-pattern, repeating the mistakes of “The Smartest Text Area in the World.” (Congratulations, we played ourselves.)
What if there’s something we can do about this?
🧩 Composable Behavior: MVB for Your Logic
We must apply the same principle of singular responsibility that we applied to our visuals (MVB) directly to our logic. This is the purpose of the next technique in the Simple Architecture Principle: Composable Behavior.
Composable Behavior is the essential tool for managing the complexity of the Smart Component itself. It allows us to extract reusable behavior from the Smart Parent into its own utility (an Angular Service, Vue Composable, or Custom React hook). If we apply MVB to this logic, we end up with bite-sized services responsible for just one thing.
🤖 Example: Deconstructing the Bricks AI Chat
Consider the Bricks AI Page, a stateful, persistent chat that is a Smart Component. It needs to coordinate several behaviors, like reacting to the submit button event and getting the selected model from projected content. Instead of coding all this into the Smart Component, we split the logic:
- The
useChatComposable (Sending/Receiving Messages): This composable is responsible solely for the real-time interaction: it exposes asendMessagemethod and returns the reactive state related to the current streamed message. Its responsibility ends with sending messages and receiving them. - The
useConversationComposable (Persistence): This is a separate unit responsible for managing history: it handles making new entries from successfully returned messages and displaying previous conversations.
🎉 The Simple! Result: Simple Orchestration
By injecting these two MVB logic units (useChat, useConversations) into our Smart Component, the Smart Component's code becomes incredibly simple. We just inject all the behavior we need and chain them together to our liking.
The Smart Component no longer owns the complexity; it only uses the complexity.
This keeps the orchestrator clean, maintains high logic reusability, and ensures the code remains flat, predictable, and maintainable, adhering to the principles that do not hide dependencies.
🗂️ Subject Based Components: Organizing for the Inevitable
Now that we’ve managed to properly separate the application into Smart/Dumb Components and successfully extracted common orchestration logic into separate Composable Behavior utilities (Services, Composables, or Custom Hooks) to keep the Smart Components light, there’s only one thing left: death.
It’s inevitable, as are all things in life.
The day will come when just extracting logic into composables and reusing the look and feel from Dumb Components is not going to be enough. You will eventually need to do some further nesting.
This is because, although the Simple Architecture Principle techniques allow us to increase complexity at a much slower rate, over time, that complexity will build up. This is especially true if the software solution needs to grow — for instance, if it’s a complex, multi-tenant back office system for a huge business.
Our whole goal was to delay the complexity raise as much as possible until the final moment when we do not have any more options.
When that moment arrives, the last technique related to growing complex software is purely organizational.
📂 The Subject Based Components Structure
Subject Based Components is a file structure/organization technique where we group components based on their domain Topic/Subject, not their position on the screen.
We define three main levels in this organizational principle:
Pages — Router entry points that host the main orchestrators
Building Blocks-The foundational, highly reusable MVB components.TextArea, ChatBubble, Sidebar,Dropdown (These are within our designated building-blocks folder).
Subject Based-Directories grouped by domain, housing Smart Components and related behavior.Conversation/, Chat/
🗺️ The Subject Directory Deep Dive
Subject Based Directories usually house Smart Components, but not exclusively. They gather everything required to manage a single domain topic, ensuring related logic and orchestrators are co-located.
Let’s clarify our Bricks AI example. We would have two distinct subject directories:
Chat/Directory: This subject is a separate topic, responsible only for the real-time act of sending and receiving messages. It would contain the Composable BehavioruseChat. It knows nothing about history or persistence.Conversation/Directory: This subject is responsible for persistence and history.
- It contains the main Smart Components:
Conversation.ts(the component hosting the persistent chat),Conversation.composable.ts(the logic related to persisting messages and associating them with the currently opened conversation), aNew/directory (withNew.tsfor making a new conversation), and anEdit/component. - Crucially, the
Conversation.tscomponent has a dependency on and imports theuseChatcomposable from theChat/directory.
This structure elegantly handles variations. For instance, the Incognito feature would be its own Smart Component (Incognito.ts). It would also have a dependency on useChat but would not use anything from the Conversation/ directory, perfectly reusing the logic it needs while ignoring the logic it doesn't.
This final organizational layer ensures that when you must introduce complexity and nesting, that complexity is confined, predictable, and immediately visible based on its domain topic, rather than scattered across a monolithic page or hidden behind opaque service orchestration.
Congratulations! You now have all five core tools of the Simple Architecture Principle to build clean, flat, and maintainable applications.
Alright, that’s a wrap.
Fueled by Monster Energy :)