Hacker News new | past | comments | ask | show | jobs | submit login
Subverting the software interview (2021) (nliu.net)
279 points by g0xA52A2A on Jan 15, 2023 | hide | past | favorite | 73 comments




Quoting from aphyr as it still brings me joy and might encourage others to read through the interview pages there,

> “In Lisp,” you offer. “We often write domain-specific languages to solve new problems.”

> “C is not a DSL!”

> “If you insist.” Keep going anyway.


My favorite:

> The Church. The lambda calculus. The gay agenda. It has known a thousand names, a thousand forms.


Why gay agenda though? What is the connection?


It's a reference to the Church-Turing Thesis, the latter namesake having been persecuted for "homosexual acts" and forced to undergo chemical castration.


This (along with several others) is linked at the top of the article.


There is “no way” to subvert this FizzBuzz interview when writing code in Python 3 only.

  def inf_range(a):
    while True:
      yield a
      a += 1

  def take(n, it):
    return [a for a, b in zip(it, range(n))]

  def fizzbuzz():
    return (a + b or str(c) for a, b, c in zip(
        (a for _ in inf_range(1) for a in (("", "", "Fizz"))),
        (a for _ in inf_range(1) for a in (("", "", "", "", "Buzz"))),
        inf_range(1)))

  print(take(30, fizzbuzz()))
For bonus points: which alphanumeric characters can be changed in the code above?


I always liked the version with no branching statements. In python it would be roughly:

    def fizz_str(n):
        return "FIZZ"


    def buzz_str(n):
        return "BUZZ"


    def fizz_buzz_str(n):
        return "FIZZBUZZ"


    def to_str(n):
        return str(n)


    indexes = [3, 0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0]


    def fizz_buzz(n):
        funcs = [
            to_str,
            fizz_str,
            buzz_str,
            fizz_buzz_str
        ]
        return funcs[indexes[n % 15]](n)


    for i in range(1, 101):
        print(fizz_buzz(i))


Here is another version of similar idea as oneliner. Additional feature - no loops and no conditions.

    fb = lambda n : n == 0 or not fb(n-1) or not print(['{}',"fizz","buzz","fizzbuzz"][((n%3)==0)|(((n%5)==0)*2)].format(n))

    fb(100)


Nice use of bitwise OR


> 0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0

I never noticed this symmetry in fizzbuzz before.


It seems like I'm missing a joke.

Is this style in homage to the linked-to page? I can't figure out why you're not using itertools nor re-using itertools names. Is it that translating from .. is that Haskell? .. to Python without idiomatic translation the subversion?

  from itertools import islice, cycle

  def fizzbuzz():
    return (a + b or str(c) for c, (a, b) in enumerate(zip(
       cycle(("", "", "Fizz")),
       cycle(("", "", "", "", "Buzz")),
       ), start = 1))

  >>> print(*islice(fizzbuzz(), 0, 30))
  1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
  16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz


> You have a tendency to overengineer things.

Overengineering is an actual problem. For a tiny example, I'll see things like:

    enum MAGIC = 67;  // explanation
    ...
    foo(MAGIC);
The use of MAGIC is the only one, and is far removed. A better solution is:

    foo(67);  // explanation
because it improves locality.

I also see things like an object fleshed out with all kinds of member functions that are never used.


I think I can see what you're getting at that you view this as a premature abstraction, but I don't think I'd call it over engineering. I'd call it a stylistic difference.

If `MAGIC` was sufficient to understand what the significance of the number was and `// explanation` was the citation or derivation, then I'd be fine with this, especially if there were a bunch of other constants with explanations with similar derivations or citations (eg a bunch of trig or constants from the same standard); then it'd be preserving a different sort of locality.

If `MAGIC` wasn't sufficient to capture the purpose of the constant & such a name wasn't readily available, so that you're always going to want to read `// explanation` (rather than only in the case you think the value is wrong), then I'd agree with you.

