Ralfiz Academy
Free lesson · JS 0.2

Set up like a professional

A professional JavaScript setup has four parts: an editor ( VS Code ), a runtime ( Node.js LTS, ideally installed with a version manager such as nvm or fnm), the browser DevTools, and a few project…

About 50 minutesDeveloper courseStart here

At a glance

12 min read

A professional JavaScript setup has four parts: an editor (VS Code), a runtime (Node.js LTS, ideally installed with a version manager such as nvm or fnm), the browser DevTools, and a few project tools: npm, which comes with Node, plus Prettier and ESLint, which you install with npm.

You create a project with npm init, which writes a package.json. Prettier formats your code the same way every time, and ESLint finds likely bugs such as unused or undefined variables before you run anything.

You will also learn to read the console and a stack trace, set breakpoints in the Sources panel, and keep your work in git.

In plain words

A carpenter does not start with wood; they start with a workbench and sharp tools. For a JavaScript developer the workbench is small and free: a code editor, the Node.js runtime to run files from the terminal, the browser DevTools to inspect and debug pages, and a couple of project tools that keep code clean.

This lesson sets up exactly what a professional team uses, so every later lab runs the same way on your machine as it does in a real job.

Why it matters

  • Consistency. When everyone uses the same Node.js version, formatter and linter, “works on my machine” problems mostly disappear.
  • Speed. Format on save, inline lint warnings and breakpoints save hours compared with hunting bugs by reading code.
  • Credibility. A take-home test with a package.json, clear scripts, a lint config and a clean git history tells a reviewer you already work like a team member.

How it works

The editor: VS Code

Visual Studio Code is the most common JavaScript editor. It is free, fast and understands JavaScript and TypeScript out of the box: autocomplete, “go to definition”, rename symbol (F2) and an integrated terminal (Terminal menu, New Terminal). Add a few extensions:

ExtensionWhat it does
ESLintShows lint problems as squiggles while you type.
Prettier - Code formatterFormats the file on save using the project’s Prettier settings.
Live Server (for the DOM course)Serves a folder and reloads the browser when you save.
Error Lens (optional)Prints errors and warnings at the end of the line.

Then turn on formatting on save in your user settings:

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.tabSize": 2
}

The runtime: Node.js LTS and a version manager

Node.js runs JavaScript outside the browser. Releases marked LTS (Long-Term Support) are stable and patched for a long time, so always install the version marked LTS on nodejs.org. This course needs Node.js 22 or newer.

You can use the installer, but professionals prefer a version manager, because different projects pin different versions:

# macOS / Linux with nvm
nvm install --lts
nvm use --lts

# Any OS with fnm (Windows too; nvm-windows is another option)
fnm install --lts
fnm use lts-latest

# Check what you have
node -v
npm -v

Run a file with node index.js. While you work, node --watch index.js reruns it every time you save. Typing just node opens the REPL, an interactive prompt that is handy for trying one line (Ctrl+D exits).

The browser console and DevTools

Every browser has DevTools. Open them with F12, or jump straight to the Console with Ctrl+Shift+J (Cmd+Option+J on a Mac) in Chrome and Edge. The Console both shows your logs and runs any expression you type, so it doubles as a scratchpad for the current page.

The console object has more than log. Log levels let you filter noise, table turns arrays of objects into a grid, group nests related lines, and time/timeEnd measures how long something took.

Try it: A console toolkit

JavaScript

console.log('log: normal output');
console.info('info: something worth knowing');
console.warn('warn: stock is low for Notebook');
console.error('error: payment failed (this one is on purpose)');

const cart = [
  { item: 'Notebook', price: 120, qty: 3 },
  { item: 'Gel pen', price: 25, qty: 4 },
];
console.table(cart);

console.group('Order R-1042');
console.log('Customer: Asha');
console.log('Items:', cart.length);
console.groupEnd();

console.time('build report');
const lines = cart.map((row) => `${row.item}: ${row.price * row.qty}`);
console.timeEnd('build report');
console.log(lines);

console.count('render');
console.count('render');
console.assert(cart.length === 3, 'Expected 3 items in the cart');

Different log levels, a table, a group and a timer. The red lines are on purpose: console.error and a failing console.assert.

A small habit makes console.log debugging much faster: wrap variables in braces. console.log({ subtotal, discount }) prints the names next to the values, so you never wonder which number is which.

Try it: Log with labels, not guesses

JavaScript

const subtotal = 460;
const discount = 46;
const gstRate = 0.18;
const total = (subtotal - discount) * (1 + gstRate);

