Your code works — but that doesn't mean it's good
\n\nYou wrote a script. It runs. Clients are happy. But a month passes, and you look in horror at that same function that has grown to 200 lines. It mixes API requests, DOM manipulations, and some kind of date magic. Sound familiar?
\n\nWelcome to the world where refactoring JavaScript code is not a perfectionist's whim, but a vital necessity. If you want your project to live long and not fall apart at the first edit, this article is for you.
\n\nWhat refactoring is and what it is not
\n\nLet's start with the main point. Refactoring JavaScript code is the process of changing the internal structure of a program without changing its external behavior. You are not adding new features, not fixing bugs. You are simply making the code cleaner, clearer, and easier to maintain.
\n\nMany people confuse refactoring with rewriting. These are different things. Rewriting is when you throw away the old code and write new code from scratch. Refactoring is evolution, not revolution. You change the code in small steps, each time checking that nothing is broken.
\n\nWhen should you refactor JavaScript?
\n\nHere are a few sure signs that your code is crying for help:
\n\n- \n
- The Rule of Three. If you copy the same piece of code for the third time — it's time to extract it into a separate function. \n
- The Monster Function. A function longer than 30–40 lines that is impossible to read at a glance. \n
- "Magic Numbers". Numbers like
86400000or42scattered throughout the code without comments or constants. \n - Global Variables. If you are afraid to change a variable because you don't know where else it is used. \n
- Tests. If you are afraid to make changes because there are no automated tests — that's the first warning sign. \n
But there are also situations where refactoring is not needed: before a deadline, in production without tests, or when the code is hastily written for a prototype that will be thrown away tomorrow.
\n\nJavaScript Refactoring Techniques: From Simple to Complex
\n\n1. Extract Function
\n\nThe most popular and safe technique. You find a fragment of code that can be grouped and extract it into a separate function.
\n\n// BEFORE refactoring\nfunction calculateInvoice(items) {\n let total = 0;\n for (let item of items) {\n total += item.price * item.quantity;\n }\n let discount = 0;\n if (total > 1000) {\n discount = total * 0.1;\n }\n return total - discount;\n}\n\n// AFTER refactoring\nfunction calculateSubtotal(items) {\n return items.reduce((sum, item) => sum + item.price * item.quantity, 0);\n}\n\nfunction calculateDiscount(total) {\n return total > 1000 ? total * 0.1 : 0;\n}\n\nfunction calculateInvoice(items) {\n const subtotal = calculateSubtotal(items);\n const discount = calculateDiscount(subtotal);\n return subtotal - discount;\n}\n\n\nWhat changed? Now each function is responsible for one action. You can reuse calculateSubtotal elsewhere, and calculateDiscount is easy to test separately.
2. Replace Magic Numbers with Constants
\n\nNever use "bare" numbers in code. In a month, you yourself won't remember what 86400000 means.
// BAD\nif (Date.now() - lastLogin > 86400000) {\n // request re-login\n}\n\n// GOOD\nconst MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;\nconst ONE_DAY_MS = 86400000; // or just a readable constant\n\nif (Date.now() - lastLogin > ONE_DAY_MS) {\n // request re-login\n}\n\n\n3. Simplify Conditional Constructs
\n\nComplex nested if-else statements are the main enemy of readability. Use early return and ternary operators.
<