Getting JavaScript running on your machine

Most people treat the setup process like it is a mysterious ritual that requires reading three separate documentation pages and installing six different tools before they can write a single line of code. It does not work like that. You need Node.js installed, a text editor that does not crash every time you save a file, and the willingness to look at an error message without immediately closing the terminal window. The actual sequence is shorter than most guides make it out to be, though the part where you configure your project correctly takes longer than it should for reasons that become obvious once you hit your first dependency conflict. I started by checking whether Node.js was already on the system. Running node --version in any terminal tells you everything you need to know within about two seconds. If you get a version number back, you are mostly fine. If you get a command not found error, you need to go to nodejs.org and grab the latest LTS release. The LTS builds are called that for a reason. They are the versions that do not introduce breaking changes between minor releases, which matters more than people realize when they are three months into a project and their build tool stops working because someone upgraded to a newer major version on a whim. After Node.js is installed, which usually takes about five minutes on a modern machine depending on your download speed, you need a code editor. VS Code is the default choice for most people, though that is partly because Microsoft bought into the JavaScript ecosystem aggressively and made sure their editor handles TypeScript, debugging, and package management without requiring additional plugins. Vim users will disagree with me, but they usually do not read articles like this anyway. The important part is that whatever editor you choose should have basic syntax highlighting and the ability to run terminal commands without opening a second window.

Once you have a terminal and an editor that do not conflict with each other, create a project directory and initialize it with npm init -y. The -y flag skips the interactive questions and generates a package.json with default values. That file is your project manifest. It tracks dependencies, scripts, and metadata. Do not delete it. Do not manually edit it with anything other than a text editor. People who try to edit JSON by hand without validation often introduce syntax errors that take twenty minutes to find because a single missing comma breaks the entire install process. Now you need a bundler. The ecosystem has moved past standalone build tools for most projects, though webpack and esbuild still have dedicated user bases. I have spent years maintaining webpack configurations, and I will tell you straight that the learning curve is unnecessarily steep for beginners. The configuration file alone can exceed two hundred lines for a moderately complex project, and half of those lines are boilerplate that gets copied from Stack Overflow without anyone understanding what each option actually does. For most people starting out, esbuild or Vite makes more sense because the setup time drops from roughly four hours of reading documentation to about fifteen minutes of copying a template configuration. Here is where I ran into a specific problem that took me two full days to resolve. I was setting up a Node.js project on a Windows machine using WSL2, and every time I tried to install a dependency with native compilation steps, like bcrypt or sharp, the build would fail with an error about Python not being found even though Python was installed. The issue was that WSL2 has its own filesystem separate from Windows, and the PATH variable inside WSL2 does not automatically include Windows installations unless you explicitly configure them. The workaround was to install Python inside WSL2 using sudo apt install python3 make build-essential, then set the NODE_PYTHON environment variable pointing to the WSL2 Python path instead of the Windows one. Without that step, any package requiring native compilation fails silently during install and then breaks at runtime in ways that are nearly impossible to debug if you do not know where to look.

After the bundler is installed and configured, which varies from project to project, you need to understand how the module system works in modern JavaScript. Node.js uses CommonJS by default with the require function, while most frontend build tools expect ES modules with import and export syntax. This creates a situation where your package.json needs a type field set to module if you want to use ES module syntax in a Node.js project, otherwise the runtime throws a TypeError that makes no sense until you look up what the difference between the two systems actually is. The error message says something like Cannot use import statement outside a module, which is technically accurate but not particularly helpful if you do not already know what a module boundary means in this context. You should also set up a development server if your project involves browser-side code. The built-in HTTP capabilities of Node.js can serve static files, but the dev server experience is noticeably worse than using something purpose-built. Vite's dev server starts in under 100 milliseconds for most projects and handles hot module replacement without requiring browser refresh, which saves probably ten to fifteen minutes of interrupted workflow per day depending on how frequently you change code. The tradeoff is that Vite requires a specific configuration pattern that does not match older bundler conventions, so migration from webpack projects is nontrivial and usually requires accepting that some configuration decisions from the old setup will not have direct equivalents. Environment variables are another area where people make unnecessary mistakes. The standard approach is to use a .env file alongside dotenv package, but dotenv loads variables from disk into process.env at runtime, which means those values exist only in the Node.js process and not in the browser. If you need frontend access to configuration values, you must explicitly expose them through the build tool, usually via a define option or a similar mechanism. Failing to do this results in runtime errors where undefined values appear in the browser console and someone spends an hour debugging why an API key is missing when the actual problem is that the build process never included it in the bundle in the first place.

TypeScript integration requires its own configuration file called tsconfig.json, which controls compiler behavior, target output formats, and type checking strictness. The default configuration is intentionally lenient, which means JavaScript files without type annotations pass through without errors even when they contain obvious logic mistakes. Setting strict to true in tsconfig.json catches many of these issues at compile time rather than at runtime, but it also increases the initial development speed penalty because every variable, parameter, and return value needs an explicit type annotation. For small projects this penalty is negligible, but for larger codebases without existing type coverage the migration time can range from several days to multiple weeks depending on how much legacy JavaScript exists. Testing setup depends heavily on what you are actually testing. Jest remains the most common choice for general purpose unit testing with about forty percent market share in JavaScript projects according to recent surveys, though Vitest has been gaining ground because it shares the same configuration syntax as Vite and runs significantly faster on large test suites. The installation is straightforward with npm install --save-dev jest or the Vitest equivalent, but configuring test coverage thresholds and CI integration takes considerably more time than the initial install and often requires reading the documentation twice before the options make sense. Linting with ESLint is optional but recommended after your first project exceeds about five hundred lines of code. Before that threshold, the time spent configuring rules and fixing style violations usually exceeds the benefit, but after that point inconsistent code patterns start causing real bugs that are difficult to find manually. The default ESLint config catches basic issues like unused variables and missing semicolons, though many teams configure additional plugins for React, Vue, or Node.js specific patterns that the base rules do not cover.

The deployment phase introduces a different set of concerns that most setup guides ignore entirely. Package-lock.json or npm-shrinkwrap.json files must be committed to version control because they pin exact dependency versions, preventing situations where a project builds successfully on one machine but fails on another due to transitive dependency updates. Skipping this step has caused production outages in projects I have worked on, usually involving a minor version bump in a deeply nested dependency that introduced an incompatible change without updating its own major version number. If you are building a project intended for production deployment, consider whether you actually need a bundler at all. Modern browsers support ES modules natively, which means you can sometimes ship source code directly with only a minification step rather than a full build pipeline. This approach reduces the complexity of your setup by approximately forty to fifty percent and eliminates entire categories of build-related bugs, though it requires that your target browser support list excludes Internet Explorer and older versions of Safari that lack module support. The complete setup process for a typical JavaScript project usually takes between forty-five minutes and two hours depending on experience level and project complexity. Beginners should expect the longer end of that range because they spend extra time troubleshooting configuration errors that experienced developers recognize immediately. The actual coding work begins only after the setup completes, which is why rushing through configuration steps often costs more time overall than doing it carefully the first time.