// Hard to read: which number is which?
console.log(subtotal, discount, total);

// Better: log an object built from the variables
console.log({ subtotal, discount, total });

// Even better for a final answer: a clear sentence
console.log(`Total to pay: ${total.toFixed(2)}`);

Wrapping variables in { } prints their names with their values. It is the fastest way to debug with console.log.

Reading a stack trace

When code throws, the console shows an error type, a message and a stack trace: the chain of function calls that led to the crash, newest first, each with a file and line number.

Uncaught TypeError: Cannot read properties of undefined (reading 'rate')
    at applyCoupon (app.js:15:36)
    at app.js:19:28

Read it in three steps. First, the type (TypeError: you used a value in a way its type does not allow). Second, the message (something was undefined and we tried to read .rate on it). Third, the top frame (line 15 in applyCoupon). Then ask “why was it undefined?” and follow the next frame to the caller, which forgot to pass a coupon.

Try it: Reading a stack trace

JavaScript

const order = {
  id: 'R-1042',
  items: [{ name: 'Notebook', price: 120, qty: 3 }],
};

function lineTotal(item) {
  return item.price * item.qty;
}

function orderTotal(order) {
  return order.items.reduce((sum, item) => sum + lineTotal(item), 0);
}

function applyCoupon(total, coupon) {
  return total - total * coupon.rate; // coupon is undefined here
}

console.log('Subtotal:', orderTotal(order));
console.log('After coupon:', applyCoupon(orderTotal(order)));
console.log('This line never runs');

The error names the problem and the property. In DevTools (F12) the stack trace also lists applyCoupon, the function that crashed, then the line that called it.

Breakpoints in the Sources panel

Logging tells you what happened; a debugger lets you stop time. In the Sources panel, open your file and click a line number to set a breakpoint. When the code reaches it, execution pauses before that line runs. You can then:

  • hover any variable, or read the Scope pane for all local values;
  • see how you got here in the Call Stack pane;
  • step over (F10), step into (F11) or step out (Shift+F11) of functions;
  • right-click a line number for a conditional breakpoint (pause only when qty > 100) or a logpoint (log without editing the code).

Writing debugger; in your code does the same as a breakpoint, but only while DevTools is open. Remove it before committing. For Node.js, open a JavaScript Debug Terminal in VS Code and run your file there; breakpoints set in the editor then work too.

Your project: npm and package.json

npm comes with Node.js. npm init -y creates a package.json, the project’s manifest. Two fields matter from day one: "type": "module", which makes .js files use modern import/export, and "scripts", named commands you run with npm run <name> (npm start and npm test need no run).

{
  "name": "ralfiz-setup",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node index.js",
    "dev": "node --watch index.js",
    "lint": "eslint .",
    "format": "prettier . --write"
  },
  "devDependencies": {
    "@eslint/js": "^9.0.0",
    "eslint": "^9.0.0",
    "globals": "^16.0.0",
    "prettier": "3.6.2"
  }
}

npm install --save-dev puts tools in devDependencies (needed to build and check the code, not to run it) and downloads them into node_modules. The generated package-lock.json records exact versions so every machine installs the same thing; commit it. npx runs a tool from node_modules without a global install. Lesson 6.2 covers versions and lockfiles in depth.

Prettier and ESLint

Prettier is a formatter: it reprints your code with consistent spacing, quotes and line breaks, and ends arguments about style. ESLint is a linter: it reads code without running it and reports likely bugs. They do different jobs:

PrettierESLint
AnswersHow should this look?Is this probably wrong?
ExampleAdds semicolons, uses single quotes“total is assigned but never used”
Config file.prettierrceslint.config.js (flat config)
Runnpx prettier . --writenpx eslint .

ESLint 9 uses a single eslint.config.js file that exports an array of config objects. The easiest start is npm init @eslint/config@latest, which asks a few questions and writes it for you. A minimal Node.js version looks like this:

import js from '@eslint/js';
import globals from 'globals';

export default [
  js.configs.recommended,
  {
    languageOptions: { globals: globals.node },
    rules: { eqeqeq: 'error', 'prefer-const': 'warn' },
  },
];

js.configs.recommended turns on rules such as no-unused-vars and no-undef. The globals line tells ESLint that names like process exist, so it does not flag them as undefined.

Try it: Where is my code running?

JavaScript

const inBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
const inNode = typeof process !== 'undefined' && Boolean(process.versions?.node);

console.log({ inBrowser, inNode });
console.log('globalThis is window here:', globalThis === window);
console.log('Browser language:', navigator.language);

