Frontend Development Environment as a Package

Written by penzington | Published 2017/05/03
Tech Story Tags: javascript | create-react-app | front-end-development | javascript-fatigue

TLDRvia the TL;DR App

Or when to build your own create-react-app

The growth of JavaScript tooling ecosystem in recent years has been astonishing. The number of available tools exploded and the complexity of a setup required to bootstrap a project which would be considered modern has skyrocketed_._ This, as well as the quick rate of churn of these tools contributed largely to the famous JavaScript fatigue. The frustration fuelled a number of projects that aim at abstracting away that complexity, usually in a form of zero-config development setups. Most notable examples are nwb and create-react-app but the list is growing.

These projects usually help developers in two ways:

  • Taking care of boilerplate app code This can take a form of simply providing a copy-pasted app skeleton but can get pretty elaborate with CLI scaffolding tools like yeoman.
  • Providing the development environment This means bundling a group of tools that compose a frontend dev environment. These include devevelopment server with live reloading, build pipeline with code bundling and compiling, testing setup, linting, typechecking etc.

I recently got interested in exploring the latter, letโ€™s call it Development Environment as a Package (itโ€™s a mouthful, so letโ€™s use ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ for short). The idea is pretty simple. Take all the infrastructure the sits around the code of your application and extract it into a single installable package that your project depends on. Just yarn add my-dev-env -D and youโ€™re done. While this seems plausible, letโ€™s first explore why it might not be a good idea.

Why you shouldnโ€™t useย ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ

Any time you extract a piece of your project or itโ€™s dependencies and build an abstraction around it, you introduce an indirection, place the extracted code in a black box. This indirection will inevitably need to be unboxed once the limitations or bugs in the extracted code start to leak. Breaking the abstraction to peak inside can get messy and frustrating. It is especially so, when the process involves complex tools with elaborate configurations, like in the case of modern frontend tooling (eslint, webpack, etc.).

Practical example Youโ€™re getting cryptic test runner errors for your unit tests suite. In a traditional setup you can debug by playing with the test runner configuration in the root of the project, but with ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ you will have to _yarn link_ the dev env package first or make the changes somewhere in the_node_modules_ directory.

Building wrappers around lower level tools leads to a loss of access to some of their features. This gets especially limiting when the tools introduce new features and your wrapper prevents you from easily accessing them.

Practical example A new version of webpack introduces optimisations to bundle size, provided you add a plugin to your configuration. With a traditional setup, you can simply upgrade webpack and paste the plugin configuration to your _webpack.config.js_ but with ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ you will need to go through the process of updating the external dependency (which may be 3rd party).

Lastly, most tools are optimised for usage directly in your project, and extracting them to a package can require a bit of hoops jumping and cringeworthy hacks. This adds new layers of complexity to your setup you wouldnโ€™t otherwise need.

While these are undeniable drawbacks of the ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ approach, they didnโ€™t prevent the projects like create-react-app from becoming very popular.

Why you should useย ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ

Some obvious wins include significant reduction in the time and effort required to start a new project as well as convenient upgrades of the toolchain, especially across multiple projects. Itโ€™s also much easier to focus on authoring code that adds real value to your enduser, when you donโ€™t have to spend hours trying to get different tools work together. Outsourcing the development environment setup can be a godsend for all developers that find the frontend ops tasks challenging, tedious or dull.

Practical example A frontend development environment consisting of development server, linting, testing and build pipeline can easily have 50 development dependencies and 10 configuration files. By using e.g. _create-react-app_ you will end up with one development dependency and no configuration files.

But the positive impact of using ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ goes beyond time saving and cleaner directory listing. In my experience, it can encourage a more decoupled architecture for large scale projects. By allowing effortless bootstrapping and streamlined updates, it helps making the decision to split codebases. In consequence, it also encourages continuous improvement through experimenting with new tools and approaches, which can be easily back-ported to older projects or discarded.

Practical example When starting to work on a new projects a team opts out of using the ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ used in all other projects in a the company. They do so in order to try Jest as the unit test runner instead of Mocha which is used in the current setup. After a while they make a decision to migrate to Jest fullyโ€Šโ€”โ€Šthey install Jest in their ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ, release a new version, upgrade it in all other projects and use codemods to migrate the tests.

