4 Lessons Learned from Building Microfrontends
Microfrontends allow developers to break down interfaces into individual, smaller applications, enabling separate teams to develop, test and deploy parts of the web UI independently. Compared to traditional monolithic frontend development, they allow more flexibility in technology choices, facilitating parallel development and improving scalability and resilience, as issues in one microfrontend are less likely to impact the entire application. Essentially, this brings the benefits of microservices — such as modularity and isolation — to frontend development.
Do microfrontends really stand to revolutionize the way we build frontends, or will they lead us down a path of unnecessarily increased complexity and technology fragmentation?
To explore this concept, we built proof-of-concept microfrontends using Nitric‘s framework. We aimed to explore the benefits of microfrontends and test our framework’s ability to build microfrontends and integrate them with Nitric microservices fully end-to-end. Here are four lessons we learned from this experiment.
Lesson 1: Discipline and Consistency Are Key
In the realm of microfrontends, the importance of discipline and consistency cannot be overstressed. When different teams or individuals contribute to the ecosystem, there’s a natural tendency for varying designs, structures and coding conventions to emerge. Having a unified vision and clear guidelines prevent a jumbled codebase and inconsistent user experience. Robust documentation acts as the compass, ensuring everyone, whenever they join, understands the system. Especially for small teams, routine check-ins or code reviews play a pivotal role in unifying direction. Having an automated testing process, including unit, integration and end-to-end testing, further solidifies this aligned approach and improves quality consistency.
Lesson 2: More Tools Lead to More Complexity
The allure of microfrontends often lies in the freedom to choose the right tool for a specific task. However, excessive technological variation can lead to productivity losses, particularly for compact teams that might not have a broad spectrum of expertise. Every tech decision drags along its baggage — learning curves, maintenance requirements or even something as tangible as bundle size. Further complications arise when addressing shared libraries and dependencies. The strategic decision — whether to bundle them into individual microfrontends or maintain a shared resource — has extensive implications, particularly regarding loading performance and version compatibility.
Lesson 3: Performance Strategies Are Vital
Integrating multiple microfrontends is reminiscent of piecing together a dynamic puzzle. Managing routes across microfrontends introduces complexities around defining routes, supervising the primary router, and aspects like nested routing or lazy loading. Performance, too, demands attention. The act of loading several microfrontends, particularly if their dependencies diverge, can usher in potential performance bottlenecks. Strategies such as employing shared content delivery networks (CDNs), bundle optimization and tree shaking become invaluable. Equally vital is a robust error-handling mechanism; a glitch in a single microfrontend should not jeopardize the entire application’s stability.
Lesson 4: Be Thoughtful about Using Shared State
Navigating the intricacies of shared states and intercomponent communications often feels like walking a tightrope. For microfrontends demanding a shared state, management becomes a complex challenge. Identifying a suitable mechanism or library that facilitates state sharing is crucial. In scenarios where direct state linkage isn’t a requirement, an event or messaging paradigm is a potential solution that decouples the microfrontends and enhances flexibility. However, shared states or messages mean you need clear contracts or APIs for delineating data sharing and updates. Implementing a thoughtful versioning strategy for these contracts can smoothly accommodate changes, safeguarding against inadvertent breaks in existing components.
We likely won’t be moving to a full microfrontends approach anytime soon, but the benefits are evident. This approach is hugely beneficial to teams needing to share common components across multiple frontends; for example, a single team can develop a chatbot interface and drop it into a monolithic frontend seamlessly, much like how many use Software as a Service (SaaS) products today. There is no need to make a full cutover; instead, you can progressively renovate, pull out the parts that make sense and you can use them later, and mix and match them with monolithic applications.
For more insight, check out a simplified version of what we built over at GitHub.