Concrete(ish) examples: if 67 terminated the Foo section of the Bar binary file format, even if we only used it once, I'd prefer to see END_OF_FOO rather than read a comment. Reading comments is context switching, at least for me. Names help me stay in the headspace of the programming language.

If 67 was the result of an ad-hoc calculation which only made sense in the context of this particular program (how many angels can dance on the head of an Acme brand pin executing our trademarked choreography) while, and so there wasn't a great name for that calculation, or at least not a name that would make sense unless you've already read the derivation, then I'd agree in that case.

That being said, I have absolutely seen overengineering or misengineering cause real problems.


In a code review, I’d given a junior programmer advice to avoid magic constants and use defines instead (c, not c++).

Resubmission came back with:

  #define SEVENTEEN 17
Last I spoke with him, he was a Java instructor.


It is truly amazing. One can rave about OOP all day and not understand the basics of programming or good, readable code. Truly mindboggling.


At the risk of sounding contrarian, I don’t think it’s mind-boggling at all. There are two types of software engineers out there: those who read to understand, and those who understand to read. What I mean by this is that I find most of my coworkers “read to understand”- they don’t know X (or, more commonly, they don’t understand X and want to prove the person who wrote “X” wrong), so they Google around until they find an SO that supports their stance, or gets them code that does what they want it to do…. Then there are those who understand that to be truly great is to read beyond the minimum needed to achieve the goal, be it to get software to do X, to prove someone wrong in code review, or something else… and to READ for pleasure and to quench the thirst of knowledge. The passionate folk, in other words. Most of the time you can find out which group a person falls into by whether they read about and write code in their free time. Kind of like leetcode, this litmus test I find has a low false-positive rate, but a relatively high false-negative rate, as there are many passionate engineers who don’t have the time or energy to read and write in their free time (typically a “wife and kids” scenario).

E: in other words, those who code to live vs live to code.


The truly brilliant thing to do would be

#define SEVENTEEN 16


I worked for a while with a software engineer who insisted in encoding the value of the macro within the name:

  #define MAGIC_16 16
his rationale was that you had to go through all the uses and consider whether the change would cause an issue.

It takes all kinds.


// the law changed, but we're too lazy to rename the constant


I see where you're going. You never know when MAGIC will be needed and whether or not it will remain constant. It also doesn't scale horizontally. The right answer if you want to be fully robust is to make a Magic microservice that will return the magic number when needed and autoscale on demand. You could have the swagger doc for the service include the explanation.


You're joking, but your description of a prepare-for-any-circumstance mentality summarizes exactly the problem, you never know if MAGIC will be needed or not remain constant, so assuming it will not and making your program structure more complex for it is overengineering.

The best approach if you want to be future-proof while would be to label the instance in-place, so that you can look later for places where #MAGIC is being used, without losing locality:

    foo(67);  // #MAGIC explanation



    list, mist
    func, gunc
    church, dhurch
This is clearly the One True Naming Scheme and now that I have seen it I can't imagine ever using anything else.


Probably didn't pass the interview. The implementation was rather simplistic and low quality.

Here's a better enterprise version: https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...

Though the fact that it was written some years ago might be a weakness. Some interviewers might prefere a more modern, distributed solution.


A multi core one, with lockless data structures.


Better, with CRDTs.


This is brilliant and spot on.


What about observability?


I was thinking of making a modern distributed spaghetti version some time.

FizzService, BuzzService, FizzBuzzService, CountingService, etc


This github org needs more leetcode examples in that style


There is also the Hello World Enterprise Edition [1]

[1] https://github.com/Hello-World-EE


Or serverless


Everything makes sense to me until the Dual definitions:

  newtype Dual a = Dual {getDual :: a}
  instance Semigroup s => Dual s where
    (Dual a <> Dual b) = Dual (b <> a)
  instance Monoid m => Dual m where
    mempty = Dual (mempty)
These are declaring instances of Dual, but they are providing method definitions for Semigroup and Monoid as if they were declaring instances for those types. I plugged this into ghc and I couldn't get this to compile either way. This isn't using an extension or some more obscure language than haskell is it?


