Web development at Margo Bank

Creating a bank from scratch is a big challenge. First and foremost, we need to think long term. Bootstrapping a web application in 2018 that must last for years, decades even, requires thoughtful consideration. The web ecosystem is teeming with new and exciting technologies, so it’s important to have a clear set of objectives and to make educated choices. This article is about how and why we selected the key technical elements that we did.

Decisions were ultimately based on the following criteria (in order of importance):

  1. Maintainability. Since being able to maintain and upgrade the application for years to come is paramount, it is only natural that we give particular importante to decoupling, reusability and testability.
  2. Productivity. Just like everyone else, we need to get things done and to feel comfortable with what we’re doing; however, productivity should not come at the expense of maintainability.
  3. Community and support. Any new technology requires new tools, new best practices, etc., and a strong community makes it easier to find help. As the team needs to scale up, using common practices makes hiring and onboarding of new developers easier as well.

Language and framework

The first structuring decision we made was the language and library/framework we were going to use. JavaScript is still the dominant language for the web, and React, Angular and Vue are the dominant frameworks. There is also Elm, which is both a language and a framework. Even though it echoes the strong functional principles of Elixir, which we use on the backend, we didn’t really consider it due to its very small ecosystem and strong lock-in.

As you can see below, React clearly stands out in the community.

Libraries / frameworks comparison on NPM Trends.

Productivity is a pillar argument for all of them, so we settled the matter on the basis of maintainability and chose to use React.

JavaScript is a dynamically-typed language; it doesn’t require us to define the data types of our constructs before we can use them. Instead, type-checking is performed at runtime. This can be convenient, but it has the drawback of not protecting us against type errors.

Fortunately, tools like Flow and TypeScript emerged to fill this gap by providing a way to add static typing to JavaScript. We chose to use Flow rather than TypeScript because it works well with the React ecosystem.

When annotating the function parameter with a type, flow warns us with a well-formatted error message.

Flow can be easily integrated in IDEs such as Atom or Visual Studio Code. It can give us instant feedback on type errors while we are typing. Combined with tools like ESlint and stylelint to prevent potential errors and Prettier to format the code automatically, it provides an optimal developer experience.

React

React is an open source library created by Facebook in 2013. As stated in the official documentation, it is used internally on more than 50 thousand components, and also in big companies like Twitter, Airbnb, Instagram, WhatsApp, Netflix, etc. Thus, breaking changes are very unlikely and always thoughtfully considered.

The simplest form of a React component.

Like most of its competitors, React is component-based. You can think of a component as a render function that takes properties and returns HTML. Thanks to JSX, HTML is now a first-class citizen; it can be assigned to variables and returned from functions.

JSX offers many advantages. The view and logic, typically separated in different files due to different technologies, are now reunited. Everything about the component is housed in the same file and is compiled with compilation checks. Also, there are no new attributes to learn — like ng-something in Angular or v-something in Vue — just plain old HTML and {} to insert JavaScript.

The strength of component-based architectures lies in composition. A component can use other components to render itself, ending up in a hierarchy of components, just like a DOM hierarchy, but with less noise and more semantics.

A page described as a hierarchy of components.

React is a library rather than a framework. Compared to Angular for example, which admittedly is very complete, but is also rather opinionated about how to structure your app, React is very flexible and doesn’t impose anything.

To get started quickly, Facebook maintains create-react-app. It offers a default build configuration including Webpack, Babel, etc. and doesn’t clutter up our project. If needed, there are different ways to customize it, like eject or react-app-rewired.

Redux

As we’ve seen, React offers a good way to separate the app into small components, but it doesn’t give a lot of guidance on how to make these components communicate.

A component can easily pass down properties to its own children in the hierarchy tree, and these properties can be callbacks to communicate information up to the ancestors. However, communicating with neighbors tends to be complicated, involving a common ancestor, making it hard to follow and debug.

Communicating with neighbors using only React makes it hard to follow.

This case is quite trivial, but already adds a lot of noise. This will accumulate with each new feature and eventually make the app a big mess.

Inspired by Flux, Elm, and CQRS/ES which we follow on the backend side as well, Redux is the most common way to manage the state of a React application. Described as a predictable state container, it promises to keep the application state in one place and provide a safe and consistent way to update it.

The application state is kept in the Redux store as a plain old JavaScript object. Anywhere in the app, we can access and subscribe for changes in the store. To update the state, we dispatch actions and implement reducers.

With each component bound to the Redux store, we don’t need to manually notify and update each component anymore.

With React and Redux in place, we now have small and reusable components with clear responsibilities and a very decoupled way to communicate between them.

Styled-components

We still have one major concern: how do we manage the style of our application? On a large project, this task can be tedious; we want our CSS code to be modular and reusable, and we don’t want any conflicts between our style rules. Classic solutions would be to use some CSS pre-processors like Sass or Less and to follow some naming conventions like BEM or SMACSS to prevent class name collisions. That said, in the React ecosystem, other solutions emerged to deal with those problems, namely Radium, Aphrodite, Glamorous, as well as the most popular, styled-components. We decided the latter was a good candidate for us.

The name of this library is quite self-explanatory. It allows the creation of components that possess their own encapsulated style. This removes the need to map styles and components together. In other words, we don’t need to define class names anymore!

Styled-components is based on Tagged Template Literals, a new feature of ES6. This allows us to use real CSS syntax in a JS file to define our component’s style while keeping the ability to write JS within this CSS code if needed.

Components’ props can be used as style modifiers.

We can extend existing styled-components using extend() or convert any regular component using styled(MyComponent). Our JSX code can now consist solely of components like <MyComponent>, instead of using <div> with class names everywhere, which makes it easier to read.

Since styled-components are created using JavaScript, it is very easy to create reusable style mixins, and there is a long list of libraries that can provide valuable helpers.

Now that the whole component, including its style, is bundled in a single JS file, it’s easier to grasp, and also easier to share.

Shared library

Our designer works on Sketch which has its own notion of components called symbols. Once the symbol is designed, it can be reused in multiple places with different parameters.

Sketch symbols on the left, Sketch artboard using those symbols on the right.

We wanted to have the same notion of a ‘components library’ in our code. They needed to be easily browsable and documented in a single place. Also, we have two codebases (back-office and front-office), and we need a way to reuse the components in both places.

First, we extracted some of our components in a library and deployed it to NPM. Then, we looked for ways to make those components browsable directly in the browser.

There are two popular libraries to help with that, styleguidist and storybook. Styleguidist offers more flexibility by letting us write the documentation exactly the way we want in markdown. Snippets in this markdown are turned into live interactive views of our components:

The markdown code on the left, the generated documentation on the right.

Note that when we edit the code displayed in the browser, the live view updates itself automatically.

By carefully documenting all our shared components, we lay the groundwork for all future developments.

Speaking of the future, it would be even better to generate this shared library based on the design. A few weeks ago at ReactEurope, Airbnb announced Lona Studio, a component-oriented design tool. It directly exports code in multiple languages and frameworks, including React. This is very exciting, but also very experimental at the moment.

Conclusion

React with JavaScript/Flow seems like a safe bet in 2018. It allows us to build our apps with small reusable components in a language guaranteed to be supported for a very long time. These components are entirely self-contained using styled-components and only communicate with each other using props and redux. We’re building for the future by encapsulating all common components in a self-documented, carefully-tested shared library, in sync with the initial designs on Sketch, using styleguidist.

All of this combined allows us to keep a maintainable code base, be productive and remain invested in a strong development community.

PS: We are hiring!