// In Node.js you would see these instead:
// process.version  -> 'v22.x.x'
// process.platform -> 'win32', 'darwin' or 'linux'
// process.cwd()    -> the folder you ran node from

The same file can run in a browser or in Node.js. Feature checks with typeof are safe even when a name does not exist.

Folder conventions and git

Keep every project in its own folder with a predictable layout. For the labs in this course:

ralfiz-setup/
  .gitignore          node_modules, .env, dist
  .prettierrc
  eslint.config.js
  package.json
  package-lock.json
  README.md           what it is, how to run it
  index.js            or src/ for bigger projects

Use git from the first day: git init, a .gitignore containing node_modules, then small commits with clear messages (git add . and git commit -m "Add order formatter"). Push to GitHub to build a public portfolio.

Example: a professional starter project

Here is the whole setup for the lab, in the order you would do it:

mkdir ralfiz-setup && cd ralfiz-setup
npm init -y
npm pkg set type=module scripts.start="node index.js"
npm install --save-dev --save-exact prettier
echo '{ "singleQuote": true }' > .prettierrc
npm init @eslint/config@latest
git init
echo "node_modules" > .gitignore
npm start

The program itself reads from the host and formats data, so it touches both Node.js APIs and plain language features:

function printEnvironment() {
  console.log('Node.js:', process.version);
  console.log('Platform:', process.platform);
  console.log('Folder:', process.cwd());
}

function formatOrder(order) {
  const total = order.price * order.qty;
  return `${order.id}: ${order.qty} x ${order.item} = ${total}`;
}

printEnvironment();
console.log(formatOrder({ id: 'R-1', item: 'Notebook', price: 120, qty: 3 }));

Run npm start to see the output, npx eslint . to check it and npx prettier . --write to format it. If ESLint reports nothing and Prettier changes nothing, the project is clean.

Common mistakes

1. Installing tools globally. npm install -g eslint ties the project to whatever version happens to be on your machine.

# Wrong: global install, different on every machine
npm install -g prettier

# Fix: a dev dependency, run with npx or an npm script
npm install --save-dev --save-exact prettier
npx prettier . --write

2. Committing node_modules. It is huge and rebuilt by npm install. Add it to .gitignore before the first commit.

3. Mixing module systems. Using import without "type": "module" in package.json (or a .mjs file) leaves Node.js guessing. Node.js 22 detects the import syntax and reruns the file as a module, but it does extra work and, inside a package without the field, prints a warning; older versions stop with a syntax error about import statements, and tools such as ESLint and bundlers may treat the file differently. Add the field, or use the .mjs extension.

4. Leaving debug code behind. Stray console.log and debugger lines in a pull request look careless. Many teams add the ESLint rule no-console as a warning for front-end code.

5. Ignoring the first error. One mistake often causes several errors. Fix the first one in the list (or the top of the stack trace) and run again.

Interview and real-world notes

  • Be ready to explain the difference between dependencies and devDependencies, and why the lockfile is committed.
  • Say that you debug with breakpoints as well as logs. Mention conditional breakpoints and the Network panel for API problems; interviewers like concrete tools.
  • Many companies pin the Node.js version in a .nvmrc or the engines field of package.json. Check it before running a new codebase.
  • Continuous integration (CI) usually runs npm ci, lint and tests on every push. If lint fails locally, it will fail in CI too, so run it before you push.

Key terms

Node.js LTS
The Long-Term Support release line of Node.js: stable, patched for security for a long time, and the right choice for learning and production.
Version manager
A tool such as nvm, nvm-windows or fnm that installs several Node.js versions side by side and switches between them per project.
npm
The package manager that ships with Node.js. It creates package.json, installs packages into node_modules and runs scripts.
package.json
The project’s manifest: name, version, scripts, dependencies and settings such as "type": "module".
Prettier
An opinionated code formatter. It rewrites spacing, quotes and line breaks so the whole team’s code looks the same.
ESLint
A linter: it analyses code without running it and reports likely bugs and bad patterns, such as unused variables.
DevTools
The developer tools built into every browser: Console, Elements, Sources (debugger), Network and more. Open them with F12.
TipCommit a package-lock.json, an eslint.config.js and a Prettier config with every project: a teammate (or a reviewer of your take-home test) can then install and check your code with the exact same tools.
Hands-on lab9 steps · 5 exercise files
Quiz4 exam-style questions with explanations
Practice tests30 and 50-question mocks, scored out of 1000

The lab, quiz and practice tests for this lesson are for enrolled students, with progress saved to your own login.