
Great Programmers Write Debuggable Code - henrik_w
http://henrikwarne.com/2013/05/05/great-programmers-write-debuggable-code/
======
emn13
Logging is not a panacea.

It's easy to swamp useful messages in tons of noise, so just logging whatever
is not necessarily an improvement. Logging means code, and more code means
more work, less readability, and more bugs. In particular, logging somewhere,
sometime means I/O, which opens up a whole new class of bugs you'd rather
avoid. For instance, I've seen several systems go down simply because the disk
space ran out.

Logging is also slow - his example of finding a cheapest path is a good
example of this. These are well-studied algorithms that explore huge state
spaces. Adding logging as he suggests can easily mean the application's
running time is spent almost entirely logging. This might not be an issue:
maybe you don't need this very often, or maybe the path's aren't very complex,
so you can get away with huge overhead because systems are so fast anyhow. But
often as not, you can't. And even when you can, you might be adding a trivial
DoS vector for your app, or you might be needlessly limiting its applicablity.

If you have an error, it's nice to log whatever you can to help understand it.
But the benefit of understanding the odd error more easily needs to be weighed
against the non-trivial costs in time, readability, maintainability, disk
space and runtime, particularly if you're pre-emptively logging before an
error ever occors.

~~~
codemac
Logging does not mean I/O.

Logging _to disk_ means I/O.

~~~
jasonzemos
Logging means a I/O operation in some way or another to observe the message.
If it's not the disk, it's a buffered stream with a syscall and all kinds of
slow. Any other magic (using another thread to do the slow stuff, etc) risks
the log message getting lost during a crash and the whole thing being useless.

~~~
perbu
You can log to a shared memory segment. You'll need a mutex on it and a
writeev call to log to it. And it will survive an application crash.

~~~
codemac
Hell - log to one shared buffer per cpu/thread, and then avoid the mutex.

Apparently when saying logging doesn't require I/O at runtime, you gotta talk
it out a lot more before people get it.

------
bjourne
I'm curious about this whole "have to be contrarian deal." Guy writes blog
post which is good advice in 99% of the situations and someone just _has_ to
bring up that, yeah, somewhere, sometimes more logging is not necessarily
better. Why always nitpick on things?

In my work, if people wrote more and better log messages I would be delighted.
They almost never do and instead rely on the Visual Studio debugger to inspect
program state. It works great when debugging your own code, but is pretty
useless when debugging someone elses code (you dont know where to place the
breakpoints) or when analyzing a crash on a production site.

~~~
colomon
The thing is, the article does not say "There are many circumstances where
logging is a great tool." Who would object to that?

However, what the article says is "One of the differences between a great
programmer and a bad programmer is that a great programmer adds logging and
tools that make it easy to debug the program when things fail." In other
words, he's more or less explicitly saying people who do not use logging are
bad programmers. So it's not at all surprising that there is vocal
disagreement.

------
xaphod
One important aspect of logging is to include enough information when
something fails. This sounds like it should be common sense, but I have seen
this too many times:

java.net.ConnectionException: Connection refused ...

Why not include at least the hostname and port? And what connection is this
exactly that failed? You might be able to find out what it was trying to
connect to based on the stack trace, but that requires knowledge of the
internal structure of the software.

This seems to be a common problem in software written in Java, I don't know
why exactly. Maybe something to do with the way exceptions are handled, so
that the relevant information is not available when printing the error?

~~~
henrik_w
So true! One of my pet peeves! Truly pathetic to not include dynamic info like
hostname, port and reason.

Who are these people writing code like that - they never had to debug a
problem before? Include as much as you can! A stack trace is only part of the
story.

~~~
noss
Definitely, I often catch exceptions and wrap them just to add more
information from the try-finally scope. It is so frequently useful to get a
full story of what the app was attempting to do.

------
housecor
Logging has its place, but I'd argue it's far more important that great
programmers write code that fails _fast_. In his example, he didn't have
instrumentation in production to determine why a module was returning null. If
null isn't an expected return value, then an exception should be thrown to
avoid the system moving forward in a crippled and unpredictable state. A well
tested system that stops processing and fails fast with a descriptive
exception greatly reduces the need for time-consuming and resource depleting
logging systems.

~~~
gbog
Yes but no, in many cases you do not want to fail fast and loudly. If a corner
case in a minor module crashes all the pages of your app, you better fail
silently, and log.

~~~
henrik_w
Or at least fail gracefully, by e.g. returning an error code to the caller.
Simply crashing isn't always the best choice.

------
lifeisstillgood
1\. Write code that you think is almost trivial. That way it is simple enough
to debug. If you think your implementation gives you bragging rights down the
pub, its waaaaay too complicated

