Hive mind
Tags: engineering, problems, story, rap
Published: 2024-03-26 12:43:58

A dim lit room. A bunch of hooded figures sit around the table.

If you didn't know better you'd swear they are doing an ancient ritual to wake up a sleeping monstrosity consisting mostly of tentacles, cones at odd angles and sexual innuendo.

But you _do_ know better, these are just your nerdy friends and they are doing what you've come to know as...

## Hive mind
"Hoodies never go out of style" you think as you take your position around the table.

You take a quick look around and, for a moment, feel transported to one of those logic puzzles from childhood:
> "The person with the extreme allergies sits between the one that speaks in the third person and the one solving the Rubik's cube. The one that drinks soda non-stop is also a fan of typed languages...

The rules of "Hive mind" are pretty simple, people take turns telling stories around the table. Stories of magic and mystery, of insanity and ingenuity, of the highest highs and the lowest lows... but they all share a common pattern, there's something to be learned from them.

And so it begins...

## Story 1: the forest and the trees

"OK, so we have _events_. These things capture something that happened in our app. They come in two flavors: frontend and backend".

"Frontend events are close to the end users and have a ton of context (what did they do just now, what did they do before that, etc.). They are also insecure and unreliable".

"Backend events are trustworthy and secure but their context is quite limited".

Problem statement:
> how might we add some rich frontend context to our "User registered" backend event?

"Any ideas on how to solve this?"

_...the speaker reads the room, the readers speak their mind..._

(You, yes _you_ 🫵 are one such reader, what do _you_ think we should do?)

After the brain storming and thundering quiets down, the room seems to agree on two possible solutions:
- Make "User registered" a frontend event (that way we can add all the context we want, security be damned)
- Send extra tracking stuff to the “register user” endpoint and track from there (because we love our endpoints with all sorts of random extra stuff in them)

Dismay feels the air, the speaker continues...

"Pretty disappointing, right?"

"The reason for this is that the problem statement is wrong! It biases out of other possible solutions, let's take a step back..."

- Why do we want that extra context in the "User registered" event?
- Because we want to use it in a report
- Where is that report coming from?
- Our data warehouse
- Don't we have both backend and frontend events in the data warehouse?
- Yes!
- Can't we just `JOIN` them?
- Yes!

"OK, so the problem statement could've been:

> We want to be able to create a report on registered users that is trustworthy (backend) _and_ includes behavioral tracking stuff we have in the frontend.

"See how the two solutions above still apply to this problem statement?"

"...but now we have other options at hand, like tracking two events, one from the backend and one from the frontend and then `JOIN` them back in the report".

Morale: when all solutions are disappointing perhaps the problem is ill-defined and we need to ask ourselves what are we really trying to accomplish.

## Story 2: the run query gig

One hooded figure takes out a strange contraption, presses a button and speaks into a microphone:

```
push push pop pop
push push pop (rest)
```

The contraption starts looping the phrase non-stop.

If you had to describe this to someone else you'd probably call it "Infinite stack beatboxing".

Others might call it "an [OOM](https://en.wikipedia.org/wiki/Out_of_memory) waiting to happen", but in this circle this is considered art.

The figure, now standing on the table, begins some free-style rapping:

_This is the story of a thing gone wrong,
So I sing this song, 'cause y'all should know._

_We were dealing with this ugly query,
It was big and hairy, it was slow and scary._

_So we caged that beast in this neat old function,
That would hide dysfunction and prevent malfunction._

_We called this function the `runQuery` gig, ho!
The `runQuery` gig, and it goes like this:_

_(snippet instrumental break)_

```ts
function runQuery(
  ...some args...,
  addSomeCrap: boolean,
): Stuff;
```

_That nasty query's usage was not small,
So we replaced it all with a `runQuery` call._

_While we worked `addSomeCrap` was `true`,
But as the code improved that would get removed._

_Unfortunately `runQuery` had a trap,
'cause it had no map, and you could still add crap._

_Surely enough someone did fall prey,
And to our dismay, added more new crap, no way!_

_So we saw the problem with our `runQuery` gig, ho!
Our `runQuery` gig, and it goes like this:_

_(reflective prosaic coda)_

> We had a problem with our nasty query so we chose to hide it behind and abstraction layer, one we called `runQuery`.

> We knew in the end `runQuery` would be efficient, but as we iteratively worked through the all the calling instances we needed a way to still convey its inefficient version (and so `addSomeCrap` was still `true` sometimes).

> What we failed to see is that `runQuery` became our abstraction's interface and that interface could be used for good and for evil, and turning one into the other was just one `true` away.

> A better approach was to have two separate `runQuery` versions the proper one, `runQuery`, and its evil twin,  `runQueryInefficientlyPullingAllSortsOfCrap`.

> We could then throw a nice comment in the latter explaining why you shouldn't use it, what to use instead and even throw in a `@deprecated` tag so that the IDE helps you spot the trap.

_(Hooded figure bows, standing ovation, the curtain falls)_