JavaScript Tooling and Orders of Magnitude

I was listening to a dev podcast recently (prooobably Front End Happy Hour, but don’t quote me on that). One of the hosts mentioned that they give out a take-home problem for candidates. The take-home problem was supposed to take several hours, but some candidates managed to burn up most of their time setting up their build toolchain instead of solving the problem.

This got me thinking about engineering judgment. How do you ensure that the time you invest in tooling pays off over the lifetime of the project? For me, it helps to think about introducing frontend tooling and infrastructure based on the size of the codebase:

1-10 lines of code: An ad-hoc script. No tooling. The script is probably inlined into the page. If there are any dependencies (probably not), the script accesses these as globals on window.

10-100 lines of code: A single script file. Still no tooling, other than the linter built into your editor. The script resides in a single unminified file, to be included using a single <script src>. Dependencies are still globals. The project could have:

  • some dependencies, still implemented as globals
  • a number of functions and objects, but all implemented in the same file, perhaps wrapped in an IIFE

100-1000 lines of code: A modular project. This is a major break point. Using a JavaScript framework is still overkill (unless the project is a framework-specific component or library), but the project likely consists of multiple files, and pulls in significant dependencies. Unit tests are a must. The project could have:

  • dependencies defined in a lock file
  • a straightforward build script that bundles the code from a single entrypoint and runs the results through a minifier
  • source maps
  • unit tests
  • a watch script to (at least) run the unit tests

A generator tool could be useful here to bootstrap the project. However, the generator tool should not assume a JavaScript framework. (I like create-react-app, but I would use it at the next stage, not here.)

1000-10000 lines of code: A real-life web application. The application is now large enough to get mileage out of a JavaScript framework. The project could have:

  • a variety of third-party components, used mostly as-is
  • a build system that includes continuous integration, if not continuous deployment
  • a notion of “development” vs. “production” builds
  • a test suite with coverage reports and additional types of tests, such as functional tests and smoke tests
  • significant “Getting Started” automation and documentation, to ensure new developers can become productive quickly
  • a consistent notion of how to manage state, perhaps backed by a simple state management library

10000-100000 lines of code: The web application, becoming its own ecosystem. The code is large enough that some of its dependencies and third-party components are starting to be phased out in favor of more custom code tailored to the project. The project could have:

  • internal components that are complex enough to be their own mini web applications
  • a growing toolkit of internal utility components, along with a style guide on how to use them
  • one or more “daughter” repos, representing code that once resided in the application, but are now robust enough to be re-used across projects
  • harder guarantees on build reproducibility, such as requiring all builds to be run in a container
  • more checks at build time: extensive lint rules, best-effort automated accessibility checks, minimum code coverage percentages
  • code compilation that goes beyond basic module bundling: this could include advanced ES features, a type system or compile-to-JS language

These are my own rough guidelines. What are yours?