Are you facing challenges in scaling your front-end to meet a growing number of users? With the increasing complexity of modern web applications, strategies like micro front-ends, monorepositories, global state management, and cache optimization are essential. 

In this article, we explore best practices for scaling front-end applications, discussing how to implement micro front-ends, manage versions in monorepositories, apply effective caching strategies, and efficiently maintain global states. 

Discover how Nubank is overcoming scalability challenges in the front-end and how you can apply these approaches to build agile, responsive, and easy-to-maintain user interfaces.

The Challenge of Scale

Companies like Nubank face unique challenges. With over 100 million customers in Brazil, Mexico and Colombia, handling large-scale distributed systems is not just a necessity but an obligation. Managing transactions like PIX, ensuring service stability, and providing a consistent user experience require innovative solutions.

Moreover, working with advanced technologies like Clojure and Datomic—whose development is influenced by engineers within Nubank itself—adds additional layers of complexity and opportunity. These technologies are not just tools; they are integral parts of our scalability strategy and continuous innovation.

Check our job opportunities

Micro Front-Ends: Dividing to Conquer

The micro front-end architecture has emerged as a solution to many challenges faced by large development teams. But what exactly are micro front-ends?

What Micro Front-Ends Are (and What They Aren’t)

Micro front-ends are an extension of the microservices concept to the front-end. They allow different teams to develop, deploy, and maintain distinct parts of the user interface independently.

This means that each team can work at its own pace, choose its own technologies (to a certain extent), and deploy updates without impacting the system as a whole.

It’s important to highlight that micro front-ends are not:

  • NPM packages or monolithic modules: Simply splitting a monolith into packages doesn’t offer the benefits of independent deployment or team isolation.
  • Separate applications for the end-user: The user experience should be unified. We’re not talking about multiple distinct applications but a single application composed of several autonomous parts.

Benefits of Micro Front-Ends

  • Independent deployments: Teams can deploy updates without coordinating with the entire organization.
  • Team scalability: New developers can be onboarded more quickly, focusing on a specific part of the system.
  • Fault isolation: Issues in one micro front-end don’t necessarily affect the entire application.
  • Technological flexibility: The possibility of using different frameworks or libraries, although this should be done cautiously to avoid client-side overload.

Costs and Considerations

  • Complex initial setup: Implementing micro front-ends requires careful planning and a robust initial configuration.
  • Cohesion of user experience: Ensuring that the interface is consistent and cohesive is a challenge when multiple teams are involved.
  • Performance overhead: Using different frameworks can increase bundle size and affect performance.
  • Observability and debugging: Monitoring and debugging an application composed of multiple micro front-ends requires advanced tools and practices.

Implementation Strategies

There are several approaches to implementing micro front-ends:

Client-Side Composition

This is the most common approach, where the integration of micro front-ends occurs in the user’s browser. Technologies like Web Components, Module Federation (Webpack 5), and frameworks like Single SPA facilitate this composition.

Server-Side or CDN Composition

The assembly of micro front-ends occurs before reaching the client, either on the server or CDN. Tools and techniques like Edge Side Includes (ESI) can be utilized.

Communication Between Micro Front-Ends

Efficient communication is essential. It’s recommended to use:

  • Custom events: Allow micro front-ends to communicate without directly depending on each other.
  • Shared states via browser APIs: Such as Local Storage or IndexedDB.
  • Avoid excessive global dependencies: Minimizes coupling between components.

Version Control in Monorepositories

Managing versions in a monorepository can be challenging, especially when multiple teams are working on different parts of the system. Here are some practices to handle this:

Individual Package Versioning

Tools like Lerna or Nx allow you to manage individual package versions within a monorepository. This enables each team to control the versions of their own components or modules, maintaining independence and facilitating coordination.

Avoiding Git Submodules

While Git submodules might seem like a solution, they often introduce additional complexity. Instead, using NPM or Yarn workspaces can simplify the management of internal dependencies.

Benefits of the Monorepository

  • Code consistency: Facilitates code standardization and reuse.
  • Visibility: All teams have access to the complete source code, promoting collaboration.
  • Automation: Simplifies the setup of CI/CD pipelines that cover the entire system.

