Jason C. McDonald
Posted on September 22, 2018
Any time a claim starts with the clause "It's a new era...", you should automatically be wary. Modern times have brought a slew of incredible advancements in technology, but if we're honest, the proverb still holds true: the more things change, the more they stay the same.
Few arenas are haunted by zeitgeist quite as much as the programming field. We tend to select technologies by nothing more than popularity and follow design patterns blindly to our own detriment. We have absurd conversations about whether "X language is dying," and constantly debate the relevance of anything we deem "old-fashioned".
So it should come as little surprise that popular programming trends call for the dismissal of half our historic toolbox, and I think that's harming us more than we know.
A Word of Warning
Now, before we go further, I ask that you not hold this up as evidence that real programmers use Emacs, Vim, Nano, or butterflies...
Integrated Development Environments, or IDEs to us acronym-obsessed nerds, are absolutely incredible tools. I'd go as far as to call them essential, indispensable tools in nearly any modern programmer's toolbox. If you haven't found one yet, you really need to, especially as there are hundreds to choose from: Visual Studio Code, Atom, Brackets, Eclipse, Emacs, anything from Jetbrains...
Going one step further, I'd actually say there is absolutely no excuse not to learn and use at least one IDE on a regular basis, especially in writing production code. Even the clunkiest terminal-only dinosaur is capable of running Emacs. If I hear tell of anyone shipping new production code written solely in Notepad, in lieu of an available IDE, I will personally smack them upside the head with their source.
Now, with that out of the way...
The Dark Side of the Power Tools
If you've ever done any woodworking, you know how much of a timesaver an electric drill and power saw can be. It would be rather ridiculous for a professional contractor crew to have access to these tools, and not use them.
However, that same contractor crew is almost certainly capable of doing the same work without power tools. In fact, I'd guarantee that they have hand saws, clamps, screwdrivers, hammers, sandpaper, measuring tapes, and levels in their toolboxes. One might wonder what the point is. If we can do it quicker using the automatic tools, why bother carrying these manual tools?
There are two very good reasons that I know of:
Power tools fail! You might not have access to the necessary power, or be in a situation where it isn't safe to use the tool (such as in the rain). There are a handful of rare, but very real, situations that hand tools are needed.
Hand tools are actually easier in some cases! Precision work is difficult with some power tools. The very thing that makes a power tool useful - power - can actually make the job harder at times. Hand tools offer more fine-grained control.
Besides these issues, however, is a deeper issue: the same skills are needed to use both manual and automatic tools effectively. Sure, someone who has no knowledge whatsoever of how to use a screwdriver can appear proficient with a drill, but chances are, their technique will ultimately result in a lot more stripped screws. Someone who cannot use a handsaw might easily use a power saw table, but their inexperience with cutting accurately will make for more mistakes. Power tools allow you to make bigger mistakes quicker.
Every carpenter must be able to measure, sight, select tools, and control those tools, in the same manner. The slower, more manual process of using the hand tools trains those instincts. Once proficient, that individual can make the most of the power tools, where the combination of the tool's conveniences and the worker's instincts results in quick, efficient, accurate work. Power tools can contribute speed and accuracy, but they cannot compensate for a workman's incompetency.
The Programmer's Power Tools
Many people are shocked to learn that I summarily ignored Intellisense and autocomplete for nearly a decade. I learned to debug as quickly and effectively with a few well-placed print statements as with a debugger. I actually wrote and compiled practice code in the command line!
All that might sound like wasted effort on my part. After all, Intellisense, debuggers, and featureful editors are essential tools! Why wouldn't I avail myself of them?
The fact is, I knew I could learn how to use those tools in mere minutes. They were available to me, and the day would come when I would choose to employ them constantly. These are the power tools we have in our toolkits, and they're all wrapped up nicely in the IDE, alongside dozens of other useful features. Indeed, that day has come for me: I virtually never write C++, Python, HTML, or CSS without my IDE of choice.
However, I've reaped rare fruits of my early decision to abstain from the power tools. I can write a decently complex chunk of code in a text editor, and have it work on the first or second try, with few (if any) logic errors. Adding the convenience of an IDE to my already honed skills means that I frequently write entire classes that work on the first shot.
In training young programmers, I've discovered that their early reliance on IDEs have robbed them of many of these skills, resulting in code that is seldom stable, clean, or efficient on the first half dozen attempts. If they continue in this manner, their growth is stunted.
There are essential skills we can only gain and refine by writing code without an IDE! This doesn't mean we don't use IDEs on a daily basis, but it does mean that deliberately honing this skill allows us to use IDEs at full efficiency.
Text Editor Coding
I wish every programmer, at some point in their career, would undertake writing an entire programming challenge solution in Notepad, with the added requirement that it compile and work on the first try. No syntax highlighters, no Intellisense, just the editor and the language documentation.
There are several skills this trains:
Grok the Syntax
You have to actually memorize major syntactical rules of your language; this, in turn, requires you to understand the why of those rules. Knowing "why" invariably produces better code, in any language.
You can certainly look up the boilerplate and syntax every time, but sooner or later, the sheer inconvenience leads to you understand what in the hey int main(int argc, char* argv) { //... }
even means! Once you know that, you can actually start using it to your advantage. Patterns and solutions reveal themselves to you. Language behaviors become part of your instinct.
Spot the Errors
You train your eyes to spot typos, including those that syntax highlighting and your linter normally point out for you. Later, those tools merely take a majority of the workload off your editorial eye, freeing you up to spot the peskier errors that the IDE misses!
When I was working in ActionScript 3.0 several years back - a language I had never learned to write without an IDE - I forgot a semicolon in a loop, and created an infinite loop in a particularly pesky part of my codebase. It took me two weeks to find it! By contrast, after years of practicing writing C++ without Intellisense and linters, I can find the same sorts of loop errors in mere minutes.
Type In Style
Messy code is unmaintainable code, but the IDE's code formatter often allows us to get complacent about our spacing and brackets. Laziness is quite virulent, and habitual failure to properly indent can quickly turn to failure to properly spell function names right. (This isn't even remotely theoretical - I've observed the exclusive pairing of these lazy habits in nearly a decade of mentoring programmers.)
By stripping away the code formatter, we are forced to reap the immediate consequences of our own sloppiness. We have to suffer through eye-tiring rereads of our code, until we are forced to refactor the style just to make it readable. We learn to take pride in a clean, well-styled source.
When the IDE is reintroduced, it merely saves us the extra keystrokes of proper indentation and bracketing. The code formatter becomes a mere janitor, cleaning up after the occasional typo. Meanwhile, our style habits have now spilled over into the areas the IDE cannot help us with: naming conventions and standards compliance. We're in the habit of doing things right!
Old School Debugging
Debuggers are an utter lifesaver for which I am daily grateful! However, you'll never hear me say "I don't know what I would do without gdb and valgrind". I do know what I'd do, because I've trained myself in the forgotten art of debugging without a debugger!
Now, I absolutely don't recommend that these habits be used in place of your language's debugger tools. That would be, frankly, moronic. Debuggers exist for a reason, and there's no excuse not to know how to use yours! At the same time, debuggers are still a power tool. Being able to debug without them means we can debug far faster and more efficiently with them!
Which would you rather do: spend an hour stepping through your source with a debugger, or spend ten minutes stepping through your source with a debugger?
Desk Checking
My good friend and fellow coder, Chris "Fox" Frasier, spent a good part of his younger years working in FORTRAN, COBOL, Assembly, and punchcards. In his era, debuggers were barely a dream. If you wanted to debug your code, there was only one way: desk checking.
Desk checking involves reviewing a printed copy of your source, and actually being the computer. You step through it in the same manner as the machine, noting the value of all your registers and variables as they change. A steady practice of desk checking leads to us suspending our own mental shortcuts for the pure logic of the computer.
This is truly a lost art, but it's one we need to reintroduce! I've done desk checking nearly as long as I've done coding. As a result, I am able to manually allocate, manage, and deallocate raw memory in C and C++ with very few errors, and debug nearly all of the errors I do get in a matter of minutes. I've learned to think like the computer. Memory management, caching, conditional branching, order of operations, data casting...all of this is just instinct to me!
Of course, I use a debugger regularly, but my desk checking abilities have merged seamlessly with it! As I step over and into code, I'm running the logic in my own mind, and can often find the problem in minutes. Sometimes I don't even need to run the debugger at all - one careful read of the function reveals the problem.
Flowcharting
It's unfortunate that flowcharts have fallen out of favor with so many coders. Perhaps it's because they've been treated as an end, instead of a means, but I wish more coders would learn to use them to their advantage.
A good flowchart can help with both preventing and debugging errors. It takes the non-linear computer logic out of the limiting, linear structure of the source code, and puts it into a form we can process more easily. Even if one doesn't follow the "official" symbology, a quick flowchart on a whiteboard or notebook can untangle some of the most pesky logic errors.
Print Statement Debugging
I don't think this technique gets enough credit, honestly. While many young coders (unwisely) use print-statement debugging in lieu of a proper debugger, it doesn't nullify the potential usefulness of the technique.
In essence, print-statement debugging is really just a form of I/O Debugging, the earliest form of testing. In the era before debuggers, this was the other tool a programmer had, besides desk checking. Fox has described sessions just watching the lights blinking, and inferring the logic error from that data alone!
There are some bugs I've chosen to chase down using a few well-placed print statements, instead of firing up gdb
. This wasn't laziness or ignorance, but a deliberate desire to approach the situation in a different way than what I was used to.
Mind you, I wasn't just dumping every piece of available data onto the command line. I'd do a quick mental desk-check, figure out where the logic was probably breaking down, and then writing a print statement to show me the relevant variable values and function call order. I was forcing myself to select a subset of information with which I'd solve the problem. This is in contrast to a debugger, which just hands you everything. Then, given the output, I'd have to run the logic in my mind and infer the issue.
Was it harder? Absolutely! But the effort has paid off. Now when I use a debugger, I can zero right in on the information I need, and figure out what's wrong in far less time.
This has also trained my test-writing habits. Having done plenty of this sort of debugging, I have a better sense of what is important to test, and what is best left to the background. After all, testing absolutely everything is a tremendous waste of time and effort.
Command Line Compiling
The last skill I've gained from working with an IDE is the necessity of compiling or running the code myself. This applies to any language, whether you're building a C++ binary, or executing a Python module. By running the code manually in the command line, you learn how the build/run process inherently works.
Knowing the steps involved has saved me considerable time in writing portable build systems, as well as in making decisions about languages, libraries, tools, and build options. I can say from experience, there is nothing worse than completing a project, only to discover that it's impossible to package the binary on a targeted platform because of one of your dependencies!
Actually, this is one thing I wish I'd learned sooner. I had often compiled or run single-file programs in the command line, but it wasn't until recently that I actually tried building anything larger that way. Let me tell you, it's an entirely different process!
Even if you ultimately use your IDE and automatic build system for your production builds, you should challenge yourself to compile an entire large project on the command line at least once. Now that I have that experience, I can diagnose nearly any build problem in mere moments.
Reintroducing the IDE
I hope you're now convinced that writing code without an IDE develops many valuable skills and habits. However, I must once again stress that I am in no way advocating for eschewing IDEs! These power tools are incredibly useful, especially when paired with the skills we gain by working without them!
There are a couple of situations where our lack of IDE-reliance comes in handy:
You may not always have your IDE handy when you are called upon to fix an urgent issue. Imagine your build has fundamentally broken, and the chain of responsibility means that you need to do something about it right now. You don't have the luxury of installing IntelliJ IDEA on your Uncle Steve's desktop; you need to bring up a private browsing tab (so your creds don't get stored), log into GitHub, and fix the fundamentally broken function that somehow got pushed to live prod. (Yes, this is a rare situation, but I've had this happen to me two or three times in my career). If you're IDE-reliant, you're entirely useless in this case. But if you know how to write the code without the IDE, you are capable of editing straight in GitHub, fixing the problem in less than ten minutes in one shot, and mitigating major disaster or inconvenience.
In some cases, the IDE just gets in the way. Sometimes, I actually prefer printing off my source code, settling into my armchair with a cup of hot tea and a red pen, and just desk-checking it. Not only does it give my eyes a break from the screen, but the change of environment and tools help me see the problem in a different light. I might even solve it faster than if I had used the debugger!
Most of the time, you'll still use your IDE, but I guarantee that those additional skills will allow you to write cleaner code in less time. With the added features and safety nets - syntax highlighting, linters, Intellisense, autocomplete, debuggers, and the like - your already good code has the potential to be truly great code.
Posted on September 22, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.