A coworker (who is new to our codebase) recently asked me to add print statements, specifically console.log()
statements (as we're using React Native), to our codebase wherever an "important event" happens. He explained that this would help him understand the codebase better.
My knee jerk response was "Absolutely not!" Yet, I never sat down and fleshed out my thoughts on why I am against print statements as permanent fixtures in a codebase. Well, here it is.
What This Blog Post Is
This blog post is my argument against allowing solely informational print statements in a codebase's main development branch.
What This Blog Post is Not
This blog post is not an argument against using print statements while you code. Print statements are invaluable when writing a feature or debugging existing code. They provide a glance into what exactly is happening as your code runs.
Additionally, this blog post is not about using print statements to log unhandled errors. Error print statement must notify developers of any unhandled errors. This makes them a lot more useful than mere informational print statements.
Lastly, this blog post is not about keeping print statements out of production. It goes a step further and argues that it should never be part of a codebase's main development branch.
The Negative Effects of Print Statements in Your Development Branch
⛷️ Slippery Slope
Allowing print statements into your development branch is a slippery slope. Are all and any print statements allowed to be merged into the main branch? If not, which ones are allowed and which ones are not? What makes one subjectively useful print statement better than the other? Who decides where to use print statements? If the team decides where print statements are allowed, how much time and resources should be spent on this decision? Where is the documentation and list of good 👼 vs. evil 😈 print statements kept? Who will maintain said documentation?
By now, I imagine you understand my point. A seemingly trivial decision leads to a lot of additional overhead for a development team.
🤯 Anxiety and a Feeling of Being Overwhelmed for New Developers
Seemingly random print statements overwhelm and distract developers who are completely new to a codebase.
As random objects and strings run across the console, anxiety and impostor syndrome may creep in as newer developers, especially those that are junior, may feel they'll never understand an existing codebase
Additionally, these print statements entice curious developers to find where they stem from and why they are firing (OK, maybe I'm speaking for my distracted self here) even if they have nothing to do with the feature they should be developing or the bug they should be fixing. This, of course, leads to lost development time (though you can argue that this sparked curiosity leads to a deeper understanding of the codebase).
🙉 Noise
Every extra print statement added to the codebase is another print statement that one or more developers will ignore sooner or later. As a print statement ages, its usefulness takes a nosedive. Eventually, even the developer that added the print statement to the codebase will find it useless. What to do with all this noise? A developer now needs to decide which, once "useful," (see: good 👼 print statements) print statements must stay and which, now "useless," (see: evil 😈 print statements) must go. Then, they must make a pull request to remove these "useless" print statements. The garbage collection of print statements becomes a recurring task that never ends.
Additionally, "useless" print statements in your codebase leads to...
💔 Broken Windows (and Broken Hearts, maybe)
Imagine a codebase filled with print statements that many perceive as "useless". They are added carelessly and never cleaned up before going into the development branch (because, hey, print statements are allowed in our codebase now). These evil print statements become technical debt that must be cleaned up (as they are now noise). In an ideal development world, this technical debt is cleaned immediately because this seemingly small problem can lead to big problems: one part of the codebase that goes unfixed leads to another part of the codebase that is left unfixed leading to another. Eventually, a small problem snowballs into many small problems that snowball into one ugly codebase that is a drag to work with.
This is the broken window theory. Don't let print statements break your heart and lead to a codebase full of broken windows.
🐛 A Codebase that is Harder to Debug
We've established that the console is a mess due to all those 😈 evil print statements that are yet to run through the human garbage collection process. Every click of a button leads to dozens of useless print statements.
Now, you need to develop a new feature and want to add those useful print statements (that every developer uses) to ensure that a function runs properly, an HTTP request is properly made and the server response complies with the data contract. You add your print statement which is now caught in the noise of a bunch of useless print statements.
The codebase is harder and more time-consuming to debug as new print statements must be located in a sea of other print statements. Anything that makes a codebase more difficult to debug is a huge negative (as this leads to more bugs in the code).
❌ Mistaken Logs in Production
With dozens of print statements in your codebase and many being called multiple times, your surface area for a big mistake increases. More specifically, the chances of logging a real user's personally identifiable information (PII) increases.
That said, if print statements are allowed in your codebase, you must strip them before going into production. Of course, this is one more piece of maintenance that must be done to support your dear print statements. 😴
Conclusion
A decision as innocuous as allowing print statements in your main branch may have huge effects on your code. Of course, every codebase and development team is different. Take the time out to analyze this decision (and all decisions!) with your team.
Code safely.
Top comments (6)
I agree with a lot of this.
As an alternative approach, I created IOChannel (part of PawLIB, our C++ utility library). It combines logging and console output into one cohesive system...
You can set categories (error, warning, debug), and toggle or route messages based on those categories.
You can set priorities ("verbosity") on messages, for one-liner, or even real-time, control of what actually gets output. This also lets you control what priority gets sent where.
One message can go to multiple output sources, and console output can be easily toggled.
"Ignored" messages aren't even parsed, so the performance impact of leaving them in is negligible.
IOChannel uses CPGF signals/callbacks (good performance) for routing messages to any custom function, so it can be combined with any other logging, user interface, or console library or tool.
My logic behind this was that sometimes you need to write particularly complicated "print statements" that will be needed for debugging more than once. This allows those messages to stay in, and actually be useful.
Of course, what messages should even live in the code is really case-by-case, especially considering everything you've already pointed out. Just because IOChannel allows debugging messages to live permanently in the code base doesn't mean it's a free-for-all. ;)
P.S. The PII situation gives me an idea for a later version: the ability for the user to specify "message hook functions" that messages pass through either before or after parsing. Because all messages would be forced through the message hook, it would be simple to scrub all messages in one step. (Remember, you can also toggle all messages off in production, without losing them in development.)
It's a codesmell for sure, use logging for prototype work then implement some sort of file logging. At work we use a long rule to disallow console.log.
Unrelated but did you know about console.table ? Nifty little trick.
Unrelated, your co-worker (and anyone else new to the codebase) would probably benefit from Commenting Showing Intent instead of print statements. I can personally attest to the direct ROI in time and effort my team has gained from it.
To Comment Or Not To Comment?
Jason C. McDonald ・ Jan 20 ・ 12 min read
...that said, if he's learning said code, it may be best for him to CSI the part of the code he's studying! (It should be subject to code review, the same as anything.)
How exactly your team would need to implement CSI, and how ambitiously, is something only y'all can determine. But I think you'll find it will eliminate most cases of "I can't understand the code base".
Agree 100%, print/console.log is only a temp debug tool and does not belong in your Git repo. Always review your changes before you commit, and take the print statements out.
I don't think there's anything wrong with having debugging statements in your repo, as long as they're in a feature branch, but you should clear them out before merging back to the main branch (usually
develop
).There shouldn't be a fear of committing anything, because committing isn't just about sharing your work with the project, it's also for you as an individual. If you go home and work from a different computer, you're going to want the changes you made earlier in the day.
You can squash your commits if you don't want any record of your work-in-progress or debugging attempts to appear in the VCS history.
Agreed!