Caching Strategies for Bundle Loading

Efficiency in loading resources is crucial for application performance. Well-implemented caching strategies can significantly improve the user experience.

Caching Shared Resources

By using technologies like Module Federation, it’s possible to share common dependencies among different micro front-ends, avoiding redundant downloads. To achieve this:

  • Define shared modules: Configure which libraries or frameworks should be shared to prevent multiple versions on the client.
  • Compatible versions: Ensure that shared dependencies are compatible with each other to avoid conflicts.

CDN-Level Caching

Utilizing a Content Delivery Network (CDN) allows static resources to be delivered more quickly to users by leveraging distributed caching.

  • Cache-Control configurations: Adjust HTTP headers to control how and for how long resources should be cached.
  • Cache invalidation: Have strategies to invalidate or update the cache when new versions of resources are deployed.

Browser Caching

  • Service Workers: Implement caching via Service Workers for more granular control over which resources are stored and when they are updated.
  • Preloading and Prefetching: Anticipate which resources will be needed and load them in advance.

Managing Global States in Host Applications

Maintaining a consistent global state in an application composed of multiple micro front-ends is a challenge.

Recommended Strategies

  • Custom events: Use the browser’s event system for communication between micro front-ends without creating rigid dependencies.
  • Shared local storage: APIs like Local Storage or IndexedDB can serve as a means to share global state.
  • Global contexts: In frameworks like React, you can use Context API, but be careful not to introduce unwanted coupling.

Best Practices

  • Domain isolation: Each micro front-end should be responsible for its own local state and interact with the global state only when necessary.
  • Well-defined contracts: Establish clear interfaces for communication between components, facilitating maintenance and evolution.

Standardization and Platform Teams

While micro front-ends address technical scalability, code standardization and the existence of platform teams are crucial for the human scalability of development teams.

The Role of Platform Teams

  • Defining good standards: Create and maintain code standards that make teams’ work easier.
  • Tools and infrastructure: Develop tools that automate repetitive tasks and ensure code quality.
  • Facilitating collaboration: Ensure different teams can work together efficiently.

Importance of Standardization

  • Faster onboarding: New developers adapt more quickly to standardized code.
  • Consistent quality: Reduces the incidence of bugs and maintenance issues.
  • Easier code review: Code reviews are more effective when there’s a consistent style.

Avoiding Unnecessary Complexity

  • Simplicity as standard: Opt for simple solutions that solve problems without adding excessive complexity.
  • Value-based decisions: Implement technologies and standards that bring clear benefits to the business and the team.
  • Beware of technological “hype”: Not every new library or framework is suitable for your application’s context.

Organizational Laws Applied to Code

Conway’s Law states that a system’s structure reflects the organization structure that develops it. Therefore, aligning technical architecture with team organization is not just beneficial but essential.

  • Aligned structures: Autonomous teams responsible for specific micro front-ends reflect a modular architecture.
  • Efficient communication: Fewer dependencies between teams reduce the need for constant communication and complex alignments.
  • Continuous evolution: A flexible organization allows the architecture to evolve with business needs.

How to Start

  • Pilot project: Implement a micro front-end in a non-critical part of the system to understand the challenges and benefits.
  • Define standards: Establish clear conventions from the outset for routes, communication, and styles.
  • Invest in observability: Monitoring tools are essential to quickly identify issues.
  • Documentation and communication: Keep documentation up to date and promote communication between teams to share learnings.

Conclusion

Scaling front-ends effectively requires a combination of technical and organizational solutions. Micro front-end architectures offer a path to handle technical complexity, while standardization and platform teams address the human challenges of large-scale collaboration.

At Nubank, we understand that continuous innovation and adaptability are essential to provide the best experience to our customers. Whether adopting advanced technologies or restructuring our teams, we are committed to evolving and facing the scalability challenges of the modern world.

Want to be part of this challenge? We’re always looking for talents passionate about technology and innovation to build the purple future together!

For more insights like these, watch the recording of the Engineering meetup.

Check our job opportunities