Simple Architecture Principle: The Arms Race Against Complexity

Simple Architecture Principle: The Arms Race Against Complexity

⚡ TL;DR: The New Weapon

We are adding a new tool to the Simple Architecture Principle: The Recipe.

  • What is it? A Single Purpose Component designed to serve exactly one specific use case.
  • The Rule: You either use it exactly as it is, or you make a new one. No configuration flags. No “Universal Wrappers.”
  • The Goal: It groups Building Blocks (Visual Consistency) and Composables (Behavior) into a sealed unit.
  • The Result: It cleans up your Subject Based Components (the main orchestrators), preventing them from becoming 300-line monsters of importing and wiring composables.

⚔️ Part I: The Return (Or, How I Got PTSD from a 49-inch Monitor)

I haven’t written in a while.

For the last three years, my vacation balance has been sitting at a solid zero. I finally decided to cash it in. But if you think I went to a beach to drink mojitos, you don’t know me.

I spent my time off locked in a room, staring at a 49-inch monitor, playing Battlefield 6.

If you’ve played, you know me. I am the guy you hate. I am the reason you rage-quit. I am the stealth sniper constantly on the move, one-tapping you from across the map before your hitbox even registers I’m there. I played so much that I’m pretty sure the immersion gave me actual PTSD. I don’t dream about sheep anymore; I dream about suppressing fire and flanking maneuvers.

When I wasn’t traumatizing teenagers in BF6, I was playing Dune: Awakening with an old friend. Now, let’s be real — this guy was barely functional in school. But today? He is way more successful than me. He grew up to be a fantastic adult with a family and a career, mostly because he mastered the things I ignored: soft skills and people management. He became the kind of person who is completely un-manipulatable. While I was grinding XP in a game, he was grinding XP in real life.

Then there is my other friend — a brilliant security engineer with street smarts that you can’t teach. He started a gambling company, became a multimillionaire, and bought a private jet. Naturally, he called me up with a “small favor.”

He needed an Inertial Navigation System.

Apparently, you can’t just buy military-grade navigation on Amazon because the government likes to gatekeep the good toys. So, he asked me to build one. I called it the IBSA Navigation System.

In Bulgarian, IBSA stands for “F*** Your Navigation System.” Because it didn’t exist for the public, until now.

So there I was, hallucinating sniper glints, watching my friends conquer the real world, and coding a physics engine for a private jet. And somewhere in that fever dream, between the virtual wars and the navigational math, I realized something:

Complexity is the enemy.

In Battlefield, if you hesitate, you die. In the IBSA system, if your math is off by a decimal, you crash. And in Software Architecture? If your components are too smart for their own good, your project dies a slow, painful death by nesting.

I’m back. The depression episodes were just a re-calibration. And I’ve brought a new weapon with me -> Recipes or Single Purpose Components


🗣️ Part II: The Meta-Language Trap

I cannot explain how tired I am of unnecessary meta-languages.

Almost every single project I have worked on has invented its own custom language that sits on top of the actual code. You know what I’m talking about.

  • Configurations of configurations that have configurations.
  • The NGRX Rube Goldberg Machine: You send an event that triggers a side effect, which triggers another side effect, which finally triggers a reducer, which updates state that triggers another side effect. Because apparently, there is nothing better than spreading 4 sequential operations across 4 different files just to change a boolean.
  • Wrappers of wrappers of wrappers.

My personal favorite is the “Job Description Lie.” You see a job posting asking for “Proficiency in MUI.” You get the job, you open the codebase, and you realize they are permanently using their own poorly written wrappers on top of MUI. Instead of using the library documentation you studied, you are forced to learn their proprietary “ButtonV2” that breaks half the props and hardcodes the other half.

Let’s get this straight: Wrapper Rot is just incompetence. You are using a UI library. It is made to be used as is. You should not have many use cases where you need to build on top of it to handle your user experience.

If your use case is that crazy, please pick the right framework (React, Stencil) and get some inspiration from shadcn. Every single thing they’ve done is public. Look at how intelligently they’ve made their abstractions. But remember: they are absolute UI/UX experts and frontend geniuses with a dedicated team. Your project, which aims to deliver business value in the Construction Industry, does not need to develop a custom UI wrapper based on Material Design. Just use the mat-button.

Now that we’ve removed all the wraps out of the way, let’s talk about the real weapon for Business Requirements.

🥣 The Recipe

Building Blocks, Smart Components, and Recipes are not here to help you wrap controls. They are here to fight Business Complexity. The idea of the Recipe is simple: Embrace the Nesting.

When we are within a Subject (our main Smart Component), and it grows too feature-rich — orchestrating too much behavior and too many components — the only natural step is to extract. But we don’t extract into a “Generic Reusable Component” that takes 50 props. We extract into a Recipe.

For example, let’s look at the Conversation Subject. The header used to have 5 controls interacting with 2 different services (Settings, Deletion, Models). In the old world, the Subject held all that logic.

Now, we take the Dumb Component (the visual header) and the Composables (the behavior), and we willingly solder them together into a new component: HeaderRecipe.

The key word is Soldered. We aren’t trying to make it flexible. We aren’t passing props down. We are hard-coding the behavior to the UI within the context of this specific Subject.

✨ The Result?

Let’s be real. What awaits us at the end of every architecture is the same thing: Death.

Complexity is unavoidable. Our goal with the Simple Architecture Principle isn’t to live forever — it is to delay that death as long as possible.

In the projects I’ve built over the past year both personal and professional — the original “Simple” stack (Subject + Composable + Building Block) was enough. It allowed us to ship, test the market, and steadily grow the application.

But as we push into Enterprise Development, we are about to hit a new wall. Even if you extract all your logic into Composables, you eventually end up with a “Smart Component” that has 300 lines of just imports and glue code binding those Composables to Building Blocks. The Recipe is the weapon for that specific battle.

However, a disclaimer: For a behemoth like Facebook, “Simple!” might not have enough firepower. When you have a platform that convoluted, with thousands of developers committing code every single day, complexity is no longer a bug — it’s the environment. But for the rest of us? For the 99% of software built to solve actual business problems without a billion-dollar R&D budget? Simple! is the way.


🚫 Part III: The “DRY” Trap

So, what is the actual result of introducing the Recipe?

When the inevitable time comes to nest a Smart Component, you don’t panic. You don’t start adding props.isHeaderForUserManagement to your existing component.

Instead, you mitigate and clean up the complexity by extracting the self-contained logic into its own Recipe.

In our Conversation Subject, the Header — which was bloating the file — gets extracted into a HeaderRecipe. This new file self-contains all the Composable logic (behavior) and the Building Blocks (visuals) it needs. It is now a private soldier for the Conversation Subject.

🤡 The “Don’t Repeat Yourself” Lie

Let’s bash “Don’t Repeat Yourself” a bit more. What a joke this principle has become.

My favorite dynamic is joining a “DRY First” team, only to CRY Later. You want to reuse that “Universal Header”? Good luck dumpster diving through 3000 lines of code across nested components that are all secretly communicating via global state management just to render a title.

And the best scenario of all? You: “Hey mate, I have an issue with the header. When I import it without any arguments, the app crashes.” Them: “Have you read the 2000 lines of readme about the header?”

You know what speaks better than a 2000-line stale readme file that deviates from reality just like those side-effects we talked about? Reading a 10-line HeaderRecipe that tells you exactly how it works.

What is better? Reading 10 lines of sequential code with a few content projections? Or deciphering an unknown, poorly written meta-language just to find out how show the most basic header ever?

The “reused” part is just what we see on the surface. The path to that final form was a nightmare. A new use case pops up, you add a prop, and all hell breaks loose. We have all missed the times when you developed a feature for one page and broke a screen you didn’t even know existed.

Basically, in the name of “Not Repeating Ourselves,” we develop absolutely abysmal solutions that keep the app in a non-stop fragile state.

⌚ The UNIX Philosophy (Or, Why I Prefer a G-Shock)

It looks to me that this is closely related to the UNIX Approach: Make each program do one thing well.

Think about it in terms of hardware. The “Universal Wrapper” component is a Smartphone. It tries to be a camera, a watch, a calendar, and a music player. To make it work, it needs a massive, complex Operating System (the Meta-Language) to manage the conflict between all these features. If the OS crashes, you lose your camera and your watch.

The Recipe is a Casio G-Shock GW-5000. It tells time. It survives a war. It doesn’t need a firmware update for a camera lens because it doesn’t have a camera lens.

The Recipe is a Nikon Z30. It takes photos better than the smartphone ever could. Why? Because it has physical dials (Building Blocks) perfectly placed for that specific task.

When we build a Recipe, we are choosing to pick up the Nikon to take a photo, rather than trying to tape a lens onto a Swiss Army Knife. We are building a bag of professional, dedicated tools, not a fragile device that is mediocre at everything.

🩹 The Fix: Intentional Separation

The Recipe fixes this fragility by rejecting that definition of DRY.

If the purpose of a Recipe is not met by your new use case, you do not modify the Recipe. You make a new one.

If you move to the UserManagement Subject, and it also needs a Header, you do not try to reuse Conversation/HeaderRecipe. You create UserManagement/HeaderRecipe.

  • It will reuse the same Building Blocks (so it looks identical).
  • It will reuse the same Composables (so it behaves consistently).
  • But it might need an alternative, subject-exclusive composable or a different button layout.
  • To summarize it. You use the tools to make easily a new component for your use case.

🛠️ The Deliberate Toolkit

The message here is clear: Stop nesting complexity to fulfill every use case in the entire app.

Instead, build a fantastic, deliberate toolkit:

  1. Building Blocks give you consistent Design.
  2. Composables give you consistent Behavior.
  3. Recipes give you the ability to reduce complexity by nesting “resposibly”. With a Single Purpose Components

When you have these weapons, creating a new Recipe isn’t a chore; it’s a trivial task. You can focus on what actually matters: Your own custom use case.

You stop being a “Configuration Manager” trying to untangle a web of props, and you go back to being a Software Architect — assembling clean, readable, single-purpose components that do exactly what they say on the tin.