---
title: "12 — Keep the App Running in Development --watch and Nodemon"
uid: keep-app-running-dev
tags: ["watch-mode", "dev-workflow", "nodejs", "nodemon", "roadmap:nodejs", "dx"]
excerpt: "Node loads code once and never reloads it — so dev velocity depends on a file watcher that restarts the process. As of Node 18.11, that watcher is built in."
date: 2026-08-13T03:27:55+0000
source: https://www.aveshina.my.id/en/blog/keep-app-running-dev
---

"Just save and re-run the command" was my dev-loop model, and the restart cost quietly ate my attention. The model that finally stuck is one fact and one consequence: **Node.js loads code once at startup and does not reload it, so the only way a code change takes effect is to restart the process.** That makes a file-watcher-that-restarts-for-me the single biggest quality-of-life tool in Node development, and as of recent versions that watcher ships with Node itself [1].

The framing that landed for me is "Node caches modules, so changes need a restart," then the two ways to get that restart for free.

## Why a watcher is necessary

Node.js evaluates a module the first time it is required or importd, then caches the result in memory. Subsequent imports return the cached object — they do not re-read the file. This caching is essential for performance and module identity, but it has a direct cost during development: after I edit a file, the running process is still executing the old code. Unlike a browser hot-reloading CSS, Node has no built-in mechanism to swap in new module code while the process runs.

The practical consequence is that, without tooling, every save means: switch to the terminal, kill the process, re-run the command. Multiply that by a hundred saves an hour during active development, and it is a serious drag. The fix is a process that watches the file system for changes and restarts the Node process automatically. Two tools do this job.

## Node's built-in --watch flag

As of Node 18.11.0 (stabilized in Node 19+), the --watch flag is built in [1][2]. It watches the entry file and its imported modules, and whenever one of them changes, it restarts the script — no external dependency required.

```
node --watch server.js
```

That is the whole interface. I edit a file, save, and the process restarts within milliseconds. Because it is built into Node, there is no npm install step, no separate config file, and no version drift between the watcher and the runtime. For new projects, this is now my default — the dev script in package.json is just node --watch server.js, and it works the same on every machine.

The flag has grown over versions — --watch-path to restrict which directories are watched, and integration with the inspector for keeping debug sessions across restarts. The Node docs are the source of truth for the current surface [2].

## Nodemon: the long-standing external tool

**Nodemon** is the external watcher that predates the built-in flag by over a decade, and it is still everywhere in existing projects [3][4]. It wraps the Node process, watches the file system, and restarts on change.

```
npx nodemon server.js
```

```
{
  "scripts": {
    "dev": "nodemon server.js"
  }
}
```

Nodemon's appeal over the raw flag is configuration — a nodemon.json file lets me specify which extensions to watch (js,json,ts), which directories to ignore (node_modules, dist), what command to run (a different runtime, a custom exec), and delay before restarting (to batch rapid saves). For a project with non-standard file types or a complex startup command, that configurability earns its keep.

The honest assessment today: for a plain Node.js project, the built-in --watch flag has absorbed most of what Nodemon was used for. Nodemon remains the right call when I need its config file or when a project's tooling (older Express apps, custom runners) is built around it. But starting fresh, --watch is one fewer dependency to maintain.

## What the watcher does (and does not) do

A watcher restarts the **process**. That means application state in memory is lost on every restart — in-memory caches reset, database connections re-open, long-running jobs are interrupted. This is fine for a dev server (I want fresh state on each change) but it is the reason a watcher is a development tool, not a production one. In production, restart-on-file-change is the wrong model entirely — the right tools there (PM2, process managers) keep the app alive across crashes, not across saves.

The watcher also does not, by itself, do *hot module replacement* — swapping a single module without losing state, the way some frontend bundlers do. It is a full process restart, every time. For a server, that is acceptable; for a UI, it would be too slow, which is why the frontend world built more sophisticated tooling on top.

## How I use this

The model I keep is "Node caches modules, so a code change needs a restart, and a watcher gives me that restart for free." For every new project, the dev script is node --watch <entry>, because it is built in and zero-config. For existing projects that already use Nodemon, I leave Nodemon in place — migrating gains nothing material. The one habit I keep regardless of tool: I configure the watcher to ignore node_modules, dist, and any generated output, because a generated file changing should not restart the dev server. Watching source files only keeps restarts meaningful. The way of thinking — process restart, not live reload — is what sets the right expectation for what the watcher does.

## References

[1] K. Esmail, "Built-in Node.js Watch Mode," Medium, 2023. [Online]. Available: [https://medium.com/@khaled.smq/built-in-nodejs-watch-mode-52ffadaec8a8](https://medium.com/@khaled.smq/built-in-nodejs-watch-mode-52ffadaec8a8)

[2] OpenJS Foundation, "Node.js CLI: --watch," Node.js API Docs. [Online]. Available: [https://nodejs.org/api/cli.html#--watch](https://nodejs.org/api/cli.html#--watch)

[3] "Nodemon," nodemon.io. [Online]. Available: [https://nodemon.io/](https://nodemon.io/)

[4] DigitalOcean, "How To Restart Your Node.js Apps Automatically with nodemon." [Online]. Available: [https://www.digitalocean.com/community/tutorials/workflow-nodemon](https://www.digitalocean.com/community/tutorials/workflow-nodemon)

```quiz
Q: Why does editing a Node.js source file not affect the already-running process?
- Node locks source files while running
- Node caches each module's evaluated result on first import and returns the cached object afterward, so the new file content is never re-read
correct: 1
explain: Module caching is essential for performance and identity, but it means the running process keeps executing the old code. A restart is required for changes to take effect.

Q: As of Node 18.11+, what is the simplest way to auto-restart on file change with no dependencies?
- npm install nodemon
- node --watch server.js
correct: 1
explain: The --watch flag is built into Node itself. Running node --watch server.js watches the entry file and its imports and restarts on change, with no external package.

Q: When a watcher restarts the process, what happens to in-memory application state?
- It is preserved across the restart
- It is lost — caches reset, connections re-open, the process starts fresh
correct: 1
explain: A watcher does a full process restart. All in-memory state is cleared, which is usually desirable in dev (fresh state per change) but unsuitable for production.

Q: Nodemon remains the better choice over the built-in --watch when…
- you want zero-config restarts
- you need its config file for non-standard extensions, ignore patterns, or a custom exec command
correct: 1
explain: Nodemon's nodemon.json allows fine-grained configuration (which extensions to watch, what to ignore, what command to run). For plain projects the built-in flag suffices; Nodemon earns its keep when configuration is non-trivial.
```