Likely meant to be lifting instances of Semigroup and Monoid through Dual:

    newtype Dual a = Dual {getDual :: a}

    instance Semigroup s => Semigroup (Dual s) where
        (Dual a) <> (Dual b) = Dual (b <> a)

    instance Monoid m => Monoid (Dual m) where
        mempty = Dual (mempty)


Funny!

The premise that modulo (%) is "slow" here doesn't quite justify the jump into Haskell, but it's still hilarious.


Could this be accelerated and parallelized by writing a custom GPU shader?


I was kind of hoping for a specialized FizzBuzz CPU built on an FPGA.


Great writing, hilarious.

I feel like we all could know someone who fits these characteristics.


I worked with a guy who implemented map and reduce in C++ as a subtask of some other task. It was, of course, a header-only templated monstrosity.

He called it "fun.hpp".

"Fun" times, indeed, working with him.


You'll like the `std::ranges` library that's been somewhat implemented as of C++20 and is getting some more stuff in C++23. It's very Fun!

    #include <ranges>
    #include <numeric>
    #include <vector>
    #include <iostream>

    int main() {
        std::vector<int> vec = {1, 2, 3};

        // map, using `std::views::transform`. Implemented in C++20
        for(int elem : 
                std::views::transform(vec, [](int x) { return x + 1; })) {
            std::cout << elem << " "; // 2 3 4
        }
        std::cout << "\n";

        // `std::ranges::fold_left` will be available with C++23.
        // Until then, we're stuck using the <numeric> iterator version, which was
        // implemented in C++20.
        std::cout << std::accumulate(vec.begin(), vec.end(), 1, 
                                     [](int x, int y) { return x * y; }) 
                  << "\n"; // 6

        return 0;
    }
Building and running with the following:

    $ g++ --std=c++20 test.cpp
    $ ./a.out
    2 3 4
    6


There's a nicer way still:

    #include <algorithm>
    #include <functional>
    #include <iostream>
    #include <numeric>
    #include <ranges>
    #include <vector>

    int main() 
    {
        std::vector<int> vec{1, 2, 3, 4, 5};
        auto plus_one{[](const auto x) { return x + 1; }};
        std::ranges::for_each(vec | std::views::transform(plus_one), [](const auto x) {
            std::cout << x << " ";
        });
        std::cout << "\n";

        std::cout << std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<>()) << "\n";

        return 0;
    }
https://godbolt.org/z/84ePjfPhf


OMG.


> I feel like we all could know someone who fits these characteristics.

"Know" or "be"?

:-P


I'm crying from laughter - this is simply fantastic! Mucho thanks to author and OP for making my morning!!!


In Clojure, general for any set of fizzbuzz-like prime factors:

  (defn any-buzz
    [num & words]
    (or (not-empty (reduce str words))
        num))

  (map any-buzz
       (range 1 16)
       (cycle [nil nil "Fizz"])
       (cycle [nil nil nil nil "Buzz"]))
Source: from a bunch of fizzbuzz code riffs I wrote to illustrate real-world programming and problem solving with Clojure:

https://www.evalapply.org/posts/n-ways-to-fizzbuzz-in-clojur...

edit: formatting, detail


What you need is Fizzbuzz, Enterprise Edition

https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...


Besides the article, I greatly appreciated the EGA-like color scheme!


The cycle method is really elegant in Perl, of all languages:

  use feature say;
  say <{Fizz,}> . <{Buzz,,,}> || $_ for 0..100;


It’s certainly concise, but I wouldn’t consider that to be elegant.


I'm somewhat well-versed in lambda calculus, and still I have no idea how it was related at all to this exercise.

In the end the only "subversion" is that it was written as zipped infinite ranges rather than an explicit for-loop.


great read. entertaining and engaging, wonderful creative writing with nerd flair.


A personal favorite

    ruby -e 'puts (0..99).map {|i| srand(46308667) if (i%15).zero?; ["FizzBuzz", "Buzz", i+1, "Fizz"][rand(4)]}'


