import { utility} from StackOverflow

When you want a simple utility function that isn’t part of the standard library, what do you do?

A. Find a library and use it
B. Write the function and add it to an internally shared collection
C. Cut and paste from StackOverflow

I’ve done all three in my twenty years of development, and I’m here to argue for option C.

The other day I needed a range function: give me an array of the integers from 0 to some number. So I googled “range in JavaScript,” hoping for a built-in method on Array.

Google brought me to a StackOverflow answer. The TL;DR is Array(n).keys(). If I think about it, I can see how that has the effect I’m looking for.

Option A: the very helpful answer points to the range function in lodash, a common library. If my codebase already had lodash as a dependency, I’d use that. It would be reasonable to bring it in, because lodash is practically an expanded standard library for JS. But adding dependencies has bigger implications for an application, like bloat and slower installation, so I don’t want to add it.

Option B: the very helpful answer also supplies a complete implementation with types, including an optional argument for where to start the range.

function range(size:number, startAt:number = 0):ReadonlyArray<number> {
    return [...Array(size).keys()].map(i => i + startAt);
}

I could put that in its own place, maybe range.ts or ArrayUtilities.ts. Then I will aways have access to range. I would build up my power with access to reusable code.

Except… notice what’s missing? I thought about adding the function to a shared utility collection, but I never mentioned looking in the shared utility collection. Because that’s harder than googling it. So no, I’m not building up my power with reusable code. I’m building up cruft.

Cruft that would include tests, because it’d be irresponsible to put a function out there for re-use without tests. Cruft that would include documentation, because that’s important. And thinking about API design, and getting the types right.

That’s way more work. And after all of that work, I gain a function that I can’t easily change. Who else might be using it? Maybe the IDE can tell me, but no guarantees (it’s JavaScript). Even if I change all known callers, I’m open to merge conflicts.

The range function increases my power only while it’s near to hand, while I remember it’s there. That’s faster than searching. Over in some utility package, it won’t stay in my head long. And it won’t be in the head of my teammates either, because they didn’t write it.

You know what is near to hand? This file right here that I’m working in. I can put the code right here, next to where it’s used.

Option C

For minimum effort, I could inline Array(n).keys() where I need a range. But that’s confusing, because reading the code, it takes me a while to figure out what that does. I do need to give it a name.

So I’m going to name a function in the same file, and start from there.

// https://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-a-range-within-the-supp
function range(n) {
   return Array(n).keys();
}

A link provides attribution and explanation. Sometimes I put that in the commit message instead of the code. More often I remove it later, but leave it in git history.

Then I improve the function just enough for my purposes. Right, I need types because TypeScript. And then keys() only gives you an iterator, not a proper array, so add that weird spread syntax.

function range(n: number): number[] {
   return [...Array(n).keys()];
}

It has a name that I won’t need to click into. It is only as complete as I need it for my use right now.

What about tests? Does it need tests?

I vote no; if it’s working for the place where I call it, then it’s working. Breaking out a function is a refactor; it doesn’t add concepts, and doesn’t need more overhead. Then again, maybe I want some exploratory tests to try it out. (I did that in the REPL.)

When the utility function is local to the file, then it doesn’t have to support every edge case. It doesn’t need a perfect API, just one expressive enough for here. And when I want to change it, I’m free to. Its scope is covered by Ctrl-F.

Reusable code is way, way more work than right-here code. Save that work for the hard stuff that matters. If your utility starts getting tricky, fall back to finding a library where someone has already worked through these microproblems. If your function contains business logic, then promote it to a real tested function. If that logic needs to be consistent through your application, it’s time for an internal library or service.

TL;DR: When you want a utility function, make it. Put it locally, right where you are. Name it what you searched for. Maybe link to your copy-and-paste source. Test it in your wider tests. Improve it only as much as you need. Delete it when it isn’t used. Promote it when it gets interesting.

Discover more from Jessitron

Subscribe now to keep reading and get access to the full archive.

Continue reading