How we built the new gocardless.com
Our website source code is no longer open source on GitHub. We are planning to release a more generic boilerplate project.
We recently deployed
and open sourced a new version of our website.
We've had some great feedback on how fast the site is so we wanted to give a high level
overview of our technical approach in the hope that others will find it useful.
In this post we'll cover both how we made the site so fast and what our development setup looks like.
Using React to render on the server means that we can deliver not only a fully rendered page to the browser, but also the rest of our website too. Once the initial page is displayed, a request is made to fetch the rest of the site as a React application (only 250kB). All subsequent navigation within the website is blindingly fast, not suffering the latency of HTTP requests since the browser already has the whole app.
While we develop against an Express server, we don't deploy that. Since gocardless.com is entirely static, we're able to deploy and serve static HTML. One huge benefit of this is that we can host the site off an S3 bucket and not have to worry about a running web server and everything that goes along with it, such as exception handling, monitoring, security issues, etc.
In order to generate our static HTML we need to know every available URL. Since we're keeping all of our routing configuration in one place, this is trivial; we're able to easily extract the paths for all pages in every locale. Once we have those URLs, we simply crawl our locally running Express app and write the responses to disk ready for deployment.
The main motivation for rebuilding our website was to allow us to more easily manage pages across different locales. We wanted to be able have one place where we could see, for each page, its handler, which locales it was available in, and its route for that locale.
Having one data structure holding all of this information means that you can see the
structure of the entire site in one place. A downside of this is that it can get a bit
tricky querying such a large and nested structure. Since the whole application relies on
this structure, we also need to be really careful not to accidentally mutate it. This is
typically dealt with by lots of defensive
cloneDeeping. Not doing so would (and
initially did) lead to subtle, hard to diagnose bugs across the application.
Our solution to the above problems was to use Facebook's Immutable library.
Using immutable data structures, such as
List, means that we don't have to
worry about mutating the actual routes data structure as it gets passed into
functions that operate on it. Immutable also has a great API for dealing with nested
setIn, which we use extensively.
We're pretty happy with the simplicity of our solution and the user experience that it enables. We hope others can benefit from our experience and, of course, if you have any feedback or suggestions, please get in touch!