2\. log everything, write tools to parse the logs. All this turning logging
off / on / up / down. It's disk space, its cheap and trust me when you wanted
-vvv debugging, the live server was set to -q

3\. testable code - oh yes, yes, yes. Functions that return common structures
that are then sent on not wrapped up for that one specific use you had in
mind(my most common crime) . But make it simple. If you cannot throw in a
config file and run it there on the command line, then its hopeless. Stubs,
dependancy injection - I get it, I just want to avoid it.

4\. Don't do as I do, do as I say :-)

~~~
dllthomas
_"1. Write code that you think is almost trivial. That way it is simple enough
to debug. If you think your implementation gives you bragging rights down the
pub, its waaaaay too complicated"_

"Debugging is twice as hard as writing the code in the first place. Therefore,
if you write the code as cleverly as possible, you are, by definition, not
smart enough to debug it." --Brian Kernighan

That said, sometimes cleverness is necessary, or actually serves the goals of
making things testable and debuggable.

 _"2. log everything, write tools to parse the logs. All this turning logging
off / on / up / down. It's disk space, its cheap and trust me when you wanted
-vvv debugging, the live server was set to -q"_

While I tend to agree, it's _not_ (just) disk space - it's IO, which sometimes
isn't cheap.

~~~
lifeisstillgood
Yeah I could not remember who said the Kernighan quote. Should have
remembered.

I am not against cleverness, just appropriate for the system - for example
_some_ part of the system needs to be very clever else its a pretty mind-
numbing project. However the rest of the project should be less clever.

~~~
dllthomas
Right, any cleverness should be buying you something worth it.

------
arocks
Misleading headline alert. All code is debuggable. Logging, mentioned in the
article, is more similar to code tracing. It may or may not be useful, because
the log might omit information crucial for debugging. There is no way someone
can write logging code for every scenario that might break in production.

To me, easily debuggable code (whatever that means) is code written clearly
with good documentation. Often well architected systems are modular (rather
than a spaghetti mess) and problems are easy to isolate.

------
astrodust
Forget writing "debuggable code", whatever that is. Normally that's called
"code that makes sense" or "code that a human can read", which I'll freely
admit is a subset of the kind of code you'll encounter in the real world. It's
still what you'd expect a competent programmer to produce.

Write _testable_ code. Each method should have a specific mandate, should
specialize, and should delegate additional functions to other methods that
follow the same pattern. It should be straightforward to verify that a
particular method is working by exercising it according to the various
branches it might take.

This is very hard to facilitate with huge methods that try to do too much,
there are too many permutations.

------
csense
When speaking about reasons not to log, I have one: What about performance?

If your software is a computer game, for example, you might have dozens of
items, characters, and particle effects being tracked by the game engine at a
rate of ~60 Hz. That's ~2000 log lines per second, or 160 KB / sec. Steam says
I've played 200 hours of Civ5. Where are we going to put 115 GB of logs?

Not to mention the string formatting and I/O of that much logging potentially
leading to unacceptable lag.

------
GhotiFish
This article is says allot, but shows little.

Ok, write lots of logs everywhere, but how and where should those go? Wont all
those messages get in the way? Will they obfuscate my code? Most of the
codebases I've worked with didn't have judicious logging, so where should I
look for examples?

Spring's source code doesn't look too bad. Is that a good standard?

Maybe other languages?

------
ohwp
Greater programmers write (unit) testable code? For example using dependency
injection.

~~~
pekk
Greater programmers don't cargo cult on abstractions like dependency
injection.

~~~
shiven
Greater programmers don't waste key clicks writing snide remarks on HN. They
are too busy writing code. ;)

------
zionpi
It's very hard to think so,our IDE or gcc has already located where we can see
the errors.And to write two versions of code is a very annoying thing.To great
programmers,source code is enough.

~~~
saejox
This made me smile.

------
qwerta
Even getting logs is luxury in many cases. I found it better to write 'fail
fast' code. Module in that example should fail with reasonable error message,
if it was used incorrectly.

~~~
gbog
If your codebase is a bit more complex than hello world, a single helpful
error message is hard to do right, because if you have a crash it should mean
something unknown happened that you cannot fix at coding time. So it is
better, in error messages, to not try to guess what is the problem and just
state the wrongful facts. But then only the log and the traceback will help.

No wonder most people just Google error messages.

------
spicavigo
Oh cmon. Why can't programming be just fun. Why this obsession with great
programmer and expert coders and the likes. Just pick a problem and hack away.
Most importantly, have fun.

Every week I come across at least 10 articles telling people about what makes
a great programmer. Sometimes its test code, sometimes its some concoction of
agile or xp etc, other times its about functional and oop.

Let it rest guys. Just go and hack.

------
hawleyal
Logging everything != debuggable

------
zerooneinfinity
Logging is nice, but I'd say core dumps and panic screens trump it.

