The Waitrose.com Journey: 10 things I’ve learned about front-end development & cross-team working

Peter O'Shaughnessy
Waitrose & Partners Digital
11 min readNov 30, 2020

--

Part One — Hidden Complexity, Accessibility, Design Systems & JavaScript Libraries

Now this is a story all about how
Our Waitrose web platform’s getting flipped upside-down
And I’d like to take a few minutes
Just sit right here
I’ll tell you how we‘ve gone from Monolith to Modern in the last few years!

In this post (a version of a talk I gave recently at Cambridge JS), I’ll share some of my top tips and lessons learned from my past two years as a front-end developer for waitrose.com. But to understand the journey we’ve been on, I’d better rewind a bit further…

Our story begins in the year 1999 — not just a great year for a party but a great year for the launch of waitrose.com! A fun fact I just learned recently: we didn’t just launch a website, but a free Internet Service Provider (these were the heady dot-com-bubble days after all!)

The following year, Waitrose entered the world of online grocery deliveries with a service called WaitroseDeliver. And in 2001 they began a partnership with a startup retail tech company called Ocado. Over time, Ocado became quite synonymous with Waitrose online, but it was its own business (although the Partnership owned a stake) with its own website and apps...

Now we can fast-forward to the Spring of 2016. Waitrose.com had by now been running for a number of years on a monolithic e-commerce platform from IBM, called WebSphere Commerce Suite. The team had attempted to keep up with customer demands, but over time the website code had inevitably become a spaghetti of customisations and enhancements, becoming harder and harder to maintain. Releasing an upgrade was a massive event which took weeks of preparation and testing. And on the front-end, the website wasn’t responsive or optimised for mobile phones at all.

A desktop website layout showing how the old-style waitrose.com homepage looked in May 2016

So the wise decision was made to explore moving to a modern architecture, with a React front-end and microservices running in the cloud. A few colleagues formed a team and started working on a proof of concept front-end, based on a boilerplate project from Erik Rasmussen called “react-redux-universal-hot-example”, which included: React, Redux, Node, Express, Babel, Webpack and hot module reloading.

Logos for: React, Redux, Node, Express, Babel and Webpack

The boilerplate had been started the previous year and the README said that at the time of writing:

“all of these libraries are right at the bleeding edge of web development… I personally believe that this stack is the future of web development and will survive for several years”.

Well, it’s definitely no longer bleeding edge, but spoiler alert: it has ended up surviving for several years for us, for the most part. But more on that later…

Another wise decision was taken, to not build the new website in isolation and then attempt to switch everything over in one go, but to gradually migrate over, piece by piece, as much as possible.

The homepage, trolley page and product pages were some of those earlier pieces to get moved over — not just to the new architecture and cloud infrastructure, but to a new responsive design at the same time — looking more like this:

Mobile web views, from left to right: Waitrose homepage, an example Waitrose product page for Smoked Salmon, Waitrose trolley page containing smoked salmon

Soon, the only major pieces yet to be migrated were the Checkout (including the integration with our downstream order processing systems) and Slots (as in, those sought-after delivery and collection slots). And this is what we called Project SAMOSA.

This is where I entered the story myself, in November 2018, when I joined Waitrose & Partners Digital, and specifically the team building out the new Checkout. Again, we weren’t just building like-for-like, but we had a brand new design too.

Mobile views showing from left to right: the Checkout page, the Payment Authentication page and the Order Confirmation page

It was only really 3 pages though: the Checkout page (with some modal windows on top for things like entering gift cards), an optional Payment Authentication page for 3D-Secure (that extra verification step you get with your card provider) and the Order Confirmation page. And the team already had a lot of that set up by the time I joined. So I didn’t imagine it would take nearly two years for these 3 pages to get fully into production, but another spoiler alert: it took nearly two years to get these 3 pages fully into production!

Icebergs

And that brings me to my first ‘lesson learned’:

Lesson Learned 1: Watch out for user interface icebergs…

Some user interfaces appear quite straightforward on the surface, but their complexity goes much deeper than you would imagine.

To give you an idea of the size of our Checkout iceberg, for just those three pages of the new Checkout user interface, we have over 60 different error scenarios, over 180 functional tests and over 2000 unit tests.

It was quick to develop the majority of the user interface, but that was perhaps a bit dangerous in terms of over-inflating any subjective thoughts about how far through the whole project we were. Like the Pareto Principle it felt a bit like we were 80% of the way there in 20% of the time, but we still had 80% of the time remaining to complete the last 20%!

A colleague also made a good observation (with an alternative ratio) when he said: “the unhappy paths end up taking 3 times as long to develop as the happy paths”.

Accessibility

Another aspect that I totally underestimated was accessibility. I had the naive idea that if you used semantic HTML and you didn’t do anything weird, you’d end up with your website being pretty accessible by default. I was wrong.

Lesson Learned 2: If you’re not doing manual accessibility testing, it’s most likely inaccessible

I specify manual testing, because most guidance out there states that automated testing can only pick up somewhere between 20% and 30% of accessibility problems. So it’s only through manual testing that we can find most of the issues. Plus, it’s only through manual testing that we can start to understand what the experience is really like for our customers.

I learned that if you’re not using the keyboard… not using a screen reader… not viewing your pages with a magnifying tool… then the chances are it’s going to be really difficult for people using those tools, to navigate your website.

And I learned that as soon as you start developing in-page interactivity, as opposed to just static pages with hyperlinks in between, then you need to start handling a bunch of things yourself, like maintaining focus in the right place, managing scroll positioning, and more. This is explained well in an article by Nolan Lawson called: “What I’ve Learned About Accessibility in Single Page Apps”.

A “Saved payment card” heading with a visual representation of a VISA card underneath, displaying the card number (bullet points, then 9077), expiry date (05/22) and name (Ms. J Smith)