From an even more holistic perspective, choosing the ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ approach instead of separate setups for each project can have positive effects beyond codebases. In companies with product teams working on multiple frontend projects, the introduction of ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ makes it easier for developers to contribute cross-team or switch teams and for projects to change ownership. It also reduces the overall time wasted on solving the same problems in a slightly different way by different teams.

Practical example At the company I work for, issuu, we have 5 product teams, each maintaining several frontend projects. A while ago, each of these projects had a similar, yet slightly different development environment. We started the migration to a ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ by first trying the approach in one of the teams when starting a new product. Today we have around 10 projects across 3 teams that use it, and plan to migrate more soon. We love how improvements implemented by one team can be used across all these project with no additional effort.

As mentioned, there is a wide selection of open source ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ solutions out there. However, Iโ€™ve decided to experiment with writing my own, which gave me some idea about pros and cons of this approach. Letโ€™s start with the cons.

Why you shouldnโ€™t write your ownย ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ

If the reason youโ€™re considering building a create-react-app clone is the NIH syndrome then it would probably be more beneficial for you and the community to spend your time contributing to one of the existing projects instead.

By sticking to the established and battle-tested solutions you and your team will avoid a range of problems that other developers has encountered and spent hours solving. You will also be able to easily keep up with the evolving community best standards without the crippling FOMO.

Practical example Some time ago it turned out that the way some of the ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ like _preach-cli_ handle development SSL certificates in an unsafe way. The vulnerability was quickly fixed by the communities behind the projects.

When making a decision to opt out of using one of the existing dev environments supported by community you should also take into consideration the size of the team and the number of projects involved. In my experience this approach makes most sense in an environment where a significant number of frontend projects are initiated and need to be maintained.

It should also be mentioned that writing a robust ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ is not an easy task, especially if you fancy advanced features like ejecting (i.e. an option to copy all the boilerplate into the project).

Regardless of these points, I found that an in-house solution worked well for my use case. Here is why.

Why you should write your ownย ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ

Composing your own development environment gives you the ability to fine tune it to the specific needs that you and your team might have. You donโ€™t have to make trade-offs that can sometimes be difficult or simply not acceptable. You can bring only the tools you actually need and configure them the way you prefer. Development setup is a very opinionated domain, so conforming to all decisions made by an off-the-shelf solution can be challenging.

Practical example A lot of developers refrained from choosing _create-react-app_ because of the lack of support for CSS preprocessors like Sass as well as server rendering of the markup.

Furthermore, since you are in charge, you can make quick iterations and fix issues and introduce features, without the need of community approval.

The experience and understanding of the tooling ecosystem gained while composing your own ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ is invaluable. Choosing the right tools and making them all play together as an installable package requires a deep understanding of their features, tradeoffs and configuration.

Practical example When choosing a JavaScript bundler for my ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ, I learned that rollup works great for libraries and widgets because of the clean output and easy module type control, while for products like web-apps webpack does an amazing job, thanks to e.g. robust code splitting.

Finally, when building an in-house ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ you can usually afford to focus on DX (Developer Experience) much more than for a one-off development setup, making the life of your colleagues or your own easier. You can optimise the most common tasks and workflows, and polish the CLIs with e.g. readable output and autocompletion.

Practical example This kind of care for DX is exemplified when running _create-react-app_ dev server on the port that is already used by another processโ€Šโ€”โ€Šit will automatically choose an alternative one. Sweet!

Example of a simpleย ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ

While the codebase for projects like create-react-app can be a bit overwhelming, building a simple ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ does not have to be difficult. Some time ago I gave a lightning talk on this subject at a CopenhagenJS meetup and to aid the talk Iโ€™ve published a simple example of a ๐Ÿ’ป๐Ÿž๐Ÿ“ฆ for authoring JavaScript libraries, with the code on GitHub. You can try it by running yarn add cphjs-inst-dev-env -D and then using one of the three available commands: yarn cphjs-tdd yarn cphjs-test and yarn cphjs-buildย . With this one development dependency you get bundling with Rollup with Babel compilation, support for Sass with SCSS syntax and importing from JS, linting with ESLint with Prettier auto-fixing, and a TDD setup with Jest for running tests on the compiled bundle.

Iโ€™ve used the development environment as a package approach for my side-projects (with create-react-app) as well as at work (with an in-house solution) for a while now. Iโ€™m quite happy with it, but Iโ€™m eager to hear about your opinion. What do you think?

Big thanks to Kenneth Skovhus and Mads Hartmann for reviewing this post.


Published by HackerNoon on 2017/05/03