I could sense that I was wading into it again. That murky territory in between me demonstrating why I would make a worthwhile addition to the team, and between the interviewer, expecting me to conform to his pre-selected answer to a question – arbitrarily constructed, and unapologetically contrived – which he probably received from an interviewer himself, who OK'd him for this seat of power over my future. The space between good answers and correct answers, widely known among its explorers as "Bullsh**". Population: Your dignity.
It began innocently enough: A simple question about how I would store, retrieve, and compute the data necessary to determine if a
user needed to be alerted about one of the actions they had just taken. I'd already said it made sense to store the data for these actions on its own table, separate from
users, since it was unlikely that every user would be taking these types of actions, and since it was its own problem domain, distinct from the standard way the application would be interacted with.
The first hint I'd be wading into Bullsh** came as he uttered, "I see that you've put a reference back to the
user_id. What would be your reasoning for placing this in a relational database as opposed to a NoSQL option?"
Now, I've had plenty of these interviews. I know that the exact answer he was looking for was, "Oh! Definitely. I just assumed since you were in this space for a while and would have a legacy system. Sorry, yes. So you would attach the
actions document to the
user document …"
But the same thing that makes me a great developer makes me a terrible interviewer: Once I learn a different perspective, I actually do adopt it if my perspective was lacking, or flawed. And if I know what I'm talking about, I will explain that shiz to you.
I should have known I wasn't getting the job.
"Well, technically, a NoSQL database is still going to be relational. You won't express the relationships between models using Structured Query Language, but your documents are still going to relate to one another, whether they exist in entirely different document stores, or as attachments onto one another. Just because you don't express that in SQL doesn't mean it's not still a relationship."
My interviewer adjusted in his seat and re-affixed his glasses at the top of the bridge of his nose. "Okay. So then, you would be using... PostgreSQL? Is that safe to assume?"
"Why would you use that over something newer like MongoDB, for instance?"
This interviewer mentioned something about working closer to the database than the others on his team, so I knew treading lightly was necessary to keep from slipping into a big pocket of Bullsh**. "Well, Postgre is –"
"... Oh. Yes. Postgre. I don't know if there's a standard way of saying it or not, but I just think, the S is part of SQL, so, 'Postgre'. Anyway, Postgre is a venerated platform, capable of handling queries that perform on millions of rows, with solid performance metrics, and a reliable architecture. I would say that it's proven itself in production cases thousands of times over, and that MongoDB and NoSQL in general have been around for less time, have shown themselves to be generally less reliable and less performant in real-world application, and would have to ask someone eager to implement them in an application, 'Why?' Simply put, Postgre is tested, and it works. If it ain't broke, don't fix it."
The interviewer furrowed his brow a bit at the end of this explanation, rubbed his chin, and went "Hm." All great signs, in my book. "Okay... now, what if this query took too long to perform every time. Let's say that querying the database is just… too slow."
Oh no! My subconscious shouted, Bullsh**, we are now entering extreme Bullsh**. Abort, scuttle, jettison, ctrl-alt-del!
My subconscious knows what's up. My conscious replied, I want to work here though! And not once did I ask my interviewer, "What's slowing the query down?"
But I did pause for a minute, think about it, and then offer, "Well... if gathering all of the relevant rows is too slow, and all we care about is the running average, then we can just store the average value of the previous
n actions, as well as the value of the least-recent action we care about, multiply that average value by
n, subtract the least-recent action's value, add the newest action's value, then get that average. It's a slight amount of arithmetic, but it would only require retrieving data from one row, and will always get done in the same amount of time, regardless of how many actions we care about."
Consternation emanated from my interviewer. "How does... could you run that by me one more time? I'm having trouble visualizing how it would work."
"Sure! So you just care about the average reading, right?" I grabbed a nearby sheet of paper and a pen, and I drew out the process for him, step by step, and going over each of the significant reasons for doing the step I was talking about.
My interviewer plainly asked, "What would you think about storing the data you care about in a cache?"
BULLSH** CENTRAL STATION. THIS IS BULLSH** CENTRAL STATION. PLEASE GATHER YOUR BELONGINGS AND DO NOT GET BULLSH** ON THEM AS YOU DEPART THE TRAIN. THIS IS THE TRAIN'S FINAL STOP.
"The data I care about being all of the actions?" I asked, only slightly dumbstruck.
"That's right. What if you stored it in Redis."
"I... would think, 'Using a cache introduces its own entire can of worms around misses, invalidation, and stale data'. Plus, I would argue that collecting the sum value of all of these actions every time and then averaging it would be consistently more work than a bit of simple math. I mean, do you know for sure that a cache is going to be the better answer, or were you more just hoping it was the answer I would give?"
"I guess I was hoping it was the answer you'd give," my interviewer replied. "Well, we're about out of time. I wanted to leave a few minutes if there are any questions you have for me. Any you can think of?"
"Yeah, I was curious, what's the most difficult problem you've had to debug around the implementation of a cache?"
"Well, I haven't really done any work with caches, myself..."
"Oh, you haven't?" I asked, "Well, you should try it some time. Cache problems are super fun."
The simple rule, "Nobody likes getting called out on their bullsh*" should have been at the forefront of my mind, after just calling two people out on bullsh* earlier that week on Twitter, and being summarily blocked by both of them. This is an interview, though, my conscious told my subconscious, and I just won it.
I didn't win it. You don't win interviews, even though typically, you are battling the person sitting opposite you, in a dim, dreary conference room emblematic of the humility you are to demonstrate. For in interviews, you, the interviewee, are always a speck of dust to the interviewer. They already work for Great Company You Wish You Were At. They've seen how the sausage gets made. They've already got accrued PTO languishing in an unused state. What do you have? A resumé with a bunch of words on it, and no pictures? Years of "building fault-tolerant applications at scale"? The ability to work on a team and to acknowledge the best decisions when they come out of larger group discussions? None of that is relevant here. You either agree with the things Interviewer says, or you're uncooperative, you're pushy, and (especially if you're pretty, look younger than you are, or are kind of a klutz) you don't know what you're talking about. The cache is a silver bullet, and silver bullets kill all kinds of monsters besides werewolves.
Every interview is an expedition through Bullsh**. Landing the interview is about determining what kind and how much you're looking at, plugging your nose, and just wading through. No, you won't have your dignity, but you'll have something better: Meager funds for survival.