For the past several months, Hassan Ali and I have been planning and making changes to the code behind the React application that runs tools.taskcluster.net. During the 2017 San Francisco Mozilla All Hands, we pushed the button and shipped out this newly updated app which paves the way for further enhancements, improvements, and optimizations.

Routing

The previous version of Taskcluster Tools had been written as a multi-page application where each page controlled its URL parameters via the hash. Most of the components you would see upon using Tools were very tightly coupled to the URL, making changes to the data flow of the components was almost impossible without also breaking the URL binding semantics. For instance, let’s say a particular component was expecting to receive its data by pulling the task ID from the URL schema of /#{TASK_ID}. If you wanted to also use this component on another page that already had a different URL schema such as /#{TASK_GROUP_ID}/{TASK_ID}, that wasn’t possible because the component was looking for the hash value in the first position to be the task ID.

We solved this with three steps:

  • Introduce react-router v4 with HTML5 history routing
  • Pass URL parameters as props to page components, with these parameters only flowing from the top of the application down
  • Refactor components to remove the hash-parameter mixins and consume the route props passed to them

With new routes in place, we gave a little greater scrutiny to the naming of some of our paths within the app, and choosing to rename a few of them where they made logical and structural sense. By using parameterized routes instead of arbitrary positions and removing our components’ strong coupling to these routes, it enabled us to make many more changes to Tools with greater improvements.

For the future: Having the routes independent of the components allows us to re-structure, rearrange, and redesign our tools without compromising the links within and external to Tools. We plan on taking advantage of this by delivering a stronger user experience in the next few quarters.

Single-page Application

Utilizing react-router v4 allowed us to improve site loading responsiveness by converting from a multi-page application to a single-page application (SPA). With HTML5 history and a server that serves index.html for all routes, it gives the appearance of multiple pages with the speed and size optimizations that can accompany SPAs. Jumping from page to page no longer requires a complete asset download.

For the future: Using SPAs should allow us to making easier upgrades to Neutrino to optimize the site further. Incorporating Service Workers and being smarter about which requests we download dynamically are avenues we want to explore further.

Standalone Componentization

With improvements to Neutrino and the new neutrino-preset-react-components, we have the ability to extract components into a generic environment for usage in other applications and ensure their independence. This can be seen concretely with two major components: react-lazylog and react-vnc-display. The lazylog contains the components necessary to efficiently render the logs you see when using the Task & Group Inspector. VNC Display contains a component which allows you to connect to a remote desktop session directly through the browser. Each of these components used t0 be inlined into the Tools application, and sometimes this brought along some extra asset baggage which increased the maintainable surface area of the app. By extracting these into discrete components, there is a smaller amount to maintain directly within Tools, while allowing other applications to take advantage of the work we have done.

For the future: Extracting even more components allows us to have greater UX and functional parity between different applications. Should other applications desire to use some of the existing functionality, we can explore extracting these bits into their own components and increase sharing.

Deployment

Changing the routing and moving Tools to a single-page application also enabled us to move our deployment from AWS S3 and into Heroku, which is a bit easier to control and management deployments. Backed by nginx, we can more efficiently serve the static tools.taskcluster.net and have finer power over HTTP headers and rollback capabilities.

The application is still being cached with AWS CloudFront, but we made significant improvements to the caching strategy to ensure that we serve you the correct content for as long as possible, without delivering stale assets. The previous caching settings we were using unfortunately did not do this correctly, which caused some headaches in the new deployment, but this was overcome eventually with some advice on local cache clearing.

For the future: We hope to utilize Heroku’s Review Apps to help automate and improve how we perform code reviews against Tools. We also plan on speeding up deployment time by directly uploading our Travis build assets directly to Heroku upon a successful pull request merge and master build. We may also explore using a more “bare-metal” nginx configuration for even greater control of our sites.

Security

Moving the backing of Tools out of S3 into Heroku allowed us to make important security changes to our delivery of tools.taskcluster.net. Previously we were vulnerable to CSP-based security vectors and click-jacking attacks, which could potentially compromise the credentials of a Tools user. At the time of writing, neither S3 nor CloudFront afforded us the capability of adding the necessary HTTP headers we needed to combat these types of attacks. Upon moving to our new deployment, we put CSP and frame headers in place, and tested that these particular vectors has been closed.

For the future: We plan on performing a deeper security analysis of Tools and determining if there are additional changes we could make to improve overall security.

Optimization

The single-page re-architecture of Tools granted us a whole host of optimization features from Neutrino and our build preset to improve the size and speed of the application we deliver.

The previous iteration of Tools produced a site directory of close to 120MB, with each page weighing in around 5–6MB of their own independent, non-shared assets; every navigation to a different page would download their own complete assets.

The new Tools now produce a site directory closer to around 12MB, a significant reduction of 90%. Each “page” references a shared bundle around 2MB in size, but this bundle is cached for all pages, meaning navigating to each page only needs, on average, less than 100KB of additional download to be interactive.

Being a SPA, the concern is that you must download the entire application in order to use it all, but this is not a concern with Tools. We dynamically download the assets necessary only for the page you visit, and only when you navigate to a different page do we download the additional assets needed to continue to that page.

This dynamic page downloading and rendering, coupled with well-cached build assets, better HTTP caching controls, and minification helps us deliver a smaller payload and therefore a faster user experience.

For the future: We want to investigate serving our content with HTTP2 to see if we can realize an even speedier load time for Tools.

Conclusion

Many moving parts came together for several improvements to Taskcluster’s Tools. We hope this overview gives a good impression of the work that has been done, as well as the motivation behind why we decided to make these changes. We hope you enjoy them.