If you clicked on this link you must be thinking to yourself “How in the hell can one FizzBuzz answer be better than another if it does the same thing. Fine I’ll click on this guys article and scroll all the way through his bull shit fluff article and look at the code. Then I will laugh at how this guy click baited me”. Sure I will admit this is a somewhat click bait title but this is a damn good FizzBuzz answer and I’ll share the basic and best code right here at the top of the article for you.
Typical FizzBuzz code
# Typical loop for i in range(1,101): if i%3 == 0 and i%5 == 0: print("FizzBuzz") elif i%3 == 0: print("Fizz") elif i%5 == 0: print("Buzz") else: print(i) # Interesting list comprehension # using string concatenation [print("Fizz"*(i%3==0)+"Buzz"*(i%5==0) or i) for i in range(101)]
The BEST FizzBuzz code
# Our extensible list modulo_list = [ (3,"Fizz"), (5,"Buzz") ] for i in range(1,101): print_string = "" for mod in modulo_list: if i % mod == 0: print_string += mod if print_string == "": print(i) else: print(print_string)
Now you’re really flabbergasted thinking what the heck is that crap and where is the print(“FizzBuzz”).
How the FizzBuzz code works
Hopefully now that you’ve seen the code you’re intrigued enough to wonder why I did it this way. First we should clarify our assumptions that any combinations of numbers that are divisible by our inputs can be joined together using the smallest number + the larger number. So if a number is divisible by 3 and 5 it would be Fizz + Buzz. This is where print_string += mod[i] comes into play to join our values together since line 9 loops through the modulo list in the smallest to largest order we provide. Now that we’ve stated that, here is a basic walk-through of the code.
- Define the modulo list which will check for the 3 and 5 divisors
- Iterate through the list output using range(1,101)
- Initialize an empty string which we will print after checking the divisors
- For each number up to 100 check if that number is divisible by the numbers defined in the modulo list by iterating over that list and checking N % mod == 0.
- If the number is divisible by one of those values than join it’s print word defined by mod onto the output string
- If the print_string is empty then the number was not divisible by anything we defined in which case print the number
- If the print_string was set by something because the current number was divisible than print the print_string
Why this FizzBuzz code is the best
One word: Code Extensibility
In short, code extensibility is the ability to write code that allows you to add more functionality without needing to delete and/or write a lot of extra code.
In software development and coding, very seldom will code you write stay constant as your software ages and I tell this to everyone I mentor because not only have I experienced this first hand I also hear it from everyone I work with that is more experienced than me.
Code extensibility is so important that many top tier software companies have interview sections just to test how you think about code extensibility. This is something that grinding hundreds of LeetCode problems will not train you to do. Coming into that problem with a LeetCode mindset is a surefire recipe to leave a less than stellar impression on your interviewer and this is where LeetCode grinding can actually over-fit an aspiring engineer to the interview process.
Let’s go back to our FizzBuzz example so we can apply this to something simple to understand. The original problem asks to check a number is divisible by 3,5, or both and print something for each case. In the basic case this end up being 3 conditions to check and a default case which is not too hard to deal with.
Now imagine getting this problem in an interview and after implementing the basic solution the interviewer asks you to consider the case where if a number is divisible by 2 to print “foo” and if a number is divisible by 7 to output “bang”. Also if a number is divisible by 2 and 3 the output should be “foofizz” and if a number is divisible by 2 and 7 the output should be “foobang”. This should apply to every single possible combination of the provided numbers all the way up to a number divisible by all(2,3,5,7) which should output “foofizzbuzzbang”.
Each extra condition will add 2^n additional logic blocks that you would need to add as well as calculate all the combinations and what their outputs would be. With the basic logic block coding design we have created, each new number we add we add exponential lines of code and tests. We need to ensure we don’t miss a combination or we will have a “bug” that could displease our customers.
This is why it’s important to come up with code early on that can grow simply as the functionality required of our program grows with it. Even video games can have software development life-cycles spanning decades if you consider the ESPN sports games series or Call of duty and they share code through each new version and add on to it as people come up with new ideas.
Couldn’t This Be Over Engineering
On the other end of the spectrum is the problem of over engineering your solutions. Imagine your boss asks you to solve something that prints out fizz and buzz and you can either write the basic solution in 5 minutes or come up with a fancy version in fifteen minutes anticipating there will be a whole lot of other numbers and words that will come up. You deliver the code and it works great. Weeks, Months, and Years go by and the same FizzBuzz code is doing exactly what it needs to do to solve the problem your boss had and we’ve never needed to add any other words to it.
For this example you may have only wasted 10 minutes but if you look at it in terms of the ratio of time to complete is where the problem of over engineering starts to show. The fancy version took 3 times as long as to complete as the basic FizzBuzz solution. Imagine if this were a more complicated problem like what you’d see on a real job. Even a simple solution to most moderately sized software development problems take about 2 days to think through and implement a code solution for. An over engineered solution may take the software developer a week to design the algorithm, implement the code, and think through edge cases while writing unit tests before delivering code that may never actually be fully utilized. If this were a pattern for a particular developer that would account to weeks of wasted development effort over time.
As you can see over engineering is just as costly a problem as having poor extensible code. This is where it is important to spend some up front time to think through how your code will need to grow and evolve over time. If you are sure this is a one and done piece of code that will provide more value than the time to write then it’s best to deliver the code as simply as possible and at least write the code in a clear way that it can be refactored in the future.
While early on in your code you may have an urge to just deliver as much code as possible as most young software developers do. Eventually you will want to slow down and put more deliberate thought into how you write your code so that it is extensible, easy to maintain, and well documented. This will allow you to onboard new members to your application easier as well as add new features and branch out to new use cases. A practical example of this is how Facebook originally started out as a small social web application with a few hundred users to now it is a booming platform with groups, video sharing, likes and sharing, metric tracking, games, dating, and all for millions of users at once. That doesn’t happen without extensible code and talented engineers developing it.
I hope you have enjoyed this insight into ways we can improve even beginning coding problems by thinking of it as something that could need more than the basic requirements. As you work through your coding adventure try to imagine other features your code might need and how you would leave room to code those in if you needed to. Doing this exercise often will allow you to think about your code at a higher level and thus taking your coding skill with it!