Massimiliano Mirra

Notes



















Date:
Tags: code · typed languages
Status: finished

The dangers of greedy functions

Functions that ask for more than they need trick the developer into writing unnecessary code. At best, this slows down future development; at worst, it harms runtime qualities: of the present system.

Imagine that you inherited this code:

const post = await api.fetchPostById('abc123')
// => {
//  id: 'abc123',
//  title: 'The dangers of greedy functions',
//  authorName: 'Max'
// }

const postUrl = getPublishedUrl(post)

One day, the back-end team changes the API so that author data is exposed through a separate endpoint. Your IDE tells you that getPublishedUrl needs an argument of type { id: string, title: string, authorName: string }, so you make the necessary changes:

const post = await api.fetchPostById('abc123')
const author = await api.fetchAuthorByPostId(post.id)

const postUrl = getPublishedUrl({
  ...post,
  authorName: author.name,
})

Tests succeed, production works, all is good.

Some time later, you look into a file called post‑utils.ts:

type Post = {
  id: string
  title: string
  authorName: string
}

function getPublishedUrl(post: Post): string {
  return '/posts/' + post.id
}

It turns out that getPublishedUrl didn’t need author data at all.

And yet, it led you into adding complexity, and even causing some performance degradation (an extra network request).

The problem

Surely you could have peeked at getPublishedUrl implementation sooner?

For every getPublishedUrl in a contrived example, dozens exist in a real code base. But impracticality aside: why establish contracts, if you’re required to look at implementations? Function signatures and object interfaces are there to save you that work. If you still have to do it, they’re not doing their job right.

In fact, the real problem (at least at the program level) is that getPublishedUrl is lying. This:

function getPublishedUrl(post: Post): string

Says: My name is getPublishedUrl and, to do my job, I need a post (i.e. a map of id, title, and authorName)." A greedy lie.

The solution (again, at least at the program level) is for functions to only ask for what they really need:

function getPublishedUrl(postId: string): string

(Unless they really need 90% or so of a large, complex type, in which case there’s little point in leaving out the 10%.)

It’s harder to pinpoint the problem at the programmer level, i.e. what led someone to choose to write that code. Most likely it was a well-intentioned quest for simplicity and readability. Unfortunately, simplicity that isn’t backed by truth looks the part, and quietly invites its opposite.

I write in the hope of making other people’s developer journey smoother and more rewarding. If you noticed a mistake or have suggestions on how to improve this article, please let me know. If you think this article can be useful to others, please consider sharing it:

Stay in touch

© 2020-2024 Massimiliano Mirra