📚 Study guide and introduction to the modern front end stack.
This guide has been cross-posted on Free Code Camp.
Grab is Southeast Asia (SEA)'s leading transportation platform and our mission is to drive SEA forward, leveraging on the latest technology and the talented people we have in the company. As of May 2017, we handle 2.3 million rides daily and we are growing and hiring at a rapid scale.
If you are familiar with front end development and have been consistently keeping up with the latest developments, this guide will probably not be that useful to you. It is targeted at newcomers to front end.
- Grab Web Team
Certain topics can be skipped if you have prior experience in them.
Web developers these days refer to the products they build as web apps, rather than websites. While there is no strict difference between the two terms, web apps tend to be highly interactive and dynamic, allowing the user to perform actions and receive a response for their action. Traditionally, the browser receives HTML from the server and renders it. When the user navigates to another URL, a full-page refresh is required and the server sends fresh new HTML for the new page. This is called server-side rendering.
While traditional server-side rendered apps are still a viable option, a clear client-server separation scales better for larger engineering teams, as the client and server code can be developed and released independently. This is especially so at Grab when we have multiple client apps hitting the same API server.
babel-preset-envintelligently determines which Babel plugins are necessary (which new language features are not supported and have to be transpiled) as browsers increase native support for more ES language features. If you prefer using language features that are already stable, you may find that babel-preset-stage-3, which is a complete specification that will most likely be implemented in browsers, will be more suitable.
Spend a day or two revising ES5 and exploring ES2015. The more heavily used features in ES2015 include "Arrows and Lexical This", "Classes", "Template Strings", "Destructuring", "Default/Rest/Spread operators", and "Importing and Exporting modules".
Estimated Duration: 3-4 days. You can learn/lookup the syntax as you learn the other libraries and try building your own app.
Declarative - You describe what you want to see in your view and not how to achieve it. In the jQuery days, developers would have to come up with a series of steps to manipulate the DOM to get from one app state to the next. In React, you simply change the state within the component and the view will update itself according to the state. It is also easy to determine how the component will look like just by looking at the markup in the
Functional - The view is a pure function of
state. In most cases, a React component is defined by
props(external parameters) and
state(internal data). For the same
state, the same view is produced. Pure functions are easy to test, and the same goes for functional components. Testing in React is made easy because a component's interfaces are well-defined and you can test the component by supplying different
stateto it and comparing the rendered output.
Maintainable - Writing your view in a component-based fashion encourages reusability. We find that defining a component's
propTypesmake React code self-documenting as the reader can know clearly what is needed to use that component. Lastly, your view and logic is self-contained within the component, and should not be affected nor affect other components. That makes it easy to shift components around during large-scale refactoring, as long as the same
propsare supplied to the component.
Ease of Learning - Learning React is pretty simple. The React API surface is relatively small compared to this; there are only a few APIs to learn and they do not change often. The React community is one of the largest, and along with that comes a vibrant ecosystem of tools, open-sourced UI components, and a ton of great resources online to get you started on learning React.
Developer Experience - There are a number of tools that improves the development experience with React. React Developer Tools is a browser extension that allows you to inspect your component, view and manipulate its
state. Hot reloading with webpack allows you to view changes to your code in your browser, without you having to refresh the browser. Front end development involves a lot of tweaking code, saving and then refreshing the browser. Hot reloading helps you by eliminating the last step. When there are library updates, Facebook provides codemod scripts to help you migrate your code to the new APIs. This makes the upgrading process relatively pain-free. Kudos to the Facebook team for their dedication in making the development experience with React great.
Over the years, new view libraries that are even more performant than React have emerged. React may not be the fastest library out there, but in terms of the ecosystem, overall usage experience and benefits, it is still one of the greatest. Facebook is also channeling efforts into making React even faster with a rewrite of the underlying reconciliation algorithm. The concepts that React introduced has taught us how to write better code, more maintainable web apps and made us better engineers. We like that.
We recommend going through the tutorial on building a tic-tac-toe game on the React homepage to get a feel of what React is and what it does. For more in-depth learning, check out the Egghead course, Build Your First Production Quality React App. It covers some advanced concepts and real-world usages that are not covered by the React documentation. Create React App by Facebook is a tool to scaffold a React project with minimal configuration and is highly recommended to use for starting new React projects.
React is a library, not a framework, and does not deal with the layers below the view - the app state. More on that later.
Estimated Duration: 3-4 days. Try building simple projects like a to-do list, Hacker News clone with pure React. You will slowly gain an appreciation for it and perhaps face some problems along the way that isn't solved by React, which brings us to the next topic...
As your app grows bigger, you may find that the app structure becomes a little messy. Components throughout the app may have to share and display common data but there is no elegant way to handle that in React. After all, React is just the view layer, it does not dictate how you structure the other layers of your app, such as the model and the controller, in traditional MVC paradigms. In an effort to solve this, Facebook invented Flux, an app architecture that complements React's composable view components by utilizing a unidirectional data flow. Read more about how Flux works here. In summary, the Flux pattern has the following characteristics:
As Flux is not a framework per se, developers have tried to come up with many implementations of the Flux pattern. Eventually, a clear winner emerged, which was Redux. Redux combines the ideas from Flux, Command pattern and Elm architecture and is the de facto state management library developers use with React these days. Its core concepts are:
The concepts sound simple, but they are really powerful as they enable apps to:
The creator of Redux, Dan Abramov, has taken great care in writing up detailed documentation for Redux, along with creating comprehensive video tutorials for learning basic and advanced Redux. They are extremely helpful resources for learning Redux.
Combining View and State
While Redux does not necessarily have to be used with React, it is highly recommended as they play very well with each other. React and Redux have a lot of ideas and traits in common:
Your app will likely have to deal with async calls like making remote API requests. redux-thunk and redux-saga were created to solve those problems. They may take some time to understand as they require understanding of functional programming and generators. Our advice is to deal with it only when you need it.
react-redux is an official React binding for Redux and is very simple to learn.
Estimated Duration: 4 days. The egghead courses can be a little time-consuming but they are worth spending time on. After learning Redux, you can try incorporating it into the React projects you have built. Does Redux solve some of the state management issues you were struggling with in pure React?
CSS (Cascading Style Sheets) are rules to describe how your HTML elements look. Writing good CSS is hard. It usually takes many years of experience and frustration of shooting yourself in the foot before one is able to write maintainable and scalable CSS. CSS, having a global namespace, is fundamentally designed for web documents, and not really for web apps that favor a components architecture. Hence, experienced front end developers have designed methodologies to guide people on how to write organized CSS for complex projects, such as using SMACSS, BEM, SUIT CSS, etc.
However, the encapsulation of styles that these methodologies bring about are artificially enforced by conventions and guidelines. They break the moment developers do not follow them.
As you might have realized by now, the front end ecosystem is saturated with tools, and unsurprisingly, tools have been invented to partially solve some of the problems with writing CSS at scale. "At scale" means that many developers are working on the same large project and touching the same stylesheets. There is no community-agreed approach on writing CSS in JS at the moment, and we are hoping that one day a winner would emerge, just like Redux did, among all the Flux implementations. For now, we are banking on CSS Modules. CSS modules is an improvement over existing CSS that aims to fix the problem of global namespace in CSS; it enables you to write styles that are local by default and encapsulated to your component. This feature is achieved via tooling. With CSS modules, large teams can write modular and reusable CSS without fear of conflict or overriding other parts of the app. However, at the end of the day, CSS modules are still being compiled into normal globally-namespaced CSS that browsers recognize, and it is still important to learn and understand how raw CSS works.
If you are a total beginner to CSS, Codecademy's HTML & CSS course will be a good introduction to you. Next, read up on the Sass preprocessor, an extension of the CSS language which adds syntactic improvements and encourages style reusability. Study the CSS methodologies mentioned above, and lastly, CSS modules.
Estimated Duration: 3-4 days. Try styling up your app using the SMACSS/BEM approach and/or CSS modules.
Jest is a testing library by Facebook that aims to make the process of testing pain-free. As with Facebook projects, it provides a great development experience out of the box. Tests can be run in parallel resulting in shorter duration. During watch mode, by default, only the tests for the changed files are run. One particular feature we like is "Snapshot Testing". Jest can save the generated output of your React component and Redux state and save it as serialized files, so you wouldn't have to manually come up with the expected output yourself. Jest also comes with built-in mocking, assertion and test coverage. One library to rule them all!
React comes with some testing utilities, but Enzyme by Airbnb makes it easier to generate, assert, manipulate and traverse your React components' output with a jQuery-like API. It is recommended that Enzyme be used to test React components.
Jest and Enzyme makes writing front end tests fun and easy. When writing tests becomes enjoyable, developers write more tests. It also helps that React components and Redux actions/reducers are relatively easy to test because of clearly defined responsibilities and interfaces. For React components, we can test that given some
props, the desired DOM is rendered, and that callbacks are fired upon certain simulated user interactions. For Redux reducers, we can test that given a prior state and an action, a resulting state is produced.
The documentation for Jest and Enzyme are pretty concise, and it should be sufficient to learn them by reading it.
Estimated Duration: 2-3 days. Try writing Jest + Enzyme tests for your React + Redux app!
For the most part, using ESLint is as simple as tweaking a configuration file in your project folder. There's nothing much to learn about ESLint if you're not writing new rules for it. Just be aware of the errors when they surface and Google it to find out the recommended style.
Estimated Duration: 1/2 day. Nothing much to learn here. Add ESLint to your project and fix the linting errors!
As mentioned earlier, good CSS is notoriously hard to write. Usage of static analysis tools on CSS can help to maintain our CSS code quality and coding style. For linting CSS, we use stylelint. Like ESLint, stylelint is designed in a very modular fashion, allowing developers to turn rules on/off and write custom plugins for it. Besides CSS, stylelint is able to parse SCSS and has experimental support for Less, which lowers the barrier for most existing code bases to adopt it.
One downside of stylelint is that the autofix feature is not fully mature yet, and is only able to fix for a limited number of rules. However, this issue should improve with time.
Estimated Duration: 1/2 day. Nothing much to learn here. Add stylelint to your project and fix the linting errors!
In most cases, it is recommended to setup Prettier on top of ESLint and stylelint and integrate it into the workflow. This allows the code to be formatted into consistent style automatically via commit hooks, so that developers do not need to spend time formatting the coding style manually.
Note that Prettier only enforces coding style, but does not check for logic errors in the code. Hence it is not a replacement for linters.
Estimated Duration: 1/2 day. Nothing much to learn here. Add Prettier to your project and add hooks to enforce the coding style!
Static typing brings about many benefits when writing apps. They can catch common bugs and errors in your code early. Types also serve as a form of documentation for your code and improves the readability of your code. As a code base grows larger, we see the importance of types as they gives us greater confidence when we do refactoring. It is also easier to onboard new members of the team to the project when it is clear what kind of values each object holds and what each function expects.
Adding types to your code comes with the trade-off of increased verbosity and a learning curve of the syntax. But this learning cost is paid upfront and amortized over time. In complex projects where the maintainability of the code matters and the people working on it change over time, adding types to the code brings about more benefits than disadvantages.
Recently, I had to fix a bug in a code base that I haven’t touched in months. It was thanks to types that I could easily refresh myself on what the code was doing, and gave me confidence in the fix I made.
Anyway, it is not extremely difficult to move from Flow to TypeScript as the syntax and semantics are quite similar, and we will re-evaluate the situation in time to come. After all, using one is better than not using any at all.
Flow recently revamped their homepage and it's pretty neat now!
This part will be kept short as setting up webpack can be a tedious process and might be a turn-off to developers who are already overwhelmed by the barrage of new things they have to learn for front end development. In a nutshell, webpack is a module bundler that compiles a front end project and its dependencies into a final bundle to be served to users. Usually, projects will already have the webpack configuration set up and developers rarely have to change it. Having an understanding of webpack is still a good to have in the long run. It is due to webpack that features like hot reloading and CSS modules are made possible.
We have found the webpack walkthrough by SurviveJS to be the best resource on learning webpack. It is a good complement to the official documentation and we recommend following the walkthrough first and referring to the documentation later when the need for further customization arises.
Estimated Duration: 2 days (Optional).
If you take a peek into your
node_modulesdirectory, you will be appalled by the number of directories that are contained in it. Each babel plugin, lodash function, is a package on its own. When you have multiple projects, these packages are duplicated across each project and they are largely similar. Each time you run
npm installin a new project, these packages are downloaded over and over again even though they already exist in some other project in your computer.
There was also the problem of non-determinism in the installed packages via
npm install. Some of our CI builds fail because at the point of time when the CI server installs the dependencies, it pulled in minor updates to some packages that contained breaking changes. This would not have happened if library authors respected semver and engineers did not assume that API contracts would be respected all the time.
Yarn solves these problems. The issue of non-determinism of installed packages is handled via a
yarn.lockfile, which ensures that every install results in the exact same file structure in
node_modulesacross all machines. Yarn utilizes a global cache directory within your machine, and packages that have been downloaded before do not have to be downloaded again. This also enables offline installation of dependencies!
The most common Yarn commands can be found here. Most other yarn commands are similar to the
npmequivalents and it is fine to use the
npmversions instead. One of our favorite commands is
Estimated Duration: 2 hours.
We used Travis CI for our continuous integration (CI) pipeline. Travis is a highly popular CI on Github and its build matrix feature is useful for repositories which contain multiple projects like Grab's. We configured Travis to do the following:
.tarand upload it to an S3 bucket which stores all our tar builds.
Traditionally, web servers that receive a request for a webpage will render the contents on the server, and return a HTML page with dynamic content meant for the requester. This is known as server-side rendering. As mentioned earlier in the section on Single-page Apps, modern web applications do not involve server-side rendering, and it is sufficient to use a web server that serves static asset files. Nginx and Apache are possible options and not much configuration is required to get things up and runnning. The caveat is that the web server will have to be configured to route all requests to a single entry point and allow client-side routing to take over. The flow for front end routing goes like this:
index.htmlfrom the static assets directory.
UsersController, fetch user data for the username
johnas JSON, combine the data with the view, and render it on the page.
A good practice for serving static content is to use caching and putting them on a CDN. We use Amazon Simple Storage Service (S3) for hosting our static website content and Amazon CloudFront as the CDN. We find that it is an affordable and reliable solution that meets our needs.
S3 provides the option to "Use this bucket to host a website", which essentially directs the requests for all routes to the root of the bucket, which means we do not need our own web servers with special routing configurations.
An example of a web app that we host on S3 is Hub.
Other than hosting the website, we also use S3 to host the build
.tarfiles generated from each successful CI build.
The last step in shipping the product to our users is deployment. We used Ansible Tower which is a powerful automation software that enables us to deploy our builds easily.
As mentioned earlier, all our commits, upon successful build, are being uploaded to a central S3 bucket for builds. We follow semver for our releases and have commands to automatically generate release notes for the latest release. When it is time to release, we run a command to tag the latest commit and push to our code hosting environment. Travis will run the CI steps on that tagged commit and upload a tar file (such as
1.0.1.tar) with the version to our S3 bucket for builds.
On Tower, we simply have to specify the name of the
.tarwe want to deploy to our hosting bucket, and Tower does the following:
.tarfile from our builds S3 bucket.
This whole process is done under 30 seconds and using Tower has made deployments and rollbacks easy. If we realize that a faulty deployment has occurred, we can simply find the previous stable tag and deploy it.
After shipping the product, you would also want to monitor it for any errors.
Apart from network level monitoring from our CDN service provider and hosting provider, we use Sentry to monitor errors that originates from the app logic.
Congratulations on making it this far! Front end development today is hard, but it is also more interesting than before. What we have covered so far will help any new engineer to Grab's web team to get up to speed with our technologies pretty quickly. There are many more things to be learnt, but building up a solid foundation in the essentials will aid in learning the rest of the technologies. This helpful front end web developer roadmap shows the alternative technologies available for each aspect.
We made our technical decisions based on what was important to a rapidly growing Grab Engineering team - maintainability and stability of the code base. These decisions may or may not apply to smaller teams and projects. Do evaluate what works best for you and your company.
As the front end ecosystem grows, we are actively exploring, experimenting and evaluating how new technologies can make us a more efficient team and improve our productivity. We hope that this post has given you insights into the front end technologies we use at Grab. If what we are doing interests you, we are hiring!
Other Study Plans