Front-end at Sesame: a health-check after 2 years

Adrien Noblet
Sesame Engineering
Published in
7 min readJun 28, 2021

Our front-end stack consists of 6 front-end apps sitting on top of our design system and a collection of various shared libraries and tools.

These front-end apps mainly rely on Sesame’s API — a federated API over a galaxy of micro-services — and third-party APIs like Stripe for payment features, Contentful as our headless CMS, Twilio for video and SMS, and Split-io for feature flags. Among these services, our GraphQL API is the main interface with the back-end; and is looked after by both the front-end engineers and the back-end engineers.

Now that we’re all caught up, let’s go over some of the components of our stack.

https://www.commitstrip.com/en/2018/10/05/stack-doubt/

React & Next.js

Next.js is powering sesamecare.com and 3 others apps: our provider portal, our admin portal and our authentication app. Being a React framework, out-of-the box server-side rendering and having build niceties had a major role in the reasoning behind that choice.

Our setup is pretty standard, running the latest Next.js version (v10 when this article is being written), with the addition of a custom node server, using express.

While having a custom server is fully-supported by Next.js, we’ve been working to implement a lot of that logic in a way that is more compatible with Next.js, instead of working around it. We still rely on it for health checks, logging, tracing (with Jaeger) and authentication.

The latter, authentication, being one of the last chunks we’ll have to move out of the node server to ease some static site generation features and typescript work.

Would we choose Next.js again? Very likely.

  • Server-side rendering is hard;
  • Latest upgrades have been great adding great features like: static site generation, rewrites/redirects, webpack 5;
  • It can be difficult at times to wrap your head around what is client-side and what is server-side, or what and how you import is key to keep your bundle size at bay.

Sesame’s design system

In close collaboration with Design and Product, we built a custom design system to power our various front-end apps.

The design system is home to design tokens, a shared library of themeable UI components, documentation and a unified set of rules and patterns for scalable design. All of this makes it a key component that shapes our approach and workflow designing and building features.

Our design system, while not as colorful as these two macaws, it’s still a fitting name

Our design system — macaw — is built using styled-components and Storybook.

Storybook lets us build components in isolation, organize them and view them for us engineers or to collaborate with product and design. With Chromatic on top, it also provides us a great visual-testing solution, running as a PR checker in Github Actions. Pretty nice!

Would we recommend Storybook? It is a no-brainer, it has become the de facto standard of tools for design systems. Storybook v6 brought Docs page and composition that we still have to fully take advantage of!

Our styling solution of choice has been styled-components from the very beginning. It is arguably the most famous CSS-in-JS solution out there. Using styled-components, we were able to gradually release a huge rebrand in September 2020 by leveraging themes and feature flags.

As for styled-components, it does not play nice with Next.js and server-side rendering. It was the root cause of a massive incident, in Feb. 2020, due to a memory leak issue. The tedious investigation was a hectic couple of days and is a story that will eventually make its way to this blog.

Contentful as our headless CMS

Contentful is widely used in our consumer-facing app to create pages and add more content to pages. Contentful is a headless CMS product originally from the lovely city — which happens to also be where the bulk of Sesame’s engineering team is in — Berlin.

Editors build pages using modules, each module being mapped to carefully crafted React components. Some components are quite simple, displaying content or navs. Others will be connected to our back-end through our GraphQL API and enrich the content with dynamic data.

Our setup gives us a lot of flexibility to put together new pages and allows us to move and try different messaging and flows really quickly. Additionally, we built some tooling to manage the Contentful schema using migrations and manage preview environments.

The key aspect of a headless CMS is actually how good your database schema design is (eg. content models in Contentful). Contentful limits the number of content models you can get and more importantly has complexity limits on every query. On the other hand, if you have too few content models, it will be hard for your editors to know which module is what and make sense of the 27 options on each of them.

Our current setup leans on the former with many content models, representing how it is looking more than describing the data itself. While it was a way for us to move fast without breaking things, it also goes against the classic headless CMS approach and makes it harder to work with Contentful on the front-end.

Contentful is quite restrictive and the more you have, the more likely you’ll be to hit the Contentful query complexity limit and/or see your performance take a dip (size of the request, size of the payload). This is going to be one of our challenges in the months to come.

GraphQL as an API gateway

Our GraphQL API — codename: Quokka — has become a key piece of our stack over the last year, running with Apollo on both the client and the server.

We’re using our GraphQL API as a layer of indirection between the front-end and our 40+ back-end services:

  1. Ensuring a common taxonomy for resources across all services;
  2. Adjusting and enriching data for the front-end, therefore minimizing the need for many mappings and combining requests;
  3. Providing caching and type safety to data-fetching on the front-end;
Quokkas are friendly and that’s where the name of our GraphQL API comes from, it’s friendly to the front-end apps

Point 2. is what brings a lot of people to GraphQL. However, looking after our GraphQL schema and ensuring a unified vocabulary made it easier for us to build reusable UI components, reduced errors and improved collaboration between teams.

GraphQL really shines as a layer of indirection when combining multiple data sources, helping with back-ward compatibility, phasing out a specific endpoint or an entire service, etc. This is what justifies what sometimes feels like an overhead wrapping perfectly usable REST endpoints in a GraphQL query or mutation.

On the server, Apollo Server has definitely been serving us well, especially with the help of apollo-datasource-rest to manage data-fetching from our many micro-services. It's a great pattern that in combination with Typescript types auto-generated from the back-end REST API and from the GraphQL schema, made working within the codebase way easier for front-end and back-end engineers. In addition to that, we’re using Apollo Studio, a dashboard and monitoring tools which we’re very happy with.

When it comes to build a GraphQL API server as a gateway for a micro-services architecture, Apollo Server brings good patterns and tools.

On the client, it is unclear if we would go for @apollo/client again. There are a few quirks with the way it integrates with Next.js, caching is hard (always is), and finally, mocking and testing has proved challenging.

Flavors of tests for the front-end

To test our front-end we rely on a variety of tests: unit tests with jest, UI tests with @testing-library/react, end-to-end tests with Cypress and visual tests with Chromatic. These tests run for every PR as part of our CI/CD pipeline (via Github Actions).

Unit tests and UI tests are pretty straightforward and besides some shenanigans with mocks and @apollo/client , we have a conventional tests setup when it comes to front-end test coverage in 2021.

For our visual regression testing we perform checks using Chromatic in our CI/CD pipeline. It provides us a better experience than more traditional snapshots testing. It is an awesome tool to have, and another security net when making changes to UI components used in different apps and contexts.

We have it as a PR checker and the default (and only) behavior of the Github Actions integration is to run for every single commit. That quickly got out of hands reaching thousands per month. Our current solution is to only run Chromatic when a specific label is applied to the pull request. We make it work but it is a brittle workaround.

End-to-end tests are focusing on the key flows of the apps, running against our development environment. Among some of the challenges we have faced or still facing:

  • Organizing test suites in manageable chunks, especially when it comes to time it takes for them to complete;
  • Running against our development environment, our tests are dependent on external services and a development data set;

Conclusion

These are a few of the building blocks of the front-end stack we’ve been running in the last 2 years or so. The front-end stack at Sesame is in a continuous evolution and we continue to experiment and have yet to nail many things.

That’s what is cool with software engineering, right?

--

--