Articles about advanced Swift development in excruciating detail, for free!
How necessary are the programming fundamentals?
(Reworked on 16 Jun 2022)
I've been meaning to write an article about computer science fundamentals and how it can improve a programmer's career for a long time, but I always had trouble finding a good way of introducing this topic. The reason I'd like to talk about this is first that, well, I really like this field and I feel that most of my programming ability today is a result of having studied this, but also because I feel like there's a lot of misinformation around this topic and I've been hoping I could address that in a way that could be convincing even for those who can't stand hearing the word "algorithms" anymore.
The context, for those who are unaware, is that part of the tech community despises this topic so much that they actively fight against it. The reason for that is due to how the interview process of large companies like Google is shaped; When regular-sized companies hire programmers, they usually look for people to perform a specific role on a specific platform (for example, iOS Developer). In this case, it's very common for the interview processes to focus on the practical aspects of these platforms, such as understanding how the platform works, the details of certain APIs, and the person's overall ability to work with that specific platform.
Traditional large companies, however, focus more on the theory of programming, asking questions not about what you can do in a specific platform, but about your understanding of the building blocks that allow such platforms to exist in the first place. This includes understanding the functionality of basic data structures like
Sets, your ability to predict, often in an academic fashion, the performance of your code, and your knowledge of fundamental algorithms like a binary search in the shape of a programming puzzle. The knowledge used in those puzzles comes from a field that has multiple names, with the most common ones being Algorithms and Data Structures and CS Fundamentals.
The thing though is that these companies tend to run puzzles that are so hard that not even extremely competent engineers are able to answer them. They often require you to have extensive knowledge of very specific pieces of theory that are almost always completely unrelated to the position being interviewed for, and while people used to be relatively unbothered by this in a past where we had way fewer layers of abstractions and the only way you could learn engineering was by going to a traditional college that would quite literally force you to study those concepts, this practice became considered unfair and outdated in the modern world where most people tend to learn from bootcamps and work with way higher layers of abstractions, and is now heavily frowned upon by the tech community.
Bruno, it looks like you agree that this is an outdated practice. So what is it that needs to be addressed?
The problem is that the community often misplaces this hate in problematic ways. The negativity around this interview format became so large that you can now often find people feeling resentment towards the theory itself, which is something that has nothing to do with obscure programming puzzles. Although on one side this is a really funny misunderstanding, this became so popular of a thing that nowadays it's almost impossible to mention a programming concept at an abstract level without someone immediately derailing the conversation into a flame war about interview practices, which is something I believe to be hurtful not only for these conversations but also for the individual careers of engineers who are being told to stay away from this knowledge for reasons that are not logical.
I believe it's very important for the community to separate the knowledge from the interview practices. As an attempt to solve this misunderstanding, I'd like to use this article to give you some information on what the theory is truly about. We'll first look at a short introduction to the field of algorithms and data structures, proceed by visualizing how this knowledge is applied in practice using an analogy, use that information to clarify why large companies care about this knowledge (despite the fact that they unfortunately do it very badly), and finally wrap-up by using what we learned to see how we can write better interview processes. I hope that this will give you the tools to better separate in your head the field itself from these companies' bad practices, allowing you to not only understand why this knowledge can be useful, but also to help you determine if this is something you should be studying to achieve your personal goals.
Separating the knowledge from the bad practices
Here are some common complaint points you hear in discussions about fundamentals:
- Theory doesn't reflect what the person will actually do in their job.
- Knowledge of the theory is not indicative of the person's skill in a given practical role.
- Theory, in general, is pointless. Why does an iOS developer need to know what a graph is?
People who feel this way then claim that this field is something that only academics should worry about, and practical programmers should not have to deal with this as part of their daily jobs.
I think anyone can see why someone would have these views. If you have never been introduced to this field, it can definitely look like it's all about pieces of obscure knowledge that aren't relevant to the actual role. They are called fundamentals, but you can learn how to code without them. Are they really that fundamental?
What's important to be aware of is the role this knowledge plays in your programming ability. Let's take a look at the third point again:
- Theory, in general, is pointless. Why does an iOS developer need to know what a graph is?
Many people today believe that the purpose of theory is to teach you extremely specific structures and techniques that are pointless for the majority of engineers. As mentioned before, I believe it's imperative for you to separate the concept of programming theory from how the infamous interview processes require you to be able to recite unrelated obscure knowledge from the top of your head. This belief is popularly perceived as being true because of a misleading assumption that these are one and the same, while in reality theory has nothing to do with solving problems in obscure ways.
The role the theory plays in practice is by being a reference for someone to determine how a certain piece of code they're writing should be designed. If this sounds familiar to you, then it's because that's how most of programming works! This is certainly something you do all the time, with the difference being just what you're using as a reference. To put in other words, people don't study generic structures and algorithms so that they can go and start literally adding them to their projects like the interview processes like to imply -- they do it because most of the problems we face today are solved by writing code that is essentially an abstracted version of those same fundamentals, and being able to notice that can give you many insights on how your code can be designed and behave.
This implicitness gives people the impression that the fundamentals are useless, while in reality, they're using them all the time! I cannot tell you how many times I've heard an iOS developer say "I can safely tell you that never in my job I needed to use a graph" and then proceed to happily talk about something interesting they worked with involving
UIView hierarchies. They are the same thing! Objects that can connect to each other with the intent to create a single connected map of elements is the literal definition of a graph, so not only do they know what a graph is, but they have also been using them since their very first day as a developer!
As mentioned in the previous paragraph, the way this "awareness" comes into play is that noticing this pattern would be of great help when solving problems involving
UIViews -- while you can definitely solve these problems by yourself, a person that realizes that views are basically graphs with a funny name will be able to reference back to decades of solved problems in this area when trying to determine the best solution. If you need a concrete example, let's pretend you have a problem whose's solution requires you to locate a specific view in the hierarchy: while you could probably come up with your own recursive method to do so, the different ways in which you could locate a node in a graph is one the topics in graph theory with the most resources available on the web. If you're able to draw this parallel, your ability to solve this problem will be greatly enhanced.
In iOS specifically, you can draw these comparisons with many of the concepts people claim to have no knowledge of:
- Linked Lists:
- Hash Tables:
- Bit Manipulation:
It can be quite common for developers to not make this distinction between what part of their knowledge is platform-specific and what is actually a platform abstraction of a fundamental structure/algorithm -- you as a professional programmer who never formally studied the theory very likely know a great deal of it, it's just that you haven't yet studied the part where you learn how to describe this knowledge in a platform-agnostic fashion.
I hope that this example is already giving you a good idea of how the theory is applied in practice and how it has nothing to do with asking people to write obscure data structures on the fly, but we still have a couple of important questions to answer. Let's start with the necessity of knowing this information: Given that we know that developers can do just fine with not having this enhanced ability to visualize their code as generic structures, is this something that you truly need to study to achieve your goals?
Analogy: Becoming a professional musician
To explain this, I would like to draw an analogy with a person's journey of learning a musical instrument. This is because musical instruments are accompanied by music theory, and I find the relation between them to be very similar to the one between programming and algorithms.
21-05-2021 Update: There were very interesting discussions about this article on HackerNews on how to form an analogy between these two concepts and actual musicians commented saying that a classical orchestra would be a better comparison. I updated the analogy -- thank you very much for your inputs!
In music, music theory is the study of the practices and possibilities of music. It seeks to define the processes and general principles of music, but also the concepts that define what music itself is, defining exactly what is a note, the theoretical definition of a chord and how it can be manipulated, how chords can be grouped into keys and how musical progression works.
A person who wants to learn an instrument like the electric guitar and is doing music lessons will be introduced to the concept of music theory, but also be told that they don't really need to learn it to learn how to play the guitar. Although they will need to learn the basics of notes and chords, there's no need to get into the deeper complicated details unless the person happens to be interested in that. The learner will be perfectly capable to learn how to play their favorite songs, and maybe even play in a cover band with their friends. As we've seen above, this is exactly the same scenario we see in programming.
In the case of music, the details of music theory start becoming more relevant when the person starts wanting to compose their own songs. Although this is still not a critical requirement in any shape whatsoever, a musician without knowledge of theory might have a harder time composing when compared with someone who does know it. This is because concepts like chord theory can give you many insights on how to achieve particular sounds and the progressions that include them, so although you can still do perfectly well without this knowledge (and we know many famous musicians who have done that), you'll be placing a lot of unnecessary responsibility on your raw musical talent that could be used much more efficiently if you had the ability to describe your knowledge in a logical/agnostic way. We can say that composing songs is the more explicit application of music theory, which could be comparable to our previous example of how our developer trying to solve a
UIView problem would be better geared to do so if they notice that they can be reduced to a generic graph theory problem.
However, music theory has also an implicit application, which is that people who learn music theory are great musicians in general. Even though the person might not be composing their own songs, their understanding of music likely makes them very comfortable playing and improvising any kind of song. These people are usually extremely skilled, being able to learn how to play new songs by ear without ever needing to check how the band that made the song actually plays it. The logic is simple: they don't need to, because they understand the logic behind what they're listening to.
Another implicit benefit of music theory is that it applies to every instrument. People who understand music theory usually have a much easier time mastering different instruments because most of their knowledge from previous instruments can be applied to new ones. All they need to learn is how the instrument is played.
Those same implicit benefits can be seen in programming. While a musician that knows the theory is generally better at understanding and composing music, a programmer who knows the theory will generally be better at understanding and writing code due to their ability to quickly draw parallels between the problems they need to solve and the programming fundamentals that explain the ways in which these problems can be solved, regardless of the platform in which the problem is present.
It's important to understand however what it's meant by "solving" in this case; this doesn't mean "this code must use the ultra-fast MegaPlutonionSort algorithm that my dog authored in 1970 and never told anyone about, otherwise it's wrong and you should quit", but simply that the code you're writing makes sense from a design, performance and resource management point of view. This is probably a good time to reiterate the article's main message: Using theory to solve problems has nothing to do with obscure puzzles and algorithms!
If this doesn't click for you, think of how "correctly solving a problem" applies in the musician's example: Even though two musicians might be playing the exact same song, one of them might have really bad posture and a choice of chords that is all over the fretboard. The other musician however has learned the proper posture, and their knowledge of theory allows them to find the exact same chords in much more comfortable positions. It's the same song with the same result, but one of the musicians will have a much easier time playing it, while the other one will struggle and likely end up with tendonitis.
Who really needs to learn the theory?
If we go back to the music example, we can say that the necessity of studying music theory in a musician's career will heavily depend on what the musician wishes to achieve:
- Do I want to learn it as a hobby, and never go beyond playing in my couch for fun?
- Do I want to play in a band, and solidify myself as a musical artist?
- Do I aspire to go beyond the mere title of a "musical artist" by living and breathing classical music, becoming an integral part of the Vienna Philharmonic, traveling the world, and going down as a legend that literally shaped the concept of music itself?
It should be clear that our two non-orchestra-dreamer-fanatics don't need music theory, as they can definitely achieve everything they want without it. They can still benefit from it if they want -- learning it would allow them to master their instrument, as well as open the door to every other musical instrument. However, from a pure career necessity standpoint, we can safely say that the theory is just a bonus thing they could learn to be a better musician.
The dreamer, however, has a completely different objective. This person is not looking to simply have fun, they want to be part of a group of people who dedicate their entire lives to perfecting music as an art form. A famous orchestra will obviously not accept some random joe who picked up a violin weeks ago -- you must at very least be exceptionally good and versatile as a musician. Even though some professional musicians might argue that even in this case it's technically possible for the person to achieve this goal without formally studying the theory, it's clear that not having a deeper level of knowledge about music would be a major setback in this person's career. In fact, orchestras are so serious that having formally studied music is often a minimum requirement.
Just like in this analogy, the necessity of studying algorithms in your career depends on what you as a programmer want to achieve. If you learned programming as a hobby and don't really want to work with it, then the fundamentals are not necessary at all. Similarly, if you see yourself working at a regular and more practical company, then it's also likely that you will never face a situation where the fundamentals would make a big difference. You could learn it to improve your ability in general, but from a pure necessity standpoint, you can surely live without them.
However, if you aspire to learn multiple platforms, work in a global tech company with state-of-the-art technology and incredibly smart people who are at the top of their field helping them literally define what tech is, then it should be clear to you that even though this is technically achievable with pure programming talent and zero theoretical knowledge (and I'm certain the community can point many who have done so), you will likely have a much easier time getting there if you channel your talent into getting a deeper and more logical level of understanding of what you do in your daily work.
Why do top-tier companies do what they do?
To understand why larger companies are so obsessed with the theory, we'll focus on the following point:
- Theory doesn't reflect what the person will actually do in their job.
When referred to interview processes this point is correct for obvious reasons; even state-of-the-art projects usually don't require having obscure knowledge on how to make a particular data structure slightly more efficient.
But before we continue, we have another point of confusion that must be separated. Just like I urged you to not treat theory and the infamous interview processes as being the same thing, I'll now ask you to take some time to separate the difficulty of the interviews from the fact that those interviews are themed around the theory. This is because these are also wildly different things; one has a legit practical reason, and the other is corporate hiring bullshit.
The reason the questions are hard and convoluted is because of corporate idiocy. These companies have so many candidates that in order to reduce the possibility of false positives and save time and money, someone decided that the way to go was to make the questions so hard and so specific that if they pass, there's a good chance they know what they're talking about. I believe this was somewhat true 15 years ago, but today, with the existence of numerous platforms that promise to quickly teach you obscure programming subjects for the sole purpose of passing these interviews, this is so detached from reality that it's hard to grasp why they haven't changed this format.
But when it comes to testing theory the itself, you have to keep in mind how these companies operate. While in regular companies you are likely to be hired to perform a very specific role as a specialist, larger companies like Google are almost always focused on engineering T-shapedness and generalism. This means that even though you might be hired to do something specific like iOS development, it's still expected that you'll have the ability to work with different platforms if needed. I work in one of these companies -- even though I work primarily with iOS, there were many cases where a task I was doing involved checking code that was outside of iOS, like a relevant piece of Android code, the functionality of a C++ library, or even writing a new data pipeline inside our backend. This is expected to the point where it's covered extensively in performance reviews, and as a generalist myself I actually really enjoy this.
I'd say that the reason why this happens is the nature of these companies -- while a regular company is trying to solve a small-scale problem that has likely been done before, top-tier companies have to deal with enormous problems that nobody has ever faced, which often requires them to be masters in multiple platforms. It's not interesting for a company like Google to hire someone who dedicated their life to understanding all about UIKit in iOS -- their problem is not that they don't know which UIKit APIs to use, is that the APIs they need don't exist at all. These problems are not solved by a person's platform knowledge, but by their understanding of computer science and their ability to craft new and efficient solutions.
We've mentioned in the music analogy how the fundamentals apply to all platforms, and this is another reason why these companies test your knowledge of them. They essentially prove that you can work with anything -- even if you never worked with a certain platform, your ability to easily draw parallels between practice and theory would likely make it very easy for you to pick it up.
This is why these companies like this field -- while in a regular company you can prove your ability by answering questions about a specific platform, in the larger companies' world of generalists it's your understanding of programming as a concept that proves you're the type of programmer they need.
Creating better interview processes
Bruno, you're claiming that the problem with these interview processes is not the concept of testing knowledge of theory, but how these companies are making the questions unnecessarily difficult for financial reasons. How would you design them in a fair and modern way?
I don't have a clear answer for this at this moment, but this is a concept I constantly experiment with and have some thoughts on some core concepts I currently believe a better process would contain.
One important thing you must be aware of is that hiring is hard. No matter how you shape it, it's simply impossible to properly evaluate a person's engineering skill in an hour-long puzzle. The only way to do that is to actually hire the person and work with them for a large period of time, and while some people do suggest that this is how hiring could be done in modern times, this would never work for several reasons. First of all, if you don't pay the person, then I doubt any sane person would sign up for this. On the other hand, if you do pay the person, then it would take little to no time for scamming rings to rise and abuse the process.
Second of all, time is expensive both for the candidate and the company. Even though we know that interviewing the person for a large period of time is the only proper way to have a legitimate view of their skills, both the candidate and the company want this to happen as quickly as possible. The company obviously wants this for financial reasons, while candidates want that because 1) interviewing is in general very stressful and annoying, and 2) the majority of people interview for multiple companies at the same time, making it important for them to be over quickly so that the candidate can start more interviews with other companies.
To optimize for these needs, companies opted to make a trade-off between accuracy and time spent. While speeding up the interviews makes everyone happy, it has the immediate consequence that you become unable to accurately measure the candidate's ability, making you need to make an educated guess instead. Because we know it's impossible to make a 100% accurate guess with a heavy time constraint, the most critical component of a good interview process becomes how you can maximize the accuracy of your guess without creating downsides on other fronts.
We know how traditional large companies maximize their guess: they make the interview as hard as possible. However, this artificial difficulty creates so many downsides on so many fronts that by this specific definition it logically cannot be considered a good process.
So what are the good approaches? The answer is not yet clear, but I can share some concepts I'm currently studying and applying:
No generic questions. Always relevant to the product
To make the process fair to candidates with non-traditional backgrounds, I believe questions should always be non-generic and related to what the job actually consists of. Some people might think that this would make you unable to evaluate a person's knowledge of deeper programming concepts, but this is not the case at all. To understand why, refer back to one of the first points of this article: people often know the theory, they just don't know it's theory. To evaluate a person's understanding of a deeper concept without making it immediately unfair to candidates who didn't formally study the theory, all you have to do is wrap the question around something that is familiar to them and make it relevant to the product.
Let's use iOS development as an example and pretend that I want to have an idea if the person is proficient at visualizing and traversing distributed components. The generic way of doing so could be to give them a generic graph data structure and ask them to search for a node, but this has two problems. First, as mentioned a couple of times, this will be very daunting for someone who hasn't studied theory. Second, most people will be discouraged from solving it as they cannot see why this is related to the product they're interviewing for.
To fix this, we can do two things. First of all, remove all the references to generic terms and replace them with something that is familiar to everyone: In this case, we could simply replace "graph" with
UIView and "search for a node" with "locate a subview in the hierarchy".
Second of all, make it relevant to the product: instead of wording it like a general problem, we could use an actual feature of the app where this is or could be a legitimate necessity as a context for why this is being asked. This second step is not always necessary as in my experience simply making it non-generic already does a great job at making people feel comfortable with solving a particular question, but I find that making it relevant makes people actually enjoy the process more as they can see exactly why being able to solve that particular problem is important for the company.
Run multiple types of questions
Coding exercises are popular because they usually provide the maximum "guessing" ability, but that's only true if that's the only type of question you're running. I find that one way you can improve your guesses is to actually run different types of questions in addition to the coding exercises. One example of exercise some companies are running are "code reviews": Present a piece of really bad code and ask the candidate to pinpoint what's wrong with it. When paired with the regular coding tasks, these alternative exercises can give you a really good view of the candidate's practical knowledge due to how relevant they are to the position.
Hiring is an unsolved problem, and there are many questions I still don't have answers to. If you have any thoughts of your own about this subject, I'd love to hear about them.
I hope this article was able to make it clear to you the role that understanding programming theory plays in a developer's career and how it's important to separate it from our resentment towards bad interviewing practices. While we've seen that almost no one truly requires to study theory in order to have a successful career, I believe all kinds of developers could greatly benefit from doing so, making it critical for us as a community to be aware of the unconscious power of bandwagoning and know where to direct our emotions in order to not incorrectly drive people away from things that can potentially take them to the next step of their careers.
While I'm certain that some people will still not agree with this article (and I'd love to know why, if that's you), I hope that this has given you new interesting insights on the practical applications of theory and what we can do to build better and more enjoyable interview processes.