How to build, test and deploy React Applications in 2017
Unless you are living in a cave, continuous integration (CI) is all the rage, but it’s not just a practice exclusive to backend guys; front-end developers have no excuse not to use it. This article is written especially for React.js developers.
CI is the development practice of integrating code into a single build multiple times per day.
Builds are usually automated and depend on thorough automatic tests to ensure they complete properly and without any issues.
Have a look at this article by Martin Fowler if you want to know more about continuous integration.
I’m going to assume you’re already doing the following:
1- You are storing your code in version control (git, hg…).
2- You’re not adding all automatically generated files into the ignore list of your version control, i.e., node_modules, generated js, CSS.
Before continuing, I would like to define some terminology I will use so that we all are on the same page:
- Build process: the process of converting source code into an “executable” bundle by the browser.
- The build: The output of the previous process, an artifact which is produced during the build process that includes interpreted source code and compiled assets.
- Deployment: Process of putting (deploying) the BUILD to a server.
If you want to know more about how to choose a CI here is an article worth reading.
You should configure it to build on each commit pushed to your version control.
1- Package Management
Now that you have a repository and a working CI system, the first thing to do is to install your code dependencies.
In the React.js world, there are two major dependency managers: NPM and Yarn. NPM is the traditional package manager for Node.js. Yarn is the new kid on the block, it emerged as an attempt to solve some of the issues experienced with NPM:
- Determinism: guarantee that an installation that worked on one system will work exactly the same way on any other system, thanks to a concise lockfile (yarn.lock) and a deterministic installation algorithm.
- Security: Yarn uses checksums to verify the integrity of every installed package before its code is executed.
- Speed: Yarn caches each package it download so it never needs to download it again.
To learn more about Yarn you can check this article.
Basically, you should be using Yarn as your dependency manager. Here is a link to the first take from NPM’s CEO on Yarn.
Here are some best practices and tips to use Yarn:
- Always commit your yarn.lock file.
- Yarn consumes the same file format as NPM. If you want to test Yarn on an existing project, just type `yarn`.
- Learn more about NPM vs Yarn using this cheat sheet.
Once you have installed your dependencies, you’ll want to set up an important tool to monitor the quality of your code: a linter.
In short, linting is used to find inconsistent or undesirable code in a project. By linting your code, you can catch bugs early, avoid potential mistakes, enforce a coding standard and keep your code clean.
- Add an `.eslintrc` to the root of your project, and always commit it.
- If you don’t know where to start, take a look at some configuration defaults by the Walmart or Airbnb teams.
The next thing you need to consider is testing. We write tests to add confidence about our code, as they allow engineers to move faster when adding features and refactoring.
We recommend using Jest because it includes a fake dom (jsdom), integrates a manual mocking library, and is very well suited for testing React.js apps. It can even capture snapshots of React trees or other serializable values to simplify UI testing.
Jest is also fast as it runs tests in parallel across worker process. You can also get code coverage out-of-the-box, by simply running the jest command with a coverage flag.
At this stage you want to bundle your code and all its dependencies, which is commonly done with a tool called a bundler. There are many choices these days, with the most popular ones being Browserify and webpack.
With Browserify you use a task runner such as Gulp or Grunt and a long list of transforms and plugins to bundle your code.
Webpack offers enough power out of the box that you typically don’t need Grunt or Gulp at all, and you can declare a simple configuration file to define your build process.
4-1 Chunking or Code splitting
As your bundle starts growing larger your load time will increase, so you should consider bundling code bootstrapping the application into one single file, and the code used on each page as a separate file. This can be done fairly easily with webpack.
4-2 Cache busting
You could just use a filename like
index.js as your bundle name. But what you should do instead is to add a hash (e.g. SHA) of the file itself in the filename, i.e.,
index.hash.js. This allows browsers to fetch the up-to-date file. You can implement that easily with Webpack chunk name.
Now you‘ll probably want to create the build artifact. The key thing here is that the artifact should only include the things needed to run your application. Because of this, you’ll want to exclude the source files that have been transformed into compiled assets such as LESS, SASS, Coffeescript, as well as non-code directories such as .git, node_modules, test, etc…
Here an example of a working CI pipeline consisting of two jobs:
Input: Environment variable + your source code
Task: Launch the build script
Output: Build Artifact, Build ID
Input: Build ID
- Push the build artifact to Amazon S3 with the commit ID, the branch name, and the build number.
- Tag the commit with the build number and branch name if applicable.
You should have one pipeline for your master branch and one for your feature and hotfix branches.
For the deploy job of your feature and hotfix branches, you can use URLs such as https://staging.com/feature_name to access a live version of your web app in a staging environment. You should also set up your web app router to take a base path as a parameter. Here, for example, it’s feature_name, but on your production, it will probably just be an empty string.
Rollback should now be easy, you can just launch a new deploy job with the build ID you want to roll back to.