Micro-frontend(MFE) versioning

Naim Gkamperlo
tech-gwi
Published in
4 min readSep 28, 2022

Building on the previous post, we will deep dive into how we handle micro-frontend(MFE) versioning in GWI. Take a deep breath!

Terms

  • Application: whole application which includes kernel along with all its MFEs
  • Frontend Configuration (or simply fe-config) — a service that depends on absolutely nothing and provides the kernel with the latest version of all MFEs
  • Kernel: kernel/shell/platform that glues everything i.e the app that holds the menu, and the home page and loads every MFE upon request

Tech stack

The way we upload MFEs is the following:

  • Continuous Integration pipeline in place using Drone:
    every time the application was built, tested, and merged to a main branch (develop, release, and master are considered as main GitHub branches), Drone took over and uploaded the build output in a GCS bucket (Google Cloud Storage) with the first 7 digits of the PR(Pull Request) commit ID
  • Kernel is written using single-spa FE library and is part of our Kubernetes cluster in testing, staging, and production environments

The problem

Building on the above, what we needed to do was a simple way to connect kernel with MFEs in a way that MFEs can:

  • be updated easily by both developers and product managers
  • easily maintained by developers
  • instantly updated and uncached in all environments

We decided to approach the above in an agile way i.e small iterations, inspect and adapt. Below is our journey toward finding the best way to keep track of our MFE versions

Attempt #1

At the very start of our journey, we used to update the latest version of each MFE through a git commit on the kernel. The following problems surfaced :

  1. every time we had a new version of an MFE the developer was blocked so that their PR(Pull Request) is reviewed
  2. main branch tree was polluted right from the start

It was clear that another approach would be required leading us to Attempt #2

Attempt #2

We hacked a new quick and dirty solution that worked in the following way:
A static golang-based HTML template that had a single input field for the commit ID of the new deployment of our only MFE at that time.
This approach worked pretty well until several issues started raising:

  1. Scaling our applications meant we had to copy-paste an HTML template every time.
  2. Hard-coded values started crippling their way in a microservice whose responsibility was to serve completely unrelated data.
  3. The task of creating a new MFE template (or even a completely new application) was quite a hard thing.

Attempt #3

Our first scalable solution was implemented. The solution provided both a front-end UI and a dedicated microservice. This approach solved the following issues:

  1. Ability to create applications along with their corresponding MFEs.
  2. Developers, product managers, and designers can easily update their MFEs directly in all environments.
  3. APIs to get the version of a single MFE along with all the MFEs of an application.
  4. Scalable UI developed with React.
  5. Ability to set dynamic values regarding MFE GCS bucket path along with the MFE names — nothing was hard-coded anymore.

The process in which our current infrastructure works can be depicted in the following picture:

However, this solution didn’t provide the following:

  1. latest branches per MFE/application — the UI provided only the latest MFE
  2. ability to have long-live feature branches tested by everyone
  3. make users aware that a new version of their currently used MFE has been updated
  4. the already implemented design system was not reused

Attempt #4

Our latest approach involved making our solution robust, scalable, and testable. We architected our new iteration with the following in our mind:

  1. Ability to test out new features (either internally or externally with our customers) without breaking any stable environment i.e testing, staging, and production i.e feature branches to test long-live branches easily
  2. New UI that
    1. utilizes our design system to not reinvent the wheel as all our UI components are already in place
    2. informs the user regarding the latest releases along with timestamps of new version creation
    3. gives the ability to create feature branches on the application level in case a feature is constructed by more than a single MFE e.g featureX is making y visible both in MFE A and MFE B and those two MFEs need to be in sync for featureX to work properly.
    3. Ability to delete feature branches with all the related commits and GCS buckets after merging to a main branch
  3. Everything that was developed before Approach #4 should continue to work properly.
  4. The main user flow can be summarised as follows

Final thoughts

We still revisit our options and try to use the best solution to serve our needs as our platform keeps growing day by day. The next steps include the following:

  • ability to directly send a branch to the staging/production environment
  • ability to promote functionality from testing to staging and staging to production
  • even simpler UI that can be utilized by anyone in the Product Trio

Once implemented we will make another blog post on all our learnings!

Stay tuned for blog posts that make an in-depth analysis on all of the above and more :)

Resources

--

--