And then there’s everything in the UI which makes sense when you can see it, but which doesn’t when you can’t. Like our design here for saved payment cards. It visually represents a card — which is nice for our visual users.

But when we actually tried it out with a screen reader, it was a mess… For example, the card number itself was read out (without any additional context) as:

“bullet bullet bullet bullet — bullet bullet bullet bullet — bullet bullet bullet bullet — nine thousand and seventy seven.”

We rejigged the HTML to use a definition list. And applied a “screenreader” class to those elements we only want to be read out by a screen reader, not displayed visually. And added the “aria-hidden” property for the opposite — to display it visually but hide it from screen readers. Now it looks exactly the same, but it’s read out as:

“Card number ending: 9077”

So I learned that I needed to become familiar with at least one screen reader. Most of us developers in Waitrose & Partners Digital use Macs and we’re fortunate to have one built in on our machines: VoiceOver. We’ve also been starting to use NVDA, one of the most popular screen readers on Windows, which is also free. I’ve been using this reference guide for VoiceOver and this reference guide for NVDA.

Design Systems

A nice way to spread good accessibility practices is through a Design System. Because you can get the accessibility right for a component once — and then re-use it across your website, and potentially multiple websites.

When I joined Waitrose, we did have something part-way towards a Design System. We had a shared component library named WDX (Waitrose Design Experience). But we weren’t that great at the most fundamental aspect of a Design System: having developers and designers collaborate closely together. We had our own developer-driven pages documenting the components, but they weren’t really contributed to, or referenced by the design team. We didn’t really have a clear picture of what components we wanted to be in a Design System or how they would fit together. And generally it just didn’t get that much love or attention, because each team was focusing on building out their own part of the website. As a result, we had a lot of duplication and wasted effort.

The Button documentation page from our internal Waitrose Digital Experience website

So we talked on and off for quite a while about starting again and kicking off a proper Design System effort, to address those issues. For a few months, nothing happened though. And I thought nothing was going to happen unless we actually formed a Design System team to kickstart it — this is how our colleagues in John Lewis got their own Design System off the ground a while earlier. But we didn’t have the capacity to take people away from our product teams and still keep our important Project Samosa work on track.

So thankfully, my next lesson to learn was:

Lesson Learned 3: Cross-cutting efforts can be successful without a dedicated team

We had a front-end lead join the team, Almos Gabor, who also quickly identified the Design System as a top priority. So he brought together a working group of people from different teams: developers and designers — and soon afterwards, product owners and delivery leads. We had regular catch-ups focused around actions, we got a good cadence going and soon we had the genesis of our new Design System, called Ingredients. It’s not public yet, but here’s a sneak peek:

A Storybook docs page for our Button component (in-image caption: “Sneak peek”)

For the component library and technical docs, we’re using Storybook, which lets us easily create interactive documentation, with embedded examples of our components. It also has in-built automated accessibility testing (for that 20–40% I mentioned earlier) and we’ve added visual regression testing too.

We’re also using another platform called Frontify for the Design docs — and embedding our Storybook components inside that using iframes. Although we’re investigating at the moment whether we can consolidate that into Storybook itself.

We soon had enough to pave the way for all the teams to start contributing to it themselves. And we helped them to do that by having one of us from the Design System Working Group work together with each team for a sprint.
We designed a workflow around it, but the basic principle is that if you need a component and it’s not in the Design System, you should first contribute one into the Design System, and then you can go ahead and use it for your feature.

The result is not quite that we’ve built the whole thing out super quickly…
But we have built up a decent amount of components in the Design System and some solid design docs, while we’ve been delivering features along the way. And most importantly, we’ve been working more closely with our Design colleagues — and putting more thought into getting our components right, upfront.

Libraries

Earlier I said that our front-end tech stack had largely survived intact for us these past four and half years. We have been replacing a few pieces along the way though…

Logos and arrows indicating moving from Mocha to Jest and from Redux Form to React Hook Form

We’ve swapped Karma, Mocha, Chai and Sinon over to use Jest, which we like because it’s more all-in-one and it has some nice features built in — like snapshot testing, which I’ll discuss in Part Two.

And we’re gradually migrating away from redux-form towards a more in-house approach, making use of react-hook-form which is a lot more straightforward and lightweight. As the name suggests, it makes use of React Hooks, which leads us to…

Lesson Learned 4: React Hooks have been worth adopting

We started introducing hooks into our codebase about a year ago. A typical example was a RouteChange component, where migrating from a class component to a functional, hooks-based component shaved off over 1/3rd of the lines of code.

It’s taken a little bit of time and practice to get our heads round it and I’ve still not written that much hooks-based code myself yet, but it’s been going well. And I think we’ve all been seeing the benefits of reducing boilerplate, encapsulating our logic better and making some of the code less messy.

Introducing hooks into our codebase has meant that we have a mixture of different styles in different places, but that’s OK. The code is always evolving and we just upgrade to our latest style and conventions whenever we next work on a component.

Lesson Learned 5: React Context has been worth adopting too

Colleagues of mine have also been recently starting to use Context.Provider and useContext hooks to manage shared state, in localised areas of the app. And they’ve been finding it really helpful, to prevent us from adding even more state into our Redux Store, which has become very bloated over the years.

An example we had was our multi-level navigation menu, which is quite complex. To implement keyboard commands, we were ending up with refs scattered around the component tree. Until we moved it over to using Context, to maintain a list of refs and handle the keyboard events in a more contained way.

There’s maybe just a slight risk that we could start using this everywhere as a Golden Hammer — but we’ll try to apply it to places like that where it makes sense.

You’ve reached the end of Part One. Read Part Two for the rest of my lessons learned and the end of the story (well, the end of this chapter anyway!)

--

--