In Modernizing Force we discussed some of the tools we've been working with to modernize's development environment, from introducing Babel and React to the creation of @artsy/stitch. Increasing overall development speed was another aim, and to that end we released @artsy/express-reloadable which automatically hot-swaps Express.js code without the restart. In this post I'd like to cover some of the issues we've faced since then, and in particular our solution to library code-sharing in Express apps.

It's common to share NPM packages across projects, and oftentimes packages are developed in parallel. Package A depends on B, but B has a bug and you don't want to have to republish (and reinstall) the package in order to see changes made locally. yarn link (or npm link) was developed for instances like this and while it works great for stop and start processes where boot time is quick, it falls short if the development environment takes a while to load. In UI-rich environments like Positron (our Publishing CMS called "Writer") and Force, each boot would come at a high time-cost due to upfront compilation of assets. Tools like nodemon would automatically stop and start our server process when assets changed but that still didn't alleviate slow iteration times.

To recap from a previous post, @artsy/express-reloadable allows devs to immediately see changes to running Express.js app code:

import express from 'express'
import { createReloadable, isDevelopment } from '@artsy/express-reloadable'

const app = express()

if (isDevelopment) {
  const mountAndReload = createReloadable(app, require)

Changes made within the api folder are detected and instantly hot-swapped in, and all that's required is a new http request; this is down from an average dev cycle of about 40 seconds for However, we found an exception while building out Artsy's new editorial pages, which involved sharing React components from our UI library Reaction between Positron and Force. Even though we ran yarn link @artsy/reaction in each consumer app, changes would not be detected and so we had to do a full restart.

To address this, a new watchModules feature was added:

mountAndReload('./api', {
  watchModules: [

Similar to how files in api/ are hot-swapped in on change, symlinked NPM modules placed in the watchModules array will now be reloaded, too.


Categories: dx, express, express-reloadable, node