Credits: Illustration by @yangheng
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
Good understanding of core programming concepts.
Comfortable with basic command line actions and familiarity with source code version control systems such as Git.
Experience in web development. Have built server-side rendered web apps using frameworks like Ruby on Rails, Django, Express, etc.
Understanding of how the web works. Familiarity with web protocols and conventions like HTTP and RESTful APIs.
Table of Contents
Single-page Apps (SPAs)
Coding with Style
Certain topics can be skipped if you have prior experience in them.
Single-page Apps (SPAs)
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.
The app feels more responsive and users do not see the flash between page navigations due to full-page refreshes.
Fewer HTTP requests are made to the server, as the same assets do not have to be downloaded again for each page load.
Clear separation of the concerns between the client and the server; you can easily build new clients for different platforms (e.g. mobile, chatbots, smart watches) without having to modify the server code. You can also modify the technology stack on the client and server independently, as long as the API contract is not broken.
Heavier initial page load due to loading of framework, app code, and assets required for multiple pages.1
There’s an additional step to be done on your server which is to configure it to route all requests to a single entry point and allow client-side routing to take over from there.
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.
Single Page App: advantages and disadvantages
The (R)Evolution of Web Development
Here’s Why Client Side Rendering Won
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.
Learn ES5 on Codecademy
Learn ES2015 on Babel
You Don’t Know JS (Advanced content, optional for beginners)
User Interface – React
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 render() method.
Functional – The view is a pure function of props and state. In most cases, a React component is defined by props (external parameters) and state (internal data). For the same props and 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 props and state to it and comparing the rendered output.
Maintainable – Writing your view in a component-based fashion encourages reusability. We find that defining a component’s propTypes make 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 props are 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 props and 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 highly-rated free course, React Fundamentals by the creators of React Router, who are experts from the React community. It also covers more advanced concepts 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…
React Official Tutorial
Simple React Development in 2017
Presentational and Container Components
State Management – Flux/Redux
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:
Unidirectional data flow – Makes the app more predictable as updates can be tracked easily.
Separation of concerns – Each part in the Flux architecture has clear responsibilities and are highly decoupled.
Works well with declarative programming – The store can send updates to the view without specifying how to transition views between states.
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:
Dispatch an action (also a POJO) to modify the state.
Reducer is a pure function that takes in current state and action to produce a new state.
The concepts sound simple, but they are really powerful as they enable apps to:
Have their state rendered on the server, booted up on the client.
Trace, log and backtrack changes in the whole app.
Implement undo/redo functionality easily.
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:
Functional composition paradigm – React composes views (pure functions) while Redux composes pure reducers (also pure functions). Output is predictable given the same set of input.
Easy To Reason About – You may have heard this term many times but what does it actually mean? We interpret it as having control and understanding over our code – Our code behaves in ways we expect it to, and when there are problems, we can find them easily. Through our experience, React and Redux makes debugging simpler. As the data flow is unidirectional, tracing the flow of data (server responses, user input events) is easier and it is straightforward to determine which layer the problem occurs in.
Layered Structure – Each layer in the app / Flux architecture is a pure function, and has clear responsibilities. It is relatively easy to write tests for pure functions.
Development Experience – A lot of effort has gone into creating tools to help in debugging and inspecting the app while development, such as Redux DevTools.
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?
Egghead Course – Getting Started with Redux
Egghead Course – Build React Apps with Idiomatic Redux
React Redux Links
You Might Not Need Redux
Coding with Style – CSS Modules
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.
Learn HTML & CSS course on Codecademy
Intro to HTML/CSS on Khan Academy
CSS Modules Specification
A pattern for writing CSS to scale
Code is read more frequently than it is written. This is especially true at Grab, where the team size is large and we have multiple engineers working across multiple projects. We highly value readability, maintainability and stability of the code and there are a few ways to achieve that: “Extensive testing”, “Consistent coding style” and “Typechecking”.
Testing – Jest + Enzyme
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!
Testing React Applications with Jest
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!
Linting CSS – stylelint
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.
Once you have learnt ESLint, learning stylelint would be effortless considering their similarities. stylelint is currently being used by big companies like Facebook, Github and WordPress.
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!
Lint your CSS with stylelint
Types – Flow
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!
TypeScript vs Flow
Build System – webpack
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).
SurviveJS – Webpack: From apprentice to master
Package Management – Yarn
If you take a peek into your node_modules directory, 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 install in 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.lock file, which ensures that every install results in the exact same file structure in node_modules across 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!
email@example.com was released in May 2017 and it seems to address many of the issues that Yarn aims to solve. Do keep an eye on it!
Estimated Duration: 2 hours.
Good old npm
We use 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:
Run linting for the project.
Run unit tests for the project.
If the tests pass:
Test coverage generated by Jest is uploaded to Codecov.
Generate a production bundle with webpack into a build directory.
tar the build directory as .tar and upload it to an S3 bucket which stores all our tar builds.
Post a notification to Slack to inform about the Travis build result.
Hosting – Amazon S3
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:
Web server receives a HTTP request for a particular route, for example /user/john.
Regardless of which route the server receives, serve up index.html from the static assets directory.
The client-side routing library reads the current route, and communicates to the MVC (or equivalent where relevant) framework about the current route.
A good practice for serving static content is to use caching and putting them on a CDN. We use Amazon Simple Storage Service (S3) because it can both host and act as a CDN for our static website content. 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 .tar files generated from each successful Travis build.
Amazon S3 Homepage
Hosting a Static Website on Amazon S3
Google Cloud Platform
The last step in shipping the product to our users is deployment. We use 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 .tar we want to deploy to our hosting bucket, and Tower does the following:
Download the desired .tar file from our builds S3 bucket.
Extracts the contents and swap in the configuration file for specified environment.
Upload the contents to the hosting bucket.
Post a notification to Slack to inform about the successful deployment.
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.
Ansible Tower Homepage
The Journey has Just Begun
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!
Many thanks to Joel Low, Li Kai and Tan Wei Seng who reviewed drafts of this article.
The Hitchhiker’s guide to the modern front end development workflow
Roadmap to becoming a web developer in 2017
Other Study Plans
JS Stack from Scratch
1. This can be solved via webpack code splitting. ↩
2. Universal JS to the rescue! ↩