DEV Community

loading...

Rules of Micro-Frontends

Ruben Casas
👨‍💻 Dev. 🔈 Online Conference Speaker. ⚛️ Working on a React Microfrontend Framework for millions of users worldwide ❤️ Love helping others.
Originally published at infoxicator.com Updated on ・3 min read

I always wondered how large web applications were built! I discovered the secret a while back and it became my passion. After experiencing the advantages and pains of using Micro-Frontends at scale, I have decided to document this journey and share some of the “best practices”.

This is an opinionated list of best practices when designing applications that follow the Micro-frontend pattern. Each “rule” should be examined and its benefits/downsides evaluated against your specific use case.

Zero coupling between Micro-frontends

To achieve the benefits of this architecture, accidental coupling should be avoided as much as possible; this will unlock the flexibility and scalability that the Micro-Frontend pattern has to offer as well as future-proofing your applications by allowing incremental upgrades or future complete rewrites of parts of your application.

Each Micro-frontend should be able to render in isolation or inside a container application. The data required should be loaded by each Micro-Frontend and avoid data waterfalls.

Do:

✅ Share libraries that can be swapped without affecting other Micro-frontends.
✅ Load all the data it needs to render.

Do Not:

❌ Have a centralised store/share data across different Micro-Frontends.
❌ Share libraries that are in active development.

Separate Code Bases

Each Micro-Frontend should have its own codebase and the version control of choice shouldn’t have any impact on the way the project is developed or deployed. Having all projects under a single monorepo or individual repositories is fine.

Do:

✅ Use Monorepos.
✅ Use individual repos.

 Each Micro-frontend should be deployed independently

Each Micro-Frontend should have it’s own CI / CD pipeline and be able to deploy to production on demand without any dependencies on other Micro-frontends. A common antipattern that should be avoided is “The deployment queue of hell” where different Micro-frontends are so tightly coupled that they need to be deployed in a specific order to avoid breaking the application.

Do:

✅ Have separate CI / CD pipelines.
✅ Release on demand.

Do Not:

❌ Have Release schedules.
❌ Have incremental/sequential deployments that require previous versions.

Micro-Frontends should be tested Independently

Because Micro-Frontends are required to render independently as well as inside a container application, it makes sense to also test them independently using unit and integration tests for both scenarios.

Do:

✅ Have unit and integration tests for each Micro-Frontend rendering in isolation.
✅ Run integration tests for Micro-Frontends rendering inside the container applications as part of end-to-end testing.

 Micro-Frontends should be versioned

When a new Micro-Fronted is deployed to production, the previous version should not be deleted and the new version should be tagged with a version number using semantic versioning or similar. It is up to the container applications to decide what specific version of a particular Micro-Frontend to use (Managed) or always use the latest version instead (Evergreen).

Do:

✅ Use Semantic versioning.
✅ Use a specific version or “latest”.

Do Not:

❌ Require a global deployment to change versions.
❌ Delete previous versions.

Minimal Communication.

Communication between Micro-Frontends should be minimal and simple, avoiding global state and framework-specific solutions as much as possible.

If two or more Micro-Frontends are sharing a lot of messages to provide their minimal functionality, they might be too tightly coupled and they could share a similar enough purpose that they should be considered to be integrated into one.

Do:

✅ Keep messages small and simple.
✅ Avoid state and communication frameworks if possible.

Do Not:

❌ Share state.
❌ Have unnecessary communication.

CSS should be scoped

CSS from loaded from one Micro-fronted should not affect others.

Do:

✅ Scope your CSS.
✅ Use a CSS-in-JS or namespacing library (like CSS Modules).

Do Not:

❌ Use global CSS.

Final Recommendations

✅ Try to create autonomous teams.
✅ Try to arrange your Micro-Frontends around business functionality.
✅ Reusability is a nice “side effect” not the target.
❌ Don’t Force this architectural style just because it is “new”.
❌ You don’t need multiple javascript frameworks.
❌ You don’t need a “micro-frontend framework”.
❌ Micro-Frontends don’t have to be “micro”.

Discussion (7)

Collapse
jfbrennan profile image
Jordan Brennan

Semantic versioning for web applications doesn’t have much of a purpose. Commit hashes or build ids are useful, but semver really is meant for dependencies

Collapse
sqlrob profile image
Robert Myers

Commit hashes can be useful, but they're not good if you want quickly figure out which is the later version when something takes time to roll out or there's some caching issue in the client or CDN. Asking a customer for that long string on the bottom with possible weak homophones (3 / d / e, 8 / a) isn't great either.

Collapse
infoxicator profile image
Ruben Casas Author

There are two ways of handling versioning in Micro-Frontends, the "evergreen" approach which is the traditional "use the latest" deployed version in production and the "managed" approach where you keep previous versions and use semantic versioning (similar to dependencies). The advantages of the "managed" approach are that you can quickly hot-swap, roll back and run A/B experiments with your Micro-Frontends.

Collapse
jfbrennan profile image
Jordan Brennan

Managed deploys aren’t exclusive to micro-frontends, so what’s the purpose of <major>.<minor>.<patch> in this context?

Thread Thread
infoxicator profile image
Ruben Casas Author

Apart from the benefits I mentioned above, you can reuse microfrontends across multiple applications and choose a particular version number. Also tell the hosting apps if your application contains breaking changes by incrementing the version. All this is optional and depends on your use case. The “evergreen” approach might be enough in some cases.

Collapse
harrisgeo88 profile image
Harris Geo 👨🏻‍💻

Even though I totally agree with every single point, in practice most of these are quite difficult to respect.

For example in the first one, when migrating a part of a system into a separate microfrontend, there is going to be a period (which in most cases is not that short) where both frontend are going to have a fairly tightly coupled.

What use cases have you experienced? I’m just thinking that all these points are a bit of ideal circumstances which are not that common in the development world. What do you think?

Collapse
infoxicator profile image
Ruben Casas Author

Yeah, so they are more like guidelines that if you follow them you will unlock all the benefits of Micro-frontends and save you a lot of headaches. Regarding the migration one, I think that's the best time to sit down and design your application, avoiding coupling is probably the most important rule and there is usually a way forward to avoid it but requires a bit more work and being intentional about the decisions made early on in your Micro-Frontend journey.