Lesson 11 of 54

Functions That Do One Thing

Function Length and Parameter Count

How long should a function be? How many parameters should it take?

The unsatisfying answer: "It depends." But I can give you useful guidelines.

Function Length

The Research

Studies in Code Complete found that routines up to 200 lines had the fewest defects per line of code. But "up to 200 lines" doesn't mean "200 lines is good."

The same research found that the sweet spot for comprehension is much shorter. Humans can hold about 7 ± 2 things in working memory. A 200-line function overwhelms that capacity.

Practical Guidelines

LengthAssessment
1-5 linesOften ideal. Functions this short are easy to understand at a glance.
5-15 linesThe sweet spot. Complex enough to be worth extracting, short enough to comprehend.
15-30 linesReview carefully. Can it be split? Is every line necessary?
30-50 linesYellow flag. Almost certainly doing too much.
50+ linesRed flag. Split it. There are rare exceptions (state machines, parsers), but they're rare.

When Long Functions Are Acceptable

Some patterns genuinely require length:

  • State machines with many cases
  • Factory functions with many construction paths
  • Parsers handling many token types
  • Event handlers responding to many event types

Even then, look for extraction opportunities. A 100-line switch statement can often become a lookup table and 10 small functions.

The Ideal Function

The ideal function:

  1. Fits on one screen without scrolling
  2. Can be understood without scrolling
  3. Has a name that describes what it does
  4. Doesn't surprise you with hidden complexity

Uncle Bob Martin advocates for functions as short as 4-5 lines. That's aspirational—many codebases won't achieve it. But pushing toward shorter is generally right.

Parameter Count

The Zero Parameter Ideal

The best functions take no arguments:

Javascript
function getCurrentUser() { }
function generateId() { }
function now() { }

Zero parameters means:

  • No input combinations to test
  • No confusion about what to pass
  • Maximum simplicity

One Parameter (Unary)

Single-parameter functions are common and clear:

Javascript
function parseJson(input) { }
function validateEmail(email) { }
function fileExists(path) { }

The parameter tells you what the function operates on.

Two Parameters (Binary)

Two parameters are manageable but start to require thought:

Javascript
function copyFile(source, destination) { }
function assertEquals(expected, actual) { }
function pow(base, exponent) { }

With two parameters, order matters. assertEquals(expected, actual) is convention; swap them and you confuse everyone.

Three Parameters (Ternary)

Three parameters are harder to understand and remember:

Javascript
function createRange(start, end, step) { }
function makeRgb(red, green, blue) { }
function between(value, min, max) { }

At three parameters, consider whether you should use an object.

More Than Three: Use an Object

Javascript
// Hard to read and easy to get wrong
createUser("Alice", "alice@example.com", "admin", true, false, 30);

// What do those booleans mean? What's the 30?

Better:

Javascript
createUser({
  name: "Alice",
  email: "alice@example.com",
  role: "admin",
  isActive: true,
  requiresPasswordChange: false,
  sessionTimeoutMinutes: 30
});

Now each value has a name. Order doesn't matter. Readers understand immediately.

The Boolean Flag Anti-Pattern

Boolean parameters are code smells:

Javascript
renderPage(data, true);

What does true mean? You have to read the function signature to know.

This is called a "flag argument." It means the function does different things based on a boolean, which usually means it does two things and should be two functions.

Before: Boolean Flag

Javascript
function fetchUser(id, includeProfile) {
  const user = db.users.find(id);
  if (includeProfile) {
    user.profile = db.profiles.find(id);
  }
  return user;
}

// Caller
const user = fetchUser(123, true);  // What does true mean?

After: Separate Functions

Javascript
function fetchUser(id) {
  return db.users.find(id);
}

function fetchUserWithProfile(id) {
  const user = fetchUser(id);
  user.profile = db.profiles.find(id);
  return user;
}

// Caller
const user = fetchUserWithProfile(123);  // Clear!

When Boolean Parameters Are Okay

Sometimes a boolean is genuinely the right choice:

Javascript
setVisible(true);
setEnabled(false);

These read naturally. The parameter name is clear from context.

But even then, consider:

Javascript
show();
hide();
enable();
disable();

These are even clearer.

Output Parameters Are Evil

Some functions modify their parameters:

Javascript
function addToList(list, item) {
  list.push(item);
}

const myList = [];
addToList(myList, "item");  // myList is modified!

This is confusing. The modification is a side effect hidden in the call.

Better:

Javascript
function withItem(list, item) {
  return [...list, item];
}

const myList = [];
const newList = withItem(myList, "item");  // myList unchanged, newList has item

Or if mutation is necessary, make it obvious:

Javascript
myList.push("item");  // Clearly mutating myList

The Parameter Object Pattern

When you have related parameters, group them:

Before

Javascript
function drawRectangle(x, y, width, height, color, borderWidth, borderColor) {
  // ...
}

After

Javascript
function drawRectangle(rect, style) {
  // ...
}

drawRectangle(
  { x: 10, y: 20, width: 100, height: 50 },
  { fill: 'blue', borderWidth: 2, borderColor: 'black' }
);

Benefits:

  • Parameters have names
  • Order doesn't matter
  • Easy to add optional parameters
  • Related data is grouped

Key insight: Short functions (5-15 lines) are easier to understand, test, and maintain. Fewer parameters (0-3) reduce cognitive load. Boolean flags are usually a sign the function does two things. When you have many parameters, use an object.