I was involved in a discussion recently regarding the relevance of C in modern programming, and I found one person’s comment both intriguing and alarming.
“Please don’t tell people to learn C. It’s our grandfather’s language.”
He wasn’t the first person I’ve encountered to make this argument, and I know he won’t be the last. It reveals a dangerous attitude which is increasingly prevalent in the programming industry, and I’m not the first to notice it.
Oh lookie, a new entry for A Field Guide to Common Nerds!
If you’re among this group, please put down your pitchfork for a few moments and hear me out.
Programmaticus Trendicus, perhaps more succinctly identified as Trendites, assert that we are in a new era of programming, where the tools work for us, and we never again need to descend to those clunky, old fashioned languages of yesteryear. I will credit them with largely positive intentions – they want to save masses of programmers the pain and agony of covering old ground unnecessarily.
I won’t even touch on the various hostile hybrids of this species and focus instead on the purebreed P. Trendicus. Suffice it to say, when combined with P. Newbieflamerous or P. Militaricus, the result is the stuff of nightmares.
The Trendite’s assertions, liberating as they may seem, are misguided at best, and hazardous at worst. The basic premise of this worldview is that lower-level languages like assembly and C are no longer needed. We have moved to better, easier, and more powerful tools like Python and Java that allow us to do everything with less muss and trouble.
At this point, let me lend credibility to part of this assertion: C is clunky, dusty, and annoying as heck. This is even more true of assembly languages. Even C++, the relevance of which is hotly debated among Trendites, is an elephantine kludge compared to light and nimble “modern” languages like Python and Java.
I’m a loyal Pythonist to the end. I love that language, and frankly bemoan the fact that I get to spend so little time in it due to work obligations. In fact, I would say that if you can effectively accomplish your task with Python, Java, Ruby, Haskell, or any other language of that sort, you really should! There isn’t much point to tormenting yourself with a lower-level language unnecessarily.
With that in mind, let me also say that C++, C, and the assembly languages are relevant and essential tools in a programmer’s toolkit. I’ve even gone as far as to say publicly...
Liquid error: internal
There is a very good reason I tweeted that.
For one thing, many languages are built on top of C, and ALL are built on top of Assembly. As I mentioned in my article The Cake Is A Lie, layers like this are not entirely stable. Sooner or later, something in the foundation will crack, and the upper levels will start crumbling. Think about how many instances of bizarre, inexplicable behavior occur in those higher level languages. More often than not, it stems from some undefined behavior (roughly, a situation in which the C language has no appropriate response built in) in the language’s C source code. This undefined behavior mutates further as it propagates up through the layers, and next thing you know, Python is dancing on the rooftops in its knickers.
Let’s imagine for one moment that the Trendites succeed in their mission, and there doesn’t remain a single programmer on earth who knows C. Once someone sees Python doing its rooftop jig, who do they call? Python’s C code could never be fixed. The bug would exist permanently, and as is inexplicable tradition among computer bugs, somehow get worse.
Taking a step away from programming for a moment, we can easily draw a parallel between C and the combustion engine. The Trendite’s argument is equivalent to telling engineering students “we already have so many good engine brands. Don’t learn how to build an engine, just get a pre-built one and put it in your vehicle.”
Obviously, there are three problems with this: A) if an engine breaks, no one is left to repair said engine. B) an engine can never be made more efficient than it already is, and C) innovative new sorts of vehicles can never be invented, because no one knows how to design an engine to meet that vehicle’s unique needs.
This does not dismiss the reasonable argument that your average mechanic should not build an engine from scratch for a Toyota Camry when a pre-built engine can be purchased and installed. It would be a waste of time, money, effort, and risk (what if it isn’t built right?) to custom-build an engine from scratch. That reality does not invalidate the need for people who can build engines; in fact, that mechanic would probably be far more effective if he had built engines in the past, because he can diagnose problems and avoid unnecessary replacements.
That’s the second reason why C++, C, and assembly remain relevant. Although a programmer may spend 90% of their time using Java, Python, or the like, their knowledge of what is going on “under the hood” in C and assembly help them to write effective, efficient, quality code. The discipline of writing good, solid C code – an arduous task – carries over into all their other programming work. Instead of hacking their way around a solution, they know what they want, how it works, and exactly how to get there.
This is why I say that a programmer must know C to be considered a true professional. Python and similar languages do so much for us automagically that we can easily become flabby armchair programmers if we aren’t careful. I’ve seen it happen so many times – I’ve broken in several Java programmers, and there were a lot of lazy habits they had to conquer. Those habits were not formed as a result of any character flaw or inherent apathy. They had simply never known the habits were problematic (or present), on account of them never coding in anything lower-level than Java.
“Okay, granted, but those habits really don’t mean anything if we’re in Java or Python!” you say.
Are you sure about that? There is a profound difference between working code and good code. Working code accomplishes the task by any means necessary, but it can often be by a sheer brute force approach, which is not efficient, pretty, or easy to maintain. Good code is written to solve a specific problem in an efficient and elegant fashion, and while there are many ways to write good code, brute force is rarely one of those ways.
The little idiosyncrasies of C and the like force the programmer to think through their solution carefully, and come up with the best plan of attack. Unlike in higher level languages, brute force in these languages usually yield exceptionally slow, error prone programs that beg to be fixed. Bad habits become immediately apparent.
In short, mastering C is an effective way to short-circuit the lazy habits in our minds and establish the practice of thinking through our code. We are programming in the trust intellectual sense of the term.
One can parallel this with the idea of learning how to drive a stick-shift car. You have a better understanding of handling, and you have more control, especially in treacherous driving situations. In addition, driving an automatic only requires one to remember that the car does many things for you.
Conversely, if one only knows how to drive an automatic, they’ll likely have a harder time driving in bad conditions, and will have a far harder time learning a stick-shift later, if they take the time to learn at all.
Just as one may argue that the sooner one learns to drive a stick-shift, the better (and easier), one may argue the same for learning C. The longer one waits, the more entrenched the bad habits will be when they begin.
With the case for lower-level languages in place, let’s revisit our friend, the Programmaticus Trendicus. When fully grown, a Trendite begins to value the “trendiness” of a language or toolkit over its efficiency. They’re addicted to syntactic sugar, those little programming language features that make it easier to write code (and by extension, to write bad code). Their software is bloated with inefficiencies, and easily takes up excessive memory and disk space. In many cases, their resumÃ© is likewise bloated with the names of brand new technologies that no one has heard of and few of which will remain “relevant” for more than a fortnight. Thankfully, many Trendites are not this deeply entrenched, especially when they are young programmers. There is still hope.
C++, C, assembly, and other low-level languages are not relics of a bygone era. They are the most essential tools we have, upon which everything we rely on is built! It is only because they exist that we have the easy, trendy languages like Python, Java, Haskell, and Ruby. It is only because programmers still learn how to master C and the like that we continue to see new and better tools and languages emerge.
It’s ironic, really. In their often well-intentioned efforts to further progress, Programmaticus Trendicus is in danger of stalling progress completely.
UPDATE: I want to emphasize that there is a definite case for phasing out C as a low-level development tool for new technologies. There are alternatives, including Rust, that can be considered. However, as long as C code exists, I believe my points still stand.
If you are now convinced that C is the next thing you need to learn, I personally recommend Zed Shaw’s Learn C the Hard Way, a newer publication which approaches the language from the perspective I described – mastering C to become a better programmer in other languages.