Chapter10 Case Studies War Stories

If you find this content useful, consider buying this book:

  • Amazon
  • Purchase all books bundle
  • Purchase from pragmatic ai labs
  • Subscribe all content monthly
  • If you enjoyed this book considering buying a copy

    Chapter 10: Case Studies and War Stories #

    We live in a world full of useless rituals. Remember commuting into a physical office to work 9-5? Hiring often falls into the trap of unnecessary “trick” questions. Why is this? Humans introduce bias, opinions, and other inefficiencies. How can it possibly make sense to ask a candidate to complete the hours or days of tests for each interview? How can companies ask questions about algorithms that never show up for the position the person is interviewing for?

    One solution is to remove humans from hiring and allow automated processes. With the global pandemic at play, it may be the case that the world removes humans from hiring and make things async. Candidates could have solutions to questions already solved, and then companies would reach out to them.

    War Story: Multi-inheritance hell #

    Noah Gift

    I remember fondly being throw into a codebase of hundreds of thousands of lines of code in Python. At the time, I didn’t have a firm grasp of Python, and another developer told me that all code should be “object-oriented.” This particular code based had many levels of multiple inheritance and, as a result, was a tower of garbage.

    To say it was “hell” is understating the problem. To change one file, I had to look at dozens of files. This workflow was the outcome of “textbook " brilliant people left to unchecked complexity. Smart developers gravitate toward complexity. They often want to do things that make their brain hurt.

    Multiple inheritance is often a “solution in search of a problem.” It can lead to a level of abstraction that is hard to understand. The lead developer told me I needed to understand object-oriented programming. With a lifetime of developer experience after that, I realize he was mistaken. This codebase was a rat-infested apartment with a slum lord owner who couldn’t be bothered to fix the mess they created. Instead, they were solving global pandemics and establishing peace in the middle east (wink).

    Reducing complexity is job number one for an experienced and competent developer. Writing code is not about proving you are smarter than your co-workers. It is about creating solutions that work and are easily maintainable. You may be reading this book right and realize you are in a similar work environment. Don’t be afraid to either pushback on complexity or get a job where complexity is not a goal. Software development should be “Lord of the Flies”.

    Interview Question Breakdown: Greedy Coin #

    Noah Gift

    You can find the code to this solution here. The general idea with this code example is to pick a “greedy” solution to make the change. Remember that “greedy” algorithms work by always taking the best option first. In this case, it would be the highest value coin and making the change with those, then moving onto the next highest.

    ➜  greedy_coin git:(master) python change.py --full 1.34
    Quarters 5: , Remainder: 9
    Dimes 0: , Remainder: 0
    Nickles 1: , Remainder: 4
    Pennies 4:
    

    There are several different types of “greedy” answers here.

    #!/usr/bin/env python
    #Noah Gift
    #Greedy Coin Match Algorithms
    
    import optparse
    import decimal
    import sys
    
    class Change():
        """Gives Correct Change Using As Many Greedy Match Algorithms as Possible"""
    
        def __init__(self, amount, verbose=False):
            self.amount = amount
            self.convert = int(self.amount*100)
            self.verbose = verbose
            self.coins = [1,5,10,25]
            self.coin_lookup = {25: "quarters", 10: "dimes", 5: "nickels", 1: "pennies"}
            if self.verbose:
                print "Converted Change Value: %s" % self.convert
    
        def printer(self,num,coin):
            """Printing Method For Recursive Results and While Results"""
            if num:
                if coin in self.coin_lookup:
                    print num, self.coin_lookup[coin]
    
        def recursive_change(self, rem):
            """Greedy Coin Match with Recursion
            >>> c = Change(.71)
            >>> c.recursive_change(c.convert)
            2 quarters
            2 dimes
            1 pennies
            [1, 0, 2, 2]
    
            """
            if len(self.coins) == 0:
                return []
            coin = self.coins.pop()
            num, new_rem = divmod(rem, coin)
            self.printer(num,coin)
            return self.recursive_change(new_rem) + [num]
    
        def while_loop_change(self):
            """Greedy Coin Match with While Loop
    
            >>> c = Change(.71)
            >>> c.while_loop_change()
            2 quarters
            2 dimes
            1 pennies
    
            """
            coin = self.coins.pop()
            num, rem  = divmod(self.convert, coin)
            self.printer(num,coin)
            while rem > 0:
                coin = self.coins.pop()
                num, rem = divmod(rem, coin)
                self.printer(num,coin)
    
        def make_change_conditional(self):
            """Greedy Coin Match with Conditional Statements
    
            Return both numbers of coins and remainder
    
            >>> c = Change(.71)
            >>> c.make_change_conditional()
            (2, 21, 2, 1, 0, 0, 1)
            >>>
    
            """
    
            quarter, qrem = divmod(self.convert,25)
    
            #initialize values
            dime, drem = 0,0
            nickel, nrem = 0,0
            penny = 0
    
            #if remainder is dime or higher
            if qrem >= 10:
                dime, drem = divmod(qrem,10)
                if drem >= 5:
                    nickel, nrem = divmod(drem,5)
                    if nrem >= 1:
                        penny = nrem
                elif drem < 5:
                        penny = drem
    
            #if remainder is nickel or higher
            elif qrem >= 5:
                nickel, nrem = divmod(qrem,5)
                if nrem >= 1:
                    penny = nrem
    
            #if remainder is penny or higher
            elif qrem > 0:
                penny = qrem
    
            return quarter, qrem, dime, drem, nickel, nrem, penny
    
        def full_results_printer(self):
            """Debug Print Method for Conditional Results"""
            quarter, qrem, dime, drem, nickel, nrem, \
            penny = self.make_change_conditional()
            print "Quarters %s: , Remainder: %s" % (quarter,qrem)
            print "Dimes %s: , Remainder: %s" % (dime,drem)
            print "Nickles %s: , Remainder: %s" % (nickel,nrem)
            print "Pennies %s:" % penny
    
        def conditional_printer(self):
            """Print Method For Conditional Results
    
            >>> c = Change(.71)
            >>> c.conditional_printer()
            Quarters 2
            Dimes 2
            Pennies 1
    
            """
            quarter, qrem, dime, drem, nickel, nrem, \
            penny = self.make_change_conditional()
            if quarter:
                print "Quarters %s" % quarter
            if dime:
                print "Dimes %s" % dime
            if nickel:
                print "Nickles %s " % nickel
            if penny:
                print "Pennies %s " % penny
    
    def controller():
        p = optparse.OptionParser(description="Makes Change Using Greedy Coin Match",
                                    prog = "pychange",
                                    version = "pychange 0.1",
                                    usage = "%prog [value] [options]")
    
        p.add_option("--full", "-f",
                    action="store_true",
                    help="Prints coin, plus remainder")
    
        p.add_option("--recursive", "-r",
                    action="store_true",
                    help="Prints coins with recursion")
    
        p.add_option("--loop", "-l",
                    action="store_true",
                    help="Prints coins with while loop")
    
        options, arguments = p.parse_args()
        if arguments:
            try:
                value = decimal.Decimal(arguments[0])
            except decimal.InvalidOperation:
                print "Please enter a valid number to make change"
                sys.exit(1)
            c = Change(value)
            if options.full:
                c.full_results_printer()
            elif options.recursive:
                c.recursive_change(c.convert)
            elif options.loop:
                c.while_loop_change()
            else:
                c.conditional_printer()
        else:
            p.print_help()
    
    def main():
        controller()
    
    if __name__ == "__main__":
        main()
    

    This example is a test for the algorithm.

    #!/usr/bin/env python2.5
    #Noah Gift
    #Greedy Coin Match Python
    
    import unittest
    import change
    
    class TestChange(unittest.TestCase):
        def test_get_quarter(self):
            c = change.Change(.25)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(quarter,1)  #quarters
            self.assertEqual(qrem, 0)   #quarter remainder
        def test_get_dime(self):
            c = change.Change(.20)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(quarter,0)  #quarters
            self.assertEqual(qrem, 20)   #quarter remainder
            self.assertEqual(dime, 2)    #dime
            self.assertEqual(drem, 0)    #dime remainder
        def test_get_nickel(self):
            c = change.Change(.05)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(dime, 0)    #dime
            self.assertEqual(drem, 0)    #dime remainder
            self.assertEqual(nickel, 1)  #nickel
            self.assertEqual(nrem, 0)    #nickel remainder
        def test_get_penny(self):
            c = change.Change(.04)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(penny, 4)  #nickel
        def test_small_number(self):
            c = change.Change(.0001)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(quarter,0)  #quarters
            self.assertEqual(qrem, 0)   #quarter remainder
            self.assertEqual(dime, 0)    #dime
            self.assertEqual(drem, 0)    #dime remainder
            self.assertEqual(nickel, 0)  #nickel
            self.assertEqual(nrem, 0)    #nickel remainder
            self.assertEqual(penny, 0)    #penny
        def test_large_number(self):
            c = change.Change(2.20)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(quarter, 8)  #nickel
            self.assertEqual(qrem, 20)  #nickel
            self.assertEqual(dime, 2)  #nickel
            self.assertEqual(drem, 0)  #nickel
        def test_get_quarter_dime_penny(self):
            c = change.Change(.86)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(quarter,3)  #quarters
            self.assertEqual(qrem, 11)   #quarter remainder
            self.assertEqual(dime, 1)    #dime
            self.assertEqual(drem, 1)    #dime remainder
            self.assertEqual(penny, 1)   #penny
        def test_get_quarter_dime_nickel_penny(self):
            c = change.Change(.91)
            quarter, qrem, dime, drem, nickel, nrem, penny = c.make_change_conditional()
            self.assertEqual(quarter,3)  #quarters
            self.assertEqual(qrem, 16)   #quarter remainder
            self.assertEqual(dime, 1)    #dime
            self.assertEqual(drem, 6)    #dime remainder
            self.assertEqual(nickel, 1)  #nickel
            self.assertEqual(nrem, 1)    #nickel remainder
            self.assertEqual(penny, 1)    #penny
    
    if __name__ == "__main__":
        unittest.main()
    

    Interview Question Breakdown: Traveling Salesman problem #

    Noah Gift

    One of the most studied problems in optimization is the traveling salesman problem. You can find the source code here. This example is a list of routes that are in a routes.py file. It shows the distance between different companies in the Bay Area.

    What is the TSP problem, and why do you care (besides answering a whiteboard question for an annoying Stanford Computer Science grad interviewing you)? It is an excellent example of a problem where a perfect solution doesn’t exist, but a good enough one does. (Kind of like putting rich kids into elite schools because it is more simple than figuring out who is talented). The general problem asks the question, “How can you travel to a list of cities and minimize the distance”?

    One way to do this is to use a “greedy” algorithm. A greedy algorithm will pick the right solution at every choice. Often this can lead to a good enough answer. In this particular example, a “random” city is chosen each time as the starting point. This example adds the ability for simulations to pick the lowest distance. A user of the simulations could simulate for as many times as they have time. The smallest total length would is the best answer.

    values = [
    ("AAPL", "CSCO", 14),
    ("AAPL", "CVX", 44),
    ("AAPL", "EBAY", 14),
    ("AAPL", "GOOG", 14),
    ("AAPL", "GPS", 59),
    ("AAPL", "HPQ", 14),
    ("AAPL", "INTC", 8),
    ("AAPL", "MCK", 60),
    ("AAPL", "ORCL", 26),
    ("AAPL", "PCG", 59),
    ("AAPL", "SFO", 46),
    ("AAPL", "SWY", 37),
    ("AAPL", "URS", 60),
    ("AAPL", "WFC", 60),
    
    ("CSCO","AAPL" ,14),
    ("CSCO", "CVX", 43),
    ("CSCO", "EBAY",0),
    ("CSCO", "GOOG",21),
    ("CSCO", "GPS",67),
    ("CSCO", "HPQ",26),
    ("CSCO", "INTC",6),
    ("CSCO", "MCK",68),
    ("CSCO", "ORCL",37),
    ("CSCO", "PCG",68),
    ("CSCO", "SFO",57),
    ("CSCO", "SWY",32),
    ("CSCO", "URS",68),
    ("CSCO", "WFC",68),
    
    ("CVX","AAPL" ,44),
    ("CVX", "CSCO",43),
    ("CVX", "EBAY",43),
    ("CVX", "GOOG",36),
    ("CVX", "GPS",43),
    ("CVX", "HPQ",40),
    ("CVX", "INTC",41),
    ("CVX", "MCK",46),
    ("CVX", "ORCL",39),
    ("CVX", "PCG",44),
    ("CVX", "SFO",45),
    ("CVX", "SWY",13),
    ("CVX", "URS",44),
    ("CVX", "WFC",44),
    
    ("EBAY","AAPL" ,14),
    ("EBAY", "CSCO",0),
    ("EBAY", "CVX",43),
    ("EBAY", "GOOG",21),
    ("EBAY", "GPS",67),
    ("EBAY", "HPQ",26),
    ("EBAY", "INTC",6),
    ("EBAY", "MCK",68),
    ("EBAY", "ORCL",37),
    ("EBAY", "PCG",68),
    ("EBAY", "SFO",57),
    ("EBAY", "SWY",32),
    ("EBAY", "URS",68),
    ("EBAY", "WFC",68),
    
    ("GOOG","AAPL",11),
    ("GOOG", "CSCO",21),
    ("GOOG", "CVX",36),
    ("GOOG", "EBAY",21),
    ("GOOG", "GPS",48),
    ("GOOG", "HPQ",6),
    ("GOOG", "INTC",15),
    ("GOOG", "MCK",49),
    ("GOOG", "ORCL",16),
    ("GOOG", "PCG",48),
    ("GOOG", "SFO",36),
    ("GOOG", "SWY",32),
    ("GOOG", "URS",49),
    ("GOOG", "WFC",49),
    
    ("GPS","AAPL" ,59),
    ("GPS", "CSCO",67),
    ("GPS", "CVX",43),
    ("GPS", "EBAY",67),
    ("GPS", "GOOG",48),
    ("GPS", "HPQ",45),
    ("GPS", "INTC",62),
    ("GPS", "MCK",03),
    ("GPS", "ORCL",34),
    ("GPS", "PCG",01),
    ("GPS", "SFO",18),
    ("GPS", "SWY",53),
    ("GPS", "URS",01),
    ("GPS", "WFC",01),
    
    ("HPQ","AAPL" ,14),
    ("HPQ", "CSCO",26),
    ("HPQ", "CVX",40),
    ("HPQ", "EBAY",26),
    ("HPQ", "GOOG",6),
    ("HPQ", "GPS",45),
    ("HPQ", "INTC",20),
    ("HPQ", "MCK",46),
    ("HPQ", "ORCL",12),
    ("HPQ", "PCG",46),
    ("HPQ", "SFO",32),
    ("HPQ", "SWY",37),
    ("HPQ", "URS",46),
    ("HPQ", "WFC",46),
    
    ("INTC","AAPL",8),
    ("INTC","CSCO",6),
    ("INTC", "CVX",41),
    ("INTC", "EBAY",6),
    ("INTC", "GOOG",15),
    ("INTC", "GPS",62),
    ("INTC", "HPQ",20),
    ("INTC", "MCK",63),
    ("INTC", "ORCL",31),
    ("INTC", "PCG",62),
    ("INTC", "SFO",51),
    ("INTC", "SWY",32),
    ("INTC", "URS",63),
    ("INTC", "WFC",63),
    
    ("MCK", "AAPL",60),
    ("MCK", "CSCO",68),
    ("MCK", "CVX",46),
    ("MCK", "EBAY",68),
    ("MCK", "GOOG",49),
    ("MCK", "GPS",03),
    ("MCK", "HPQ",46),
    ("MCK", "INTC",63),
    ("MCK", "ORCL",34),
    ("MCK", "PCG",3),
    ("MCK", "SFO",16),
    ("MCK", "SWY",56),
    ("MCK", "URS",3),
    ("MCK", "WFC",2),
    
    ("ORCL", "AAPL",22),
    ("ORCL", "CSCO",37),
    ("ORCL", "CVX",39),
    ("ORCL", "EBAY",37),
    ("ORCL", "GOOG",16),
    ("ORCL", "GPS",34),
    ("ORCL", "HPQ",12),
    ("ORCL", "INTC",31),
    ("ORCL", "MCK",34),
    ("ORCL", "PCG",35),
    ("ORCL", "SFO",20),
    ("ORCL", "SWY",40),
    ("ORCL", "URS",35),
    ("ORCL", "WFC",35),
    
    ("PCG", "AAPL",59),
    ("PCG", "CSCO",68),
    ("PCG", "CVX",44),
    ("PCG", "EBAY",68),
    ("PCG", "GOOG",48),
    ("PCG", "GPS",01),
    ("PCG", "HPQ",46),
    ("PCG", "INTC",62),
    ("PCG", "MCK",03),
    ("PCG", "ORCL",35),
    ("PCG", "SFO",18),
    ("PCG", "SWY",54),
    ("PCG", "URS",01),
    ("PCG", "WFC",01),
    
    ("SFO", "AAPL",46),
    ("SFO", "CSCO",57),
    ("SFO", "CVX",45),
    ("SFO", "EBAY",57),
    ("SFO", "GOOG",36),
    ("SFO", "GPS",18),
    ("SFO", "HPQ",32),
    ("SFO", "INTC",51),
    ("SFO", "MCK",16),
    ("SFO", "ORCL",20),
    ("SFO", "PCG",18),
    ("SFO", "SWY",52),
    ("SFO", "URS",18),
    ("SFO", "WFC",18),
    
    ("SWY", "AAPL",37),
    ("SWY", "CSCO",32),
    ("SWY", "CVX",13),
    ("SWY", "EBAY",32),
    ("SWY", "GOOG",32),
    ("SWY", "GPS",53),
    ("SWY", "HPQ",37),
    ("SWY", "INTC",32),
    ("SWY", "MCK",56),
    ("SWY", "ORCL",40),
    ("SWY", "PCG",54),
    ("SWY", "SFO",52),
    ("SWY", "URS",54),
    ("SWY", "WFC",54),
    
    ("URS", "AAPL",60),
    ("URS", "CSCO",68),
    ("URS", "CVX",44),
    ("URS", "EBAY",68),
    ("URS", "GOOG",49),
    ("URS", "GPS",01),
    ("URS", "HPQ",46),
    ("URS", "INTC",63),
    ("URS", "MCK",03),
    ("URS", "ORCL",35),
    ("URS", "PCG",01),
    ("URS", "SFO",18),
    ("URS", "SWY",54),
    ("URS", "WFC",0),
    
    ("WFC", "AAPL",60),
    ("WFC", "CSCO",68),
    ("WFC", "CVX",44),
    ("WFC", "EBAY",68),
    ("WFC", "GOOG",49),
    ("WFC", "GPS",01),
    ("WFC", "HPQ",46),
    ("WFC", "INTC",63),
    ("WFC", "MCK",02),
    ("WFC", "ORCL",35),
    ("WFC", "PCG",01),
    ("WFC", "SFO",18),
    ("WFC", "SWY",54),
    ("WFC", "URS",0),
    
    ]
    

    This following is the script used. Note that it takes as an input of the complete simulations to run.

    #!/usr/bin/env python
    """
    Traveling salesman solution with random start and greedy path selection
    You can select how many iterations to run by doing the following:
    
    python tsp_greedy_random_start.py 20 #runs 20 times
    
    
    """
    
    import sys
    from random import choice
    import numpy as np
    from routes import values
    
    dt = np.dtype([('city_start', 'S10'), ('city_end', 'S10'), ('distance', int)])
    data_set = np.array(values,dtype=dt)
    
    def all_cities(mdarray):
        """Takes a multi-dimensional array in this format and finds unique cities
        
        array([["A", "A"],
        ["A", "B"]])
        
        """
        cities = {}
        city_set = set(data_set['city_end'])
        for city in city_set:
            cities[city] = ""
        return cities
        
    def randomize_city_start(all_cities):
        """Returns a randomized city to start trip"""
        
        return choice(all_cities)
    
    def get_shortest_route(routes):
        """Sort the list by distance and return shortest distance route"""
    
        route = sorted(routes, key=lambda dist: dist[2]).pop(0)
        return route
    
    def greedy_path():
        """Select the next path to travel based on the shortest, nearest path"""
    
        itinerary = []
        cities = all_cities(data_set)
        starting_city = randomize_city_start(cities.keys())
        #print "starting_city: %s" % starting_city
        cities_visited = {}
        #we want to iterate through all cities once
        count = 1
        while True:
            possible_routes = []
            distance = [] 
            #print "starting city: %s" % starting_city
            for path in data_set:
                if starting_city in path['city_start']:
                    #we can't go to cities we have already visited
                    if path['city_end'] in cities_visited:
                        continue
                    else:
                        #print "path: ", path
                        possible_routes.append(path)
            
            if not possible_routes:
                break
            #append this to itinerary
            route = get_shortest_route(possible_routes)
            #print "Route(%s): %s " % (count, route)
            count += 1
            itinerary.append(route)
            #add this city to the visited city list
            cities_visited[route[0]] = count
            #print "cities_visited: %s " % cities_visited
            #reset the starting_city to the next city
            starting_city = route[1]
            #print "itinerary: %s" % itinerary
        
        return itinerary
    
    def get_total_distance(complete_itinerary):
        
        distance = sum(z for x,y,z in complete_itinerary)
        return distance
    
    def lowest_simulation(num):
        
        routes = {}
        for i in range(num):
            itinerary = greedy_path()
            distance = get_total_distance(itinerary)
            routes[distance] = itinerary
        shortest_distance = min(routes.keys())
        route = routes[shortest_distance]
        return shortest_distance, route
    
    def main():
        """runs everything"""
        
        if len(sys.argv) == 2:
            iterations = int(sys.argv[1])
            print "Running simulation %s times" % iterations
            distance, route = lowest_simulation(iterations)
            print "Shortest Distance: %s" % distance
            print "Optimal Route: %s" % route
        else:
            #print "All Routes: %s" % data_set
            itinerary = greedy_path()
            print "itinerary: %s" % itinerary
            print "Distance: %s" % get_total_distance(itinerary)
        
    
    if __name__ == '__main__':
       main()
    

    Noah Gift

    Interview Question Breakdown: Introduction to Big O notation in Python #

    Noah Gift

    With Covid-19 it offers a great lesson that can apply to Big O notation. The general intuition is the “growth rate” of time. Developers should focus on solutions that don’t create uncontrolled growth. For example, in the case of Covid-19, we want to have grown to be logarithmic vs. exponential. This example is the so-called “flatten the curve” analogy.

    With a logarithmic growth rate eventually, things flatten out. Even better, imagine a growth rate that is zero. Imagine if we could simultaneously “cure” billions of people with a vaccine. This example is equivalent to O(1) or “fixed time” algorithms like a python dictionary. If you find yourself stuck with the intuition of how Big O notation works, it is similar to our collective experience with the ability to flatten the curve.

    Interviewing #

    Alfredo Deza

    I’ve had a fair share of bad interviews. When I started in technology, I did so knowing that it would be challenging because I didn’t come from a background in technology. I dropped out of college after a couple of months, and never had formal Computer Science education. A CTO once told me that the road ahead was going to be steeper for me than those of others with a CS degree. This situation caused me to believe in over-compensating in knowledge and creating a massive craving for constant learning. Along the way, there have been bosses and co-workers that have taken the (unfortunate) chance to let me know that not having a degree is a differentiator. It isn’t, though, and you should never let anyone bring you down by waving a diploma at you. If you do have a degree that is great as well; hopefully, your path will be somewhat smoother. As long as you keep learning, nothing can block your progress.

    Interviewing is a challenging thing to do well. None of these stories should reflect in a negative way to the people that caused them (if you happen to recognize them!). In 2010 I had started looking for a new job, with only a year and a half experience as a software engineer working in a small start-up. Before that, I’d worked as a System Administrator for three years. That right, there was my whole experience in technology: three years as a System Administrator and a year and a half as a Software Engineer. With perseverance, I managed to get two solid choices; one of them was with Mozilla. Firefox was one of my favorites things since it came out, and I was beyond static to talk to Mozilla. Phone interviews went OK, and the one odd part I remember is that I got asked about dealing with callbacks in JavaScript even though the position was for Python. I fumbled forward and managed to get set up for a trip to the Mozilla offices in California. The hotel I stayed in was popular with candidates for other companies, too; I could spot people preparing for interviews at Google and Apple. I was beyond ecstatic because Mozilla represented a great opportunity also. It was a full day of meetings, and it all started at around 9 a.m. with some introduction to Mozilla and the role by the hiring manager. The whole day divided into one-hour interviews with a break for lunch.

    The first interviewer was someone I already knew, the author of essential web-related Python libraries, including a significant framework. He went straight to the whiteboard and asked me how can I scale a database with millions of requests coming in, and to start drawing an architecture that would solve the problem. Most interviewers want to understand your problem-solving abilities, not that you actually can draw a few lines in a whiteboard addressing the scalability of a massive application in a few minutes. At least, that is what I thought, and so I started trying to explain my train of thought as I went along. The example was a primary database, with some replicas, and adding more models as load increasing, perhaps splitting read access versus write access. I thought I was doing well until the interviewer started adding more constraints: “No, no, no. This example is not a read-intensive application; it is “writes” heavy. Having read-only replicas doesn’t help, we need to scale the writes, not the reads”.

    The point though, is that this is such a tremendously complicated problem to solve that it is how cloud providers like Google, AWS, and Microsoft make tons of money. I kept pushing forward until the hour was up, and that interview completed. It felt terrible. As I said, this is quite the problem to solve, and even though I had some database scaling in my head, I wasn’t able to do good enough for the interviewer, who, as I mentioned, was a very senior Python developer.

    That interview followed another one by another very famous Python developer. In this case, he built testing libraries. He had an envelope with a stack of papers, which he is starting going through trying to pick one. “There we go, we can use this one… FizzBuzz!". I’d never heard of this fizzbuzz thing, and started getting worried about what was coming up. The premise of the fizzbuzz challenge is that you write a program that prints a range of numbers (one to twenty in my case), and for multiples of three, it should print Fizz and for multiples of five it should print Buzz. Mentally blocked, I stumbled through, trying to write something that would work, completely unsuccessfully. After a few minutes, the interviewer asked me if I was familiar with the modulo operator in Python, to which I replied that I’ve never heard of it. It turns out that this operator in Python is the one required to solve this challenge easily. It solves the question if a number is divisible by another by returning 0, otherwise returning the same number:

    >>> 3 % 3
    0
    >>> 3 % 10
    3
    >>> if 60 % 3 == 0:
    ...     print('Fizz')
    ...
    Fizz
    >>> if 100 % 5 == 0:
    ...     print('Buzz')
    ...
    Buzz
    

    Of course, after understanding this operator, I solved the challenge in a few seconds. The interviewer was unimpressed; I was feeling terrible. He then asked me if I have ever used closures in Python. I had no idea what closures were, or if I’d ever seen them anywhere, so I said no. I could feel the whole interview derailing at that point even though I was trying to keep focused and positive. Closures, as it turns out, is nothing other than a nested function. Why some people like to call it a closure instead of a nested function is beyond me. Still, I think that in this case, the interviewer wanted to assert his knowledge over mine, with a complete disregard on how useful it would be to know that a closure is a nested function. I’d seen nested functions before, and I had no idea those were also called closures.

    After a lunch break, I came back to the last two interviews of the day. One with the Ops guy, which I think I nailed with my experience as a System Administrator and the last one with a rather young engineer. As soon as we finished greeting each other, he opened with: “I’ve been told to grill you with questions of scalability and performance, but I think that is just silly since nobody here had any experience on those before we came to Mozilla”. What a brutal statement! It made me realize that throughout the day, these hard questions weren’t that useful and that I am qualified to get the job done. I felt much better and finished that last interview with a strong footing. After flying back home, the recruiter sent me an email saying that they wouldn’t be pursuing me further and wished me luck. Again, I felt a bit puzzled; I think I did well, so I inquired further, asking if there is anything I could do, that I felt like I had done well in the interviews (well, except for the exceptions you know now). “The hiring manager thinks you are too junior and this wouldn’t work out unless you relocate permanently to Mountain View, going every day to the office”

    I tried finding a middle ground, offering to be there for a few months to prove myself and then come back home, but they declined again. My second son was a few months old, and I didn’t want to be away from my family or put them under the strain of moving across the country for a job where I a “negative addition” from the start.

    Bad interviews and experiences like the one I just described will happen, don’t let those get you down. I’ve felt more productive in environments where I valued from the beginning, and not knowing how to solve FizzBuzz has never been a disadvantage in my professional career. I would be hard-pressed to remember any one time where I’ve used the modulo operator. I still think Mozilla is a great company, and I have lots of friends that work there, and I wish them the best. Hiring and interviewing is a hard thing to do.

    Good interviews #

    Alfredo Deza

    I’ve had lots of excellent interviews too. Whenever I have a good conversation, I try to go through everything that happened in the process and learn from it. How did the recruiter handle the scheduling? What was the feedback like? Was the interview engaging, or was I forced to solve Computer Science algorithm questions in a whiteboard?

    Solving algorithms in a whiteboard (or any other online tool) is not a good metric for hiring knowledgeable engineers. When I do get asked to do one, I tend to explain that I don’t deal with these types of algorithms at all in my day-to-day job. However, I still go through the problem focusing on sharing my train of thought as I make progress so that the interviewer can see how I handle the problem.

    Have you ever met a person, started a conversation, and there was almost instant good chemistry? That is very similar to great interviews! Not only has the interviewer to be open to have a great conversation, but you (as a candidate) have to take the opportunity to demonstrate how excited you are about the chance to get to talk to someone and spend some time discussing your experience.

    My brother-in-law has a great story about going to a management program for a few days that is quite relevant to interviews. On one of the days in the program, the class was about public speaking. Public speaking is one of those things that aren’t always meant to happen when you are in front of more than a hundred people; it works wonders for talking to a single person, and it is even more relevant for interviews. The teacher of that day’s class picked my brother-in-law to stand in front of the class and give a description of what his responsibilities are at work and provide a status update on the last couple of weeks.

    After a few minutes, having completed the task, the teacher then asked him to talk for a few minutes about his favorite hobby. He happens to like kite-boarding, which is a very complicated sport (to me) that involves a surfboard attached to your feet (very much like a snowboard) and a kite that is attached to a harness around the waist. He talked about how exciting it is to go out in the sea, check the wind conditions, and sometimes seeing dolphins swimming around. The teacher had to interrupt him as he didn’t realize he kept going for more than a few minutes.

    The teacher made a pause and asked the audience if they could tell the difference between the work description and the hobby description. It was glaringly obvious. When talking about work, his voice was paused and monotone, the posture looked off, and body language looked like someone waiting at the longest line at the grocery store. The mental picture everyone has when talking about something enjoyable project’s itself outwards to the conversation and into the listeners. This example is true even if the other person is not right in front of you (for example, in a phone conversation). When I am interviewing, I try to steer the conversations to things I’m tremendously passionate about, like testing thoroughly and producing good documentation. You would be able to tell how passionate I’m about these because I start to move my arms around more, and I end up raising my voice too!

    I have a friend that is extremely passionate about HTTP. He was known at work for being the HTTP guy, and whenever someone had a question about anything related to HTTP he would be called in to give his opinion. How does he get to be known at the office for being the point of reference for HTTP? Can you imagine my friend slouching in a corner with a monotone voice? Hopefully you can see a clear picture of what that means, and how it can convey a positive impact next time you are interviewing.

    One time, my interviewer started ten minutes late, and he was yawning. Instead of complaining (even mentally!) I made it my goal to be so excited about the stuff I was talking about that this person would end up feeling energized just by talking to me.

    When the tables turn, and you find yourself interviewing someone else, you can entice the interviewee to get some excitement going on. One of the ways I do this is by asking about a personal project and to explain to me how it works or what difficult parts it solves. I’m genuinely interested, and the conversations end up being way more upbeat than talking about the last job.