The software development blog of Valeri Karpov

Menu

Why Math is Necessary for CS Majors

While math and computer science have been lumped together for about as long as the latter has existed, there’s a lot of backlash recently toward the idea that a solid math background is integral to being a good developer. The relationship between the two was something that I struggled to grasp as an undergraduate in Computer Science. The relationship between math and CS isn’t as direct as, say, math and physics, or even philosophy and CS. However, taking a rigorous pure math course as an undergraduate will help you significantly, whether you choose to be an ivory tower academic, a developer for the latest hip startup out of Silicon Valley, or an engineer for a big NYC bank.

The reason why has nothing to do with learning what most people would call “practical skills.” Even as an undergraduate specializing in theory and theoretical computer vision, I realized that the limit of my practical use of mathematics was a high school-level understanding of linear algebra, some basic graph theory, and whatever I needed for big-O notation. While some advanced mathematics, like Galois Theory, have CS-related applications, you probably won’t use them in CS outside of the most closeted of ivory towers. I can honestly say that, in the 8 years since I got my first software engineering internship back in high school, I’ve never had to use anything I learned in undergrad Real Analysis or Galois Theory (thankfully, because I honestly deserved to fail Galois Theory). So clearly, when I completed the required courses to graduate as a math major, I was wasting my time, right? Wrong!

The fallacy in the above reasoning is that learning CS isn’t a video game styled tech tree. Just because understanding a proof of Stokes’ Theorem isn’t a strict prerequisite for being an effective developer, doesn’t mean that it doesn’t help. As Irish poet W.B. Yeats once wisely said, “education is not the filling of a pail, but the lighting of a fire.” Similarly, learning to be a developer isn’t about crossing off a checklist of practical skills and making your resume look like a buzzword bingo board. Learning to be a developer is about practice (which is why Allen Iverson wasn’t a software developer), and what a pure math class gives you is a slightly different environment in which to practice your skills. When you have spent a little time looking at both, you’ll realize that going through a pure math textbook and remembering the correct theorems and lemmas to use in a homework proof is pretty damn similar to figuring out which modules you need to effectively add some new functionality to your codebase.

Many engineers bemoan the lack of unit testing instruction in undergrad CS curricula, they forget that unit tests are only useful if you have the rigor to write them in the first place. Not only is the process of figuring out which theorems to use an exercise in dependency management, but the process of proving a theorem is similar to writing unit tests to prove the correctness of your module. A lot of developers nowadays have copped a bad attitude when it comes to writing proper unit tests, saying that their code is trivially correct. I bet these people haven’t sat down to prove that there are no rational numbers satisfying the equation x^2 = 2 either, and I think that a solid grounding in pure math can nip this weakness in the bud. This way, Rudin’s Principles of Mathematical Analysis, the bane of every freshman math major’s existence, is essentially a large codebase for you to practice on.

Similarly, graph theory is absolutely integral to the day-to-day of being a software developer, even if you’re not working with graphs directly. Speaking of modules, to an experienced developer, a well-organized codebase looks a lot like a graph. Pieces of code are bits of logic with dependencies which are references to other bits of logic, which, of course, intuitively maps to nodes and edges. All refactoring work comes down to just thinking about graphs and how to make your code graph comprehensible. Beyond this simple example of refactoring and managing dependencies in code, the applications of reasoning about graphs in software development are endless, from breaking a bulky task down into components, to thinking about points of failure in a sophisticated network topology. While I’ve never had to think about Hadwinger’s conjecture in a professional context, my undergrad Graph Theory course gave me a lot of valuable practice reasoning about graphs in a rigorous way. This practice continues to serve me well to this day, whether I’m trying to organize my dependencies in AngularJS, thinking about the topology of my MongoDB cluster, or just figuring what tasks I need to get done today.

Bottom line, kids out there, if you really want to be a successful software developer, taking proof-based math (and proof-based graph theory in particular) is an excellent step in the right direction. It won’t be easy, but becoming good at something never is.

I have a somewhat contrived example in code here: https://www.npmjs.org/package/omni-di . A more realistic example would be a config file: you may have a service which depends on the server config service, which in turn depends on the `fs` service. Just nodes in a graph. Right now I only see that as being useful for data visualization, but I can see running some graph theoretical stuff on that in the future.