Read the article

On the surface a website is a simple application: you read a page, click a link, look at a picture. On our website, we serve roughly the same content to each user, across a bunch of pages. If you click a link you’ll go to a new page, easy peasy. So you may wonder where the complexity lies, and why it even warrants a blog post.

In June of 2022, we embarked on a long journey to move our main website from two disparate frameworks; Jekyll and Gatsby, to NextJS. Unifying our code, and aligning how we build our apps with the rest of web discipline at Monzo.

The whole process took us from June of 2022 to November of 2023, with plenty of fun curveballs along the way. Let’s dig into what it takes to migrate

Getting everyone on board the migration train 🚂

We have thousands of pieces of content on our website, each owned by different teams within Monzo. Investments, Savings, Current Accounts and Monzo Flex all want to sing about their features (rightly so, they're great). Likewise, our legal, data privacy, platform, infrastructure and security teams are keen that our website stays compliant, scalable and safe.

So, like every project at Monzo, we start with a proposal, we share what we are doing, why, and how we intend to do it. We invite teams from the whole company to review, feedback, and ultimately, approve the concept we put forward. For teams we really want an opinion from, we'll flag them in slack as a blocking approver.

Once we've got a green light from everyone, and we're all aboard, it's time to go full steam ahead, checking-in with updates and any changes to the plan.

This train is a non-stopping service 🚄

Naturally, as a bank the website has content that we simply can't stop showing, even for a little bit. Customers would understandably be upset if they couldn't review the terms and conditions for their account, and our regulators would be miffed to say the least.

So we set a very low tolerance for downtime. All pages would be available at all times. After experimenting with our approach we settled on creating duplicates of each area, and then redirecting traffic to our shiny new pages.

Because we were able to break each area down into it's own compartment, we also avoided a big bang launch, and instead staggered the change over many separate steps.

Starting Static

The oldest part of our website ran on Jekyll, this is where we kept all of our pages that weren’t served by our Content Management System (CMS).

Where our Gatsby apps could be ported to Nextjs with only a small amount of re-jigging of code, Jekyll provided the biggest transition for code structure, moving from vanilla HTML and JQuery to React.

Staying Legal

Our Legal app contains some of our most important content. Not only does it require moving across to a new location, but we needed to be able to prove no change had taken place.

Our solution was a testing suite that expected a 1:1 match of content for each legal document migrated. For the duration of the migration, we kept our legal documents in sync in two locations to ensure no funny business.

Mapping Components

For our remaining apps, we keep all of our content in Contentful, a ‘headless’ CMS. For each type of content, we have something called an Entry Type, for example a Hero, or a Testimonial Section. In our code we have generic components, which represent small pieces of reusable functionality. A Testimonial could be made up of a set of simpler blocks and buttons, and so can a Hero.

By decoupling contentful content from our components, we can easily swap out our old image for the new one.

By decoupling contentful content from our components, we can easily swap out our old image for the new one.

In the middle we have a map, which allows us to assign entry types to components. This go-between decouples the two, allowing us to make adjustments to either our:

  • Contentful data model (What if we now want to support a subtitle as well as the title?)

  • or components (maybe we need to change from ‘image’ to ‘media’ because we allow videos), without either change breaking the other half.

Staying on track 🛤️

Momentum during a migration is everything, many a migration has run out of steam. With incomplete migrations we accrue excess tech debt. Tech debt can be a useful tool for faster delivery of projects, but when we accrue too much, it can block progress, and slow us down longer term.

Countering the need for momentum, our team also have other priorities to juggle. Long-term Monzo fans will know we had a creative refresh last year, meaning a new colour palette, logo and illustrations across our website, and apps. Running both projects in parallel meant taking a more scenic route, applying our refreshing new styles to not only our new platform, but also our legacy platform simultaneously.

Celebrating milestones on Slack

Keeping a good cadence of communication is essential

A regular post in a public space keeps us accountable and our stakeholders informed of progress.

Celebrating milestones is important, it’s a great way to show appreciation for folks making a positive impact in the squad, and all of the other teams helping make change happen.

It’s also important to record what’s gone wrong, and pointing out any blockers that might slow us down, and stopping us repeating mistakes in the future.

Documenting the important bits

Having a run book of how to make a change can help engineers navigate complicated multi-step processes. The smoothest parts of our migration had the best documentation, and that’s no coincidence.

With such a long journey, there were always going to be changes to schedules and disruptions. Remembering the purpose and impact of the project, and keeping everyone informed both kept us on track to our goal.

Inside the engine room ⚙️

A lot of complexity lies in the code. We run as a Single Page Application (SPA), giving the user a fast, app-like experience when navigating between pages. Under the hood, we are only loading the difference between two pages, rather than a whole new page.

This makes the site feel fast, but means we need to take extra care to abide by React’s rule of keys, making sure all our component loops are unique. React and NextJS handle a lot of complexity for us, serving a totally static html file on first load, and then subsequently collecting just JSON data to replace the content of the page for further navigations within the site.

To keep the project running smoothly we have a set of code generators specially created for the migration. These generators are functions which automatically create and change code for us, cutting down on repetitive tasks like creating components, updating caches of data about our CMS, or creating a report of progress on the project so we can inform stakeholders.

When we generate a new component (a small piece of the website, like a button or footer) there are extra pieces that go along side it, like tests and styles. Creating them manually and hooking them up can be tedious, so we have our generator do it for us.

Generating the skeleton for a new Hero component

Each generator runs as a javascript based command line interface (CLI) with handy prompts for engineers, we even use them to power parts of our Continuous Integration builder.

On the topic of tooling, one piece of advice for budding software engineers: Take some time to learn about Abstract Syntax Trees (ASTs). They are incredibly powerful and can simplify some of the thorniest problems. We couldn't have delivered much of this migration without them. Some of your favourite tools already use them: linters, codemods, compilers and transpilers all rely on ASTs in some guise.

On we use them to generate typescript type definitions from our contentful model, and our components, to make sure we output valid typescript and SCSS.

Code reads: import { Entry, Asset, EntryFields } from 'contentful' as AST

While the above screenshot may look like a long way of writing import { Entry, Asset, EntryFields } from 'contentful' . We know that it will consistently output the correct values, and string concatenation is a sure fire way to introduce bugs 🐛.

Planned engineering works continues

Moving framework is only part one of our journey to an improved, effective Our next stop is server side rendering, and a delve into the back of the frontend. As with all software, our website is forever changing and moving forward. As we look to the future of Monzo, our website will continue to be an important channel (tunnel).

Come work with us!

We regularly update our careers page - check it out and see if there's a role that fits!