Skip to content

Instantly share code, notes, and snippets.

@aesnyder
Last active August 29, 2015 13:57
Show Gist options
  • Save aesnyder/9770794 to your computer and use it in GitHub Desktop.
Save aesnyder/9770794 to your computer and use it in GitHub Desktop.
Logging, local variables, and a dash of opinion.
###
Logging, local variables, and a dash of opinion.
This gist intends to explore the impact of logging in software development. Briefly I should give
a bit of background: I'm not a fan of logging frameworks. The original intent of this article was
to present an argument against logging by examining:
1. How logging introduces state
2. Why local variables are bad
3. Proof that local variables are worse than logging
The primary issue with this argument is that even if the other devlopers are on board with 1, and 2 selling
them on #3 is rooted 100% in opinion. Many developers will still argue that while functions without local
variables are easier to read and reduce complexity that doesn't outweight the benefit of logging. I hoped that
in prooving 1 and 2, that 3 would be an easy sell but even to myself it wasn't. For that reason I have modified
my arguement as follows:
1. How logging introduces state
2. Why local variables are bad
3. Argue that local variables are worse than logging
4. Demonstrate the best, most functional, zero local variable
solution to logging.
Ready to have your mind blown? Let's go.
1. How logging introduce state?
To answer that, lets start with a few functions:
###
# Figure 1.1 - no logging
sum = (a, b) ->
a + b
# Figure 1.2 - logging via local variable
sum = (a, b) ->
result = a + b
console.log "Result: #{result}"
result
###
Notice that in order to log the operation we have to first save the result as a local variable, then pass
the variable to console.log, and finally return result.
2. Why are local variables bad?
Local variables won't lead to your house burning down or a plane falling from the sky,
but what they tend to do is greatly increase complexity in applications.
The complexity comes from the fact that often there will be many lines of code in between the instantian of the
'result' variable and its use. In languages such as JavaScript there is not a concept of true constants, therefore
at any point result's value could accidentally be changed through a mutating operation. There are numerous other
scenarios that could arise as well, such as accidentally forgetting to return result after using it.
--- Needs explanation of the benefit of functions that do as little as possible, take an argument and return the
result, with no other action.
Resources:
1. http://c2.com/cgi/wiki?WhyFunctionalProgramming
3. Why local variables are worse than logging?
Short answer: this is 100% opinion.
Many coders will look at figure 1 and say its beauty, composability, and lack
of local variables outweigh logging. They will say that at any point they can temporarily insert a log statement
for debugging.
At the same time, many coders have spent their entire career using logging frameworks and the complexity of local
variables is worth not having to manually insert console.log statements. While I hate this argument, I actually see
where they are coming from.
Instead of arguing something so subjective, I decided to try to stay objective and come up with non-local variable
logging solutions.
###
# Figure 2.1
sum = (a, b) ->
console.log "Result: #{a + b}"
a + b
###
While this Figure 2.1 an obvious solution to avoid local variables, it does so in an even more brittle manner in
that it a developer could still forget to retrun a + b, they could accidentally mistype the arguments, operator, or order
and overall it leaves much more for the developer to process.
So at this point you're probably like "this guy cares about this too much", and you too probably are happy to ditch logging
or stick with logging. So before I lose you to that project manager breathing down your neck here's the optimal solution that
avoids state, and still gets you logs.
###
# Figure 2.2
logReturn = (variable, identifier) ->
console.log "#{identifier}:#{variable}"
variable
sum = (a, b) ->
logReturn a + b, "Result"
###
So what we did here is kind funcy in that we avoid local variables by using a function to handle our concept of logging
something and then returning it. In fact, if we put into english the differences between Figure 1.1 and Figure 1.2 - it closely
aligns with the resposibility of the logReturn function.
Conclusion:
I learned a ton from this exercise. In fact my motivation for the entire exercise started with the gut feeling that arguing
"we should avoid logging because it causes us to write our code differently" wasn't strong enough. Even when qualifying that
the changes in our code were objectively bad. It remained that whether code quality outweighs logging benefits was left 100%
to opinion. So the best way to strengthen the argument was to get the argument to a level where we were comparing apples to apples.
In our case this meant comig up with a logging solution that broke as few of the functional programming principles as possible.
Now the question becomes in your software development do you implement logging (Figure 2.2) or not (Figure 1.1)?
If it were up to me we wouldn't, but the dynamics of the team and current project are such that we will. And I'm hoping that we
will go with a solution closer to Figure 2.2.
###
@david
Copy link

david commented Mar 25, 2014

Keep in mind there's already a more generic function that does what logReturn does: tap

_.tap(a + b, (v) -> console.log "Result = #{v}") # => a + b

or, fancier:

_.tap(a + b, _.partial(console.log, "Return ="))

However, if what you care about is logging for debug purposes, I'd argue there's something better than logging: explicit state.

Imagine this: an error happens. A process inside your app captures your app's state when an exception happens and uploads it to some place. To debug the error, all you need to do is load that state into your local development environment and go from there.

Some pointers: http://scattered-thoughts.net/blog/2014/02/17/local-state-is-harmful/ (this one is just great)
The Functional Final Frontier: https://www.youtube.com/watch?v=DMtwq3QtddY (an illustration of the above)

@aesnyder
Copy link
Author

Thanks @david, and tap + partial application looks wonderful, but I think the fact that it will be used so often merits wrapping that method in a short repeatable name like:

_l = (variable, identifier) ->
  _.tap variable, _.partial console.log, identifier

so that in use (which it will be used frequently) its clean:

if _l(count, "The count is:") >= 5 then flyHome()

In fact, that actually appears to add semantic meaning to the code?

@david
Copy link

david commented Mar 26, 2014

Not sure... I think

if _l(count, "The count is:") >= 5

is actually somewhat obscure. Contrast with

if count >= 5

if you really want to do this, I'd rather have

if _l(count) >= 5

where _l prints count and maybe a stack trace (or at least the original file location).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment