Discover more from The Cantankerous Coder
Now in cornflower blue
For several years now I’ve been working with and promoting both micro-services and, to a lesser extent, micro-frontends. But over the past couple of years, I’ve become a big fan of a different approach: the micro-application or micro-app.
The essential idea is to take a large (generally front-end) application, perhaps a monolithic one, and to break it down into bite-sized pieces, each small enough that a single developer can handle it. And then to make each of those pieces its own application.
Yes, that means a fully separate application.
Crazy, right? What am I smoking?
But bear with me a moment and let’s think about how this might work.
OK, let’s clear one thing up right away. Obviously, we don’t want the end user bouncing from one domain to another, so we’re going to put a facade in front of the various micro-apps that make up the full application. Maybe a reverse proxy such as NGINX, or some sort of cloud trickery. But to the end user, it should appear as if they never leave the main application.
Glad we got that out of the way.
Why, then, a separate application? Let me count the ways.
It provides about as clear a separation of concerns as one can muster. A bit of understanding of domain-driven design (DDD) and bounded contexts can go a long way.
The code base can be small enough to fit in the average developer’s head. This makes for easier coding and fewer bugs. Complexity is our enemy.
Because a single developer works on the app, that dev owns that code. This avoids the tragedy of the unregulated commons and embraces the ownership model.
Breaking one app does not automatically break the monolith.
Individual apps can be scaled easily and as necessary.
We can do blue/green deployments easily as well: just move the forward. With multiple copies running simultaneously, this can be done one instance at a time.
A successful exploit does not automatically compromise the entire application. The surface area for attacks is small.
Tech debt can be easily managed. Updates or upgrades can be applied one micro-app at a time.
Devs can experiment with new technologies. There is no reason one couldn’t mix React, Vue, Svelte, and SolidJS apps. Or test different libraries, frameworks, test frameworks, approaches, etc. without mixing them into the same application.
Each dev can code in their preferred style with their preferred libraries and frameworks. Not just semis or no-semis, but OOP vs. FP, etc. And goodbye bikeshedding! Goodbye wasted process trying to keep everyone happy.
You’re not locked in to one set of libraries or one framework, so hiring is easier. React devs in short supply? Hire a Svelte dev instead.
Devs can show off and demo their individual apps allowing cross-pollination of ideas and enhancing learning for the entire team.
Whew. I’m tired already, but I’m sure I could come up with half a dozen more benefits.
Alright, fine. Let’s address the complaints. The first is typically, Doesn’t this introduce a lot of duplication?
And my answer is: so what?
Most of the duplication is going to be boilerplate code, and most of that we generate. So that is really a non-issue.
Then there are shared concerns such as look and feel, authentication and authorization, etc.
The solution here is to move those out of the app to the extent possible.
So instead of building your styles into your React code (never a good idea), put them in CSS where they belong. Instead of creating components in Vue, create them in semantic, accessible HTML and then use Vue to generate them.
Once you have a CSS library of styles (and CSS properties, which make theming easy) and an HTML pattern library, which establishes what the output of your React, Vue, Svelte, SolidJS components must be, then it is easy to create the components in whatever library you’re using. Import the CSS and you’re done.
An added benefit of this is that it puts control of the look and feel back in the hands of the designers and UX folks where it belongs. No more nonsense of devs thinking that this ought to be a bit wider or this color a shade darker.
Now the final product—the HTML and CSS displayed in the browser—is effectively outside the micro-app and acts as a formal contract. The contract says “when I request X from your micro-app, your app delivers this exact set of HTML, CSS, and behaviors to the browser”. The devs build their micro-apps to that contract. And with a tool such as Playwright, we can ensure that they adhere strictly to that contract.
That actually ensures greater consistency and re-usability across the entire application—even if individual micro-apps are using different libraries.
OK, then. How do the various micro-apps communicate with each other?
Obviously, via the URL is one possibility. Another is cookies or session storage. Heck, how do any apps communicate?
Yet another is an event bus—a pub-sub system. With BroadcastChannel and/or Websockets or maybe Server-Sent Events we can communicate not only intra-page, but between tabs, browsers, or even devices. We call this “decoupling”, no? And again it is a good thing. Ask around.
Wait! What if the dev gets hit by a truck? Now what?
Good question. That’s why every dev has at least one “buddy”. This buddy does not write code in the app (buddies have their own apps for that). But each week the buddy gets a full update on the app. And then the code reviews…
Put simply, the buddy should be ready to step in at any time, either briefly for an emergency, or longer term if the worst happens.
And let’s not forget those regular demos and discussions among the entire team. Everyone should have at least a passing familiarity with the individual apps.
There’s much more, but I’ll leave it for another essay. I’m still experimenting with this idea and working out a few quirks, but so far I’ve been very happy with it. I have a couple such apps in production now.