A Function Name Is a Promise About a Relationship

Why a function that returns a hardcoded value technically qualifies but misses the point, and what a function's name is actually supposed to promise the reader.

September 3, 20263 min read4 / 10

Last time, the rule was: no return, no function. That rule catches a lot, but not everything. This one slips right through it:

JavaScript
function mystery(x, y) { return 40; }

It has a return. It takes two inputs. And it's still not a real function, for a reason the last rule can't catch.

Borrowing From Math

Every function you ever graphed in school, y = 2x + 3 and the rest, describes something specific: a relationship between an input and an output. Put in 1, get a y. Put in 2, get a different y. The whole curve is just that relationship, plotted point by point.

A function in code is the same idea with a different notation. f(x) and getShippingCost(weight) are doing the identical thing: describing what happens to a value on its way from input to output.

mystery(x, y) breaks that idea completely. Feed it 1 and 1. Feed it 900 and -4. Nothing changes. The output has no relationship to the input at all.

That's the real bar a function has to clear, and it's a step above just having a return statement.

Two flow diagrams: the top one shows an unnamed function always returning 40 no matter the input, the bottom one shows readingTime changing its output whenever the word count or reading speed changes ExpandTwo flow diagrams: the top one shows an unnamed function always returning 40 no matter the input, the bottom one shows readingTime changing its output whenever the word count or reading speed changes

What a Name Is Supposed to Tell You

Once a function's output genuinely depends on its input, the name gets to do real work: it can describe what that dependency is.

JavaScript
function readingTime(wordCount, wordsPerMinute) { return Math.ceil(wordCount / wordsPerMinute); }

You don't need to open this function to know what it does. The name already told you: give it a word count and a reading speed, and it hands back an estimate. That's the semantic relationship, stated up front, before a single line of the body runs.

1400 words, 200 wpminput
readingTime(...)the promise
7 minutesoutput

mystery could never earn a name like that. There's nothing about its behavior worth naming, because there's no real relationship to describe.

Most of Your Codebase Fails This Test

Here's the uncomfortable part. Open any large codebase and read through its function names. Most of them describe an action, not a relationship:

📧sendEmail
💾saveUser
📝logError
🔄updateCache

These are useful. They're also procedures, not functions, by this definition, because their names describe something they do, not something their output depends on. Compare that to readingTime above: its name told you the dependency before you read a single line.

That's not a problem to go fix everywhere. It's just worth noticing how rare an honestly-named function actually is once you start looking for the pattern on purpose.

No Input and Undefined Are Still Honest Answers

Two edge cases are worth settling now, because they look like exceptions but aren't.

A function that takes no arguments can still be a real function. currentHour() has no parameters, but it still describes a relationship, between calling it and the clock at that moment. The absence of an input is itself a valid input.

Returning undefined can be honest too. Imagine looking up a property that may or may not exist:

JavaScript
function getSetting(config, key) { return config[key]; }

If key isn't in config, this returns undefined. That's not a bug or a cop-out, it's the correct, semantically honest answer to "what is the value of a key that was never set." The relationship still holds. The output just happens to be the absence of a value.

The Actual Question to Ask

Forget counting return statements. The real test for any function in your code is simpler to state and harder to satisfy.

If you change what goes in, does the output change in a way the name predicted?

mystery(x, y) fails instantly, no matter what you feed it. readingTime(wordCount, wordsPerMinute) passes every time, because moving either input moves the output in a way the name already told you to expect.

That question is also the doorway into the next problem worth solving: what happens when a function's output depends on something other than its inputs, a global variable, the current time, a shared object sitting outside the function entirely. That's where side effects come in, and where this definition of a function starts getting genuinely tested.