This is great and was a lot of fun to figure out.

It also has the side effect of making me feel better for never attempting to write my own encryption algorithms! :D


> how their work ethics share the same ability to resist deformation as their play habits

"We work hard, we play hard" in case anyone missed it :-)


Lol I sure did miss it


Beautifully written, great read.


I'm smiling as I read from top to bottom.


So good.


Kyle did it better


Gorgeous. I'm not versed well enough in the theory to validate his code, but I know enough to see where he's going with this.

As an aside, there's a lot of rightful complaining about the interview process. I've been on both sides and it sucks either way.

As a hiring manager for a small company, we never had the 'process' that bigger companies have, so it was always a seat of the pants decision. Not my favourite thing to do.

As an interviewee, I've struggled with people's favourite questions, you know the ones that you ask so much that it starts sounding like an easy question in your head?... I'm productive, I ship software but interviewing is so incredibly stressful and always makes me feel like I don't know much.

Anyhow, if I was interviewing the OP for a typical web app CRUD position or monolith to micro services refactor or some other common role, I'd think twice. Clearly he's very smart. Is he able to "deliver value" vs optimizing code? I know the post was tongue in cheek, but in the real interview setting you gotta demonstrate how you're a smart team player and allude to your capabilities. It's kinda like dating I guess. You can't show how much of a real person you're on the first date.


You could have at least clicked on the LinkedIn link before assuming the author was a guy... https://www.linkedin.com/in/naomi-w-liu/


My bad, should’ve used “their”.


Not everyone has a LinkedIn account, not everyone may know that “Naomi” is usually feminine, and the article run through a textual gender analyser does read as “masculine”.

Mis-stating gender might be careless when as you imply “their” is the always the safe bet, but it’s probably not enough to justify OP’s unduly critical response. He / she or they could have just replied with “I checked their LinkedIn and Naomi’s a girl, btw”.


The name Naomi might have been a dead giveaway.


https://en.wikipedia.org/wiki/Naomi_Uemura

This man has won the People's Honour Award for climbing the highest mountains on 5 continents and being the first man to reach the north pole solo.

So Naomi is not necessarily a dead giveaway, you guys really split hairs and raise hackles over just about nothing worthwhile. Mistaking someone's sex is not a massive deal.

I used to have on my team a guy named Joan (pronounced joe-an) who spoke really softly, so his potential new employers would call me for a reference asking about "she" and "Joan", I'd gently correct them so as not to embarass him on any phone interview in the future. Nobody needed to throw a fit over an honest mistake.

Coincidentally, that guy was the best programmer I ever employed.

Anyway, just one of many counterexamples to a non-issue.


Does finding an instance of a male named Naomi really convince you that Naomi is not typically a female name?


Yes. There's a whole article about the given name, which says it is unisex: https://en.wikipedia.org/wiki/Naomi_(given_name)#Japanese

And this is on top of calling someone else out for presuming something... and here you all are presuming that Naomi is a feminine name.

We're making a mountain out of a molehill here. This is a persistent and pernicious problem in society at large now. What is the actual point of calling out any POSSIBLE perceived remote slight that you really have to perform mental gymnastics in bad faith to even arrive at?

There was no malicious intent on the part of the original poster, and yet they are excoriated for nothing?

What a massive amount of spare time we all have to waste.

Especially when it is a unisex name.

We could be doing something to better our world, and we spend time _on this_. We do it every day on a massive scale. It's so tiring, so wasteful, and so lamentably useless when there are so many useful and actually kind things we could be doing, instead of just virtue-signalling fake empathy on the internet that amounts to nothing.


Tbh he could just not have read the name... I certainly skipped it on the first read.


It can be a male name, here's a random example I found in Google: https://earth.stanford.edu/events/energy-seminar-naomi-hiros...


> validate his code

> by Naomi Liu



A name does not imply sex. Even if it did, the parent wrote multiple paragraphs and you get hung up on something as inconsequential as this.

Take a day off.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: