Functions and thinking in small pieces
This is where programming starts to feel different from following steps. Instead of one long script that does everything top to bottom, you break a problem into small named pieces with clear inputs and outputs, then combine them. That habit is what separates code that can grow from code that has to be rewritten.
Why one long script stops working
A 30-line script is easy to hold in your head. A 300-line script is not. When everything lives in one block, a change in one place can quietly break something far below it, and the only way to understand any part is to read the whole thing.
Small functions fix this by giving each piece of work a boundary. Inside the boundary you only care about one job. Outside it, you only care about what goes in and what comes out. You can read, test, and fix one piece without re-reading everything else.
The goal is not "more functions" for its own sake. The goal is code where each part can be understood on its own.
Inputs in, output out
The most useful kind of function takes everything it needs as parameters and gives back a result with return. It does not reach out and change variables that live elsewhere.
function applyDiscount(price, percent) {
return price - price * (percent / 100);
}
const total = applyDiscount(80, 25); // 60
You can understand applyDiscount by reading four lines. You can check it by calling it with a few numbers and looking at the result. Compare that with a function that changes a global total variable: to know what it does, you also have to know everything else that touches total. Clear inputs and outputs make a function predictable, and predictable code is easy to trust and reuse.
Return a value, don't just print it
A very common beginner mix-up is treating console.log as if it were return. They are completely different. console.log shows something on the screen for a human. return hands a value back to the code that called the function, so that code can keep using it.
function square(n) {
console.log(n * n);
}
const result = square(3); // prints 9
console.log(result); // prints undefined
A function with no return gives back undefined. If other code needs the answer, return it. Log it separately while you are debugging, then remove the log. Functions that return values can be combined; functions that only print cannot.
Name it for what it does
A good function name tells the reader what they get without opening the function. removeInvalidEmails(list) says far more than process(data) or handleStuff().
A practical test: try to describe the function in one sentence. If the sentence needs "and" — "it validates the form and saves it and shows a message" — it is probably doing several jobs and should be split into several functions.
A few habits that help:
- Use verbs for functions that do something:
calculateTotal,formatDate. - Name parameters after what they hold:
prices, notarr. - If you copy and paste the same few lines with different values, turn them into one function and make the differing values parameters.
Build big things by combining small ones
Once each piece does one job, you can snap them together. Each function is simple, and the combination reads almost like a description of the task.
function subtotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
function addTax(amount, rate) {
return amount * (1 + rate);
}
function checkoutTotal(items, taxRate) {
return addTax(subtotal(items), taxRate);
}
If the total is wrong, you can call subtotal and addTax on their own to find which step is broken. If tax rules change, you edit one small function and nothing else. This is the real payoff: code that grows by adding pieces instead of by making one block longer.
What to do now
Take a script you have already written — any exercise longer than about 30 lines — and refactor it:
- List the separate steps it performs, in plain words.
- Turn each step into a function with parameters and a
returnvalue. - Give each function a name that passes the one-sentence test.
- Replace any copy-pasted blocks with a single function call.
- Call each function by itself with a couple of sample inputs to confirm it returns what you expect.
The program should behave exactly as before. The difference is that you can now explain each piece in one sentence, and change one without fear of breaking the rest.