Best Practices and Common Mistakes
Code that works today and code you can still understand next year are two different achievements. This lesson is about the second one.
Look at how far you've come. You can store and shape data, make decisions, build and change the page, react to people, load data from servers, and remember things between visits. That's the whole foundation of front-end JavaScript.
As in the HTML and CSS tracks, the last stretch before the final project is about doing it well. JavaScript is even more forgiving than HTML: code full of quiet problems can still appear to work, right up until the day it doesn't. These habits are what keep that day from coming.
Habits for readable code
const by default, let when it changes, never var. When someone reads const, they know that value stays put. That's one less thing to track in their head. (Variables, Scope and Closures.)
Names that explain themselves. const loavesLeft = 3 beats const x = 3. Functions get verbs (calculateTotal, showMenu); booleans read like yes-or-no questions (isOpen, hasDiscount). If you need a comment to explain what a variable holds, rename the variable instead.
Small functions that do one job. If a function is longer than your screen, or its name needs an "and" in it, split it. Small functions are easier to read, test, and reuse. (Functions.)
Early returns instead of deep nesting.
// Hard to follow: the real work is buried three levels deep
function addToCart(product) {
if (product) {
if (product.stock > 0) {
if (!cart.includes(product)) {
cart.push(product);
}
}
}
}
// Flat: handle the "no" cases first, then do the work
function addToCart(product) {
if (!product) return;
if (product.stock === 0) return;
if (cart.includes(product)) return;
cart.push(product);
}Comment the why, not the what. // add 1 to count above count += 1 tells nobody anything. // The API counts pages from 1, not 0 saves someone an afternoon.
Keep the three languages apart. JavaScript switches classes and attributes; CSS decides what they look like; HTML holds the content and structure. Avoid element.style for anything a class could do. (Where CSS Meets JavaScript.)
Habits for safe code
===, always. The loose == "helps" in ways that hide bugs. (Operators and Comparisons.)
Convert on purpose. Form fields, dataset, and storage all give you strings. Call Number() before doing math. (Numbers, Strings, and Booleans.)
Format for people with Intl. Intl.NumberFormat turns 1234.5 into $1,234.50 (or 1.234,50 €, depending on the country), and Intl.DateTimeFormat does the same for dates. It's more reliable than gluing symbols onto toFixed results. (Functions.)
textContent for anything from outside. Visitor input and server data never go into innerHTML. (Changing Text, Attributes, and Classes.)
Handle errors at the edges, and never swallow them. Wrap parsing, network calls, and storage in try/catch, and always do something useful in the catch. (Errors and Debugging.)
The browser is the receptionist, not the security guard. Anything in front-end code can be read and changed by visitors. Real checks and real secrets live on the server. (Forms and Custom Validation, Loading Data with fetch.)
Habits for pages people can use
Real buttons for actions, real links for navigation. A <button> works with the keyboard for free; a clickable <div> doesn't. A link goes somewhere; a button does something. (Events.)
Keep accessible state in sync. When JavaScript opens something, update aria-expanded. When it shows a message, use aria-live or role="status" so screen readers announce it. When a dialog or error appears, move focus to where the person needs to be.
Respect motion preferences in JavaScript too. CSS media queries don't cover animations started from JavaScript. Check window.matchMedia('(prefers-reduced-motion: reduce)').matches before starting one.
Progressive enhancement. Build the page so its content and basic links work even if the JavaScript fails to load on a flaky connection, then layer the interactivity on top, the same idea from Where HTML Meets CSS and JS.
Don't block the cook. Long loops freeze the whole page. Slow work, like loading data, should be awaited, never waited on in a loop. (Timers and the Event Loop.)
The mistakes everyone makes
Here's a gallery of the bugs almost every beginner writes at least once, with the fix. If you recognize a few, welcome to the club.
| Mistake | What you see | The fix |
|---|---|---|
if (total = 0) | The condition is always false, and total gets set to 0 | === for comparing, = only for assigning |
'2' + 3 from a form field | '23' instead of 5 | Number(field.value) |
(x) => { x * 2 } | undefined | Add return, or drop the braces |
A missing await | Promise {...} instead of data | await the promise inside an async function |
| A selector typo, or a script running too early | Cannot read properties of null | Check the selector; use defer |
quantity || 1 when 0 is a valid answer | 0 becomes 1 | quantity ?? 1 |
| Adding a listener inside another listener | The action runs twice, then three times... | Add each listener once, outside |
const copy = original for an array | Changing the copy changes the original | [...original] or structuredClone |
innerHTML with visitor input | A security hole (XSS) | textContent, or createElement |
setInterval with no clearInterval | Timers that pile up and never stop | Keep the id and clear it |
Passing object.method as a callback | this is lost | () => object.method() |
fetch without checking response.ok | A 404 page treated as real data | if (!response.ok) throw ... |
Tools that catch mistakes for you
You don't have to spot every problem by eye. Professional teams let tools do the boring checking:
- A formatter, most often Prettier, rewrites your code with consistent spacing and line breaks every time you save. No more debates about style.
- A linter, most often ESLint, reads your code and warns you about likely bugs:
==instead of===, variables you never use, aletthat should beconst, a missingawait. Many of the mistakes in the table above can be caught this way before you even run the code. - Your editor, set up with both, underlines problems as you type. Extensions & Customization covers adding them.
Try it: bug hunt
This little order form "works." Nothing turns red in the console. But it's full of the mistakes from this lesson. How many can you find and fix?
HTML
JavaScript
Preview
Console
Nothing logged yet. Anything you pass to console.log() shows up here.
Try it yourself
Fix every problem you can find. Here's the checklist, so you know when you're done (try first, then peek):
- Every
varshould beconstorlet. Which ones need to belet? xis a meaningless name. Rename it to say what it holds.- Both comparisons use
==or!=. Make them strict, and compare against real numbers instead of strings. priceandqtyare strings. Convert them withNumber().- A 0% discount becomes 10%, because
||treats0as falsy. Use??, or check the value properly. - Both
innerHTMLlines display text the visitor typed. Switch totextContent. (Try typing<b>bold</b>as the item name before and after.) - The nested
ifinsideelsecan be flattened with an earlyreturn. - Money should always show two decimal places, like
$14.40, not$14.4. Format the total withtoFixed(2), or better, withIntl.NumberFormat.
If you found all eight, you're reviewing code like a professional. If you found four, that's four more than you'd have spotted when this track began.
Check your understanding
Up next
There's one thing left to do: take the profile page you built in HTML and styled in CSS, and bring it to life. Head to Final Project: Bring Your Profile Page to Life.
