This article is inspired by the BBC produces series on nature, with a particular episode dealing with the wildebeest migration. The series was presented by Sir David Attenborough, and I will try and channel his spirit in this article. I have fond memories of these sort of documentaries, watching them together with my father. Those were simpler times.
I’ve used this theme and contents in talks that I gave with a similar title. Note this article details the approach that works best for us at Jumbo Supermarkten. It might not be the best in your situation, so treat its contents as inspirational.
So, there are a couple of impressive migrations. One of the most famous one is the aforementioned wildebeest migration. This involves more than a million wildebeest, zebra’s and gazelles forming a super herd. during their migration they encounter numerous dangers, but are still able to rear calves on the way! 🐃🐃🐃
In the South American rainforests live fire ants. They work together in groups with sizes up until hundreds of thousands of ants for form a floating collection to traverse rivers or ponds.
When they encounter a large body of water, such as a flooded river or a pond, they quickly band together to form a raft using their own bodies. The ants link their legs and form a structure that can float on the water's surface.
This is peak collaboration! 🐜🐜🐜
The elusive pack of …
Lastly, there’s the elusive pack of developers. They typically move about in scrum teams of various sizes and compositions and they have to deal with dangers such as moving requirements, Friday deployments, bug infestations and Github outages.
We will investigate patterns for optimising migration processes and see if there’s anything we can learn from nature.
Let’s start with a definition, so that we have a common understanding of what we’re talking about. Let’s consider a migration as:
☝ A Period of moving large volumes from Point A to Point B.
We’ll break it down into the following:
Period: it doesn’t happen instantly, it takes time and effort;
Moving: could be anything, from ants, to zebra’s to code;
Large volumes: we’re not talking about one gazelle that happens to investigate life on the other side of the valley. It’s a group of things;
Point A to Point B: Well, they have to get somewhere right? They have a job to do at Point B.
Migrations are not without risk. Anything that changes is subject to some risk. But the need is straightforward: The wildebeest basically follow the rain to lush feeding grounds. If they wouldn't, the environment would not be able to sustain, or support the numbers and they will die a horrible death. 💀
The life of a wildebeest is an endless pilgrimage, a constant search for food and water.
Let’s apply it to those poor developers. A developer goal is to be able to quickly push new features and have stability in the environment to release confidently.
Stability versus change
Stability is the opposite of migrating. There's a risk involved here, just as the wildebeests face. I’m not a wildebeest biologist, but I assume that they would probable also prefer stability over a migration. But they still do it.
It's about resources, or support: for the wildebeest it's food, for developers it’s the support of the platform.
Long term support combined with an improvements on developer experience and the ability to create new stability is a good reason to migrate!
Our natural habitat and herd
We’re going to cover a use case, so it’s relevant to give you some context. I work for Jumbo Supermarkten, we’re a grocery chain operating in the Netherlands and Belgium, doing offline and online groceries with deliveries. We’re working with over 450 developers in our tech department (affectionately called Jumbo Tech Campus or JTC) on a range of products.
The developers here are the stewards of the herd, they will need to make sure the herd of applications or code gets to point B safely.
Now that we've met the stewards, let's take a look at the herd and landscape;
A typical teams’ setup is usually scaffolded around a Nuxt application, which gets data via microservices. The Nuxt application probably depends on a couple of custom modules and plugins that we’ve written and handles state via Vuex. To build the user interface we use a Component Library (called Kompas) and the final build also loads some micro applications client side (such as the shopping basket).
Now imagine such a setup, with different types of complexity and size, spread between de company. This is what our super herd looks like.
That’s our cue
This meant we were going to lose support, which, as we’ve learned, a very good reason to migrate! With our knowledge combined, we started to plot a migration path, based on the dependencies and bottlenecks we could identify beforehand (let’s treat those as river crossings, keeping in line with our analogy).
Our micro frontends where a bottleneck due to the state management tooling and API orchestration. We were using Vuex in all our applications. To facilitate inter product communications, we simply exposed the store directly between applications. Metaphorically speaking, these bridges we've been building in the past needed to be rebuilt to accommodate our herd!
We have now moved to a split between internal state management to whatever fits within the team. (Most likely Pinia, Apollo) and expose public methods via tech agnostic APIs so that we don’t repeat our past mistakes.
Nuxt modules and plugins
As mentioned, we had a couple of modules and plugins that supported our Nuxt applications in generic eCommerce related tasks. These were specific to certain domains of the customer journey. The work was distributed between the teams to find a solution, which often meant extracting the core functions and adopting them in compatible wrappers.
One of the biggest river crossings we faced, was our component library. We needed to morph it into a VueJs 2 and 3 compatible supportive library. It is a big library and is used on every user interface on web, so it has a massive impact.
Let’s have a closer look at this particular challenge. We’ve built it for the obvious reasons of building a custom component library. In order to maintain it, we have adopted a distributed approach. This means that everybody that uses the library, is allowed to contribute to the library. We believe that this approach fits with us and has some important benefits:
Investment in the library
As mentioned, everybody who uses the library can improve it. This creates an investment and fosters responsibility from all developers in ensuring that it works and keeps working. Similar to a herd that collectively nurtures their young.
Not as dramatic as the image of circling vultures over a fallen wildebeest, it does happen that people leave your organisation. If they had a key role, it could potentially mean that a lot of knowledge leaves the company. With distributed approach, the knowledge is roughly distributed equally as well, so the risk of a knowledge drain is mitigated.
Short feature cycles
When wildebeest have to cross a fast flowing river, they assist each other: Every animal that takes a step forward helps others to cross the river by blocking the stream or creating a space where another animal can move forward. This is not much different with our distributed model: each team contributes and moves forward, which accelerates other teams as well.
Small shareable increments
And once you've crossed that river, the whole herd can move forward again: we wait until the large body of the herd has moved over before we cross over to the next challenge.
Any addition is immediately shared and usable by other teams across the department, so Product Owners are happy, everybody benefits once you cross that river.
So we couldn't just morph the component library into VueJs 3 since we still need VueJs 2 support. Our approach is more like a tree: we've kept the original trunk in place, which supports VueJs 2 until its End of Life.
Based on that trunk, we've put effort into going through each and every component over time. The distributed approach really helped in sharing the load. We then migrated each to use the composition API using vue-demi to be able to switch between the version. This way we were able to make a VueJs 3 compatible package that exports the original source code, but is fully compatible with VueJs 3.
Once we're fully migrated, we can literally start uprooting the original tree and remove the attachments to VueJs 2.
The road to Point B
So where are we now, in terms of our goals? We’re actually making good progress: We’ve passed a large chunk of river crossings already. New projects are being started in VueJS 3 / Nuxt 3 by default, providing they have no legacy dependencies. Having implemented tech agnostic APIs ensures less issues with future migrations.
It was a massive undertaking, but I think we approached it in a well orchestrated way. As a surprising side effect, it also improved communications between teams.
So our herd has found green pastures again, overcoming challenges as a team. We're not quite there yet though, but we’re closing the gap and are comfortably on schedule. What we’ve learned can be captured lessons.
🐃 Common direction to guide us: Know where you want to go
🦓 Increments to build upon: Deliverables that add values once complete
🦋 No team gets left behind: doing it together, but also backwards compatible
🐜 Distributed approach: allows us to do our day to day job while migrating Not blocking development of new features
I would like to close with this loosely modified quote by Sir David Attenborough:
It seems to me that the natural world* is the greatest source of excitement; the greatest source of visual beauty; the greatest source of intellectual interest. It is the greatest source of so much in life that makes life worth living
* This of course, should be replaced with frontend development
I hope to leave you inspired or at the very least entertained and more up to date on the wonders of nature.