If you are writing a modern production system of any complexity, chances are you are using microservice architecture for it. If that system serves the Web browser, sooner or later you will discover that you must break up the UI into parts served by multiple micro-services (or micro front-ends).
The promise of microservices is that they can be built by separate teams, allowing teams relative independence and ability to move at their own speed, without the need to for intergalactic coordination. However, sooner or later executives (God bless them) will notice that some of the user interfaces contain elements that look similar across the system, and the dreaded word “Reuse” will raise its head.
It can sound downright heretical to say anything against reuse in software industry. That code reuse saves time and improves quality feels so innately right that it takes gumption to raise against it. What would be more logical than to create a reusable component for things that look similar and make all micro front-ends reuse them?
Movement on a planetary scale
To try to illustrate the problem we can hit with indiscriminate reuse, let’s look at the movement of planets in a solar system. As planets rotate around the star, they follow their own orbits that place them at various distances to each other. If we simplify the geometry and make all orbits perfect circles and in the same plane, the planets will still have different rotational speed, dictated by various factors (yes, ‘Gravity’ violated many of them). This rotation will place them at various positions to each other, with two extremes:
- Conjunction – when two planets are lined up on the same side relative to the star and closest to each other
- Opposition – when two planets are lined up on opposite sides relative to the star
As planets are in constant motion, they will at any moment be between these two points.
Back to the topic
OK, so let’s bring it back to the microservice system. The reason UI component reuse is being discussed in your meetings is because a design pass was made, and the first iteration of the whole system’s user experience has been put together, and some repetition is observed. This page in micro front-end A looks very similar to the page in micro front-end B. That picker is the same in three pages. Let’s solve it once and for all for everybody. Reuse, people! Sensible leadership all around.
It is easy to fall into this trap because reuse works great for, say, libraries. Of course you will not write that set of mathematical transformations over and over everywhere. In our Node.js microservice system, the list of modules each app depends on is getting longer and longer.
I just typed
npm list > deps in one of our largest microservices (I am tempted to call it Gustav for its girth). The flattened list of all dependencies is 3689 lines long. We should call it “miniservice” or just “service” by now. I am not writing 3000 modules by hand any time soon. Reuse is fine and dandy here.
Similarly, all our micro front-ends are using the same platform API layer. The APIs are ensuring the state of the microservice system is consistent. Again, I am not saving the same state in two APIs and keeping it in sync somehow – that would be crazy.
All our micro front-ends are using the same style guide and the same set of React components written to follow that guide with a set of basic visual building blocks (no, we are not using Bootstrap; yes, we know 15% of all web sites use it – good for them). Without it, our UI will look like a ransom note:
Say NO to casual UI reuse
This brings us to reusing actual parts of pages between micro front-ends. If you follow the reasoning above, it is the logical next step. This is where our astrophysics comes into play.
The pages that look similar today are like planets in conjunction. They are at their closest position in the microservice planetary system. The danger is that we are forgetting the constant movement. It may be hard to believe right now, but these pages will move apart. Why? Complex systems rarely evolve in perfect coordination. You cut your hair and a month later the cut starts losing shape, because the hair refuses to grow exactly the same everywhere. Diverging priorities, architectural changes, business pressures, reorgs all conspire against your microservices keeping their relative distance to each other.
The whole point of a microservice system is to insulate against this corporate chaos by allowing self-sufficient teams to cut through the haze and continue to make progress. Wonton UI reuse will tie the microservices together and restrict their freedom of movement. This is an elaborate way to write a distributed monolith, the worst kind. In our planetary analogy, you are applying super-natural gravitational forces between planets that completely mess up their trajectories and may result in the fiery collapse of the entire system. Or ‘Gravity 2’.
Doing it right
The heretical thought presented in this article is that some duplication and bespoke UI code is good if the outcome is preserved agility and independence of the microservice teams. Of course, thousands of lines of duplicated code is its own kind of a waste, agility or not. This is where good practices are essential, such as using back end for front end to push most of business logic to the server, leaving mostly presentation logic on the client. The business logic can then reuse libraries and modules across microservices (within reason, you need avoid the danger of increased coupling so don’t go overboard).
If you do it right, you will already use common style, common APIs, shared libraries and common widgets. The rest is just composition, where you can create a bespoke UI that perfectly fits your page and nobody will break you by pushing a change without enough testing at 2am.
Yes, there are exceptions. Some UI components are just too tricky to implement even once, let alone multiple times. You will know those when you see them – the microservice teams will come together and agree that one of them will build it for all of the teams because life is too short to fix that horrible bug 5 times.
The threshold to clear will be:
- Is this truly a single component, or multiple components merged together, selected with a maze of flow control and configuration parameters? If latter, don’t.
- Is the component simple enough and with a single purpose, more likely to survive inevitable zigs and zags of business requirements and design changes?
- Is the team writing the shared component up to the task and ready to write exhaustive functional and UI tests to guard against regressions and breakages?
- Is the problem complex enough to overcome the overhead of reuse?
Once you are true to these conditions, reusable components will be few and far between. If a once useful reused component becomes a sore point in a year, fork and take the control back.
You will reap the consequences
In complex microservice systems, some of the truisms of software development need to be put to the test and re-examined. The true test is not if applying them works at the current moment (at the planetary conjunction), but if the system will survive the opposing forces of changing business requirements, architecture evolution, aging code and organizational changes.
As engineers, we must remember that business defines requirements, design defines the UX, but we are responsible for turning the requirements and the design into clickable reality. It is our responsibility to choose that reality not only based on what our system looks like today, but what it may look like in 6, 12 or 18 months. Resist the urge to reach for quick shortcuts through casual reuse, and plan for what situation that reuse will likely put you after a few architectural and organization changes.
Like planets, your microservices will never stand still.
© Dejan Glozic, 2017