My last post discussed techniques I once saw as the height of cleverness but now deem foolish. The truth is: I’m still battling with these coding dilemmas everyday… It seems to be a constant game of cleverness/terseness/speed vs. verbosity/readability.
We’re told to write code for the poor soul who has to maintain it in the future. What nobody tells you is how much knowledge this poor soul has, both about the language and the problem domain. People also say to expect this future maintainer to be an idiot — this will mean you write the most understandable code possible. But again: how much of an idiot is this person?
The fact is: we have a set of unspoken assumptions we tend to make about this mystery future maintainer.
The following are mostly assumptions related to JavaScript syntax. Unexplored assumptions are ‘Design patterns’, ‘OOP’, ‘The problem domain’ etc.
Level 1 maintainer
- Sentient humanoid
- Speaks/Writes English well
- Knows what JavaScript is
- Has programmed before
Level 2 maintainer
- Knows the correct syntax for if, else, for, while, do, throw, function, var …
- Knows about types
- Knows all the types available in JS
- Knows the difference between a statement and an expression
Level 3 maintainer
- Knows about strict vs. non-strict equality operators
- Knows about short-circuiting in logical operators
- Knows the difference between Arrays and array-like Objects
- Knows several ways to cast a value, and ways to implicitly force coercion
- Knows regular expressions to an intermediate level (anchors, alternation, character classes)
Level 4 maintainer
- Knows what a bitwise operator does
- Knows about primitives vs. objects, and wrapped primitives for method calling
- Knows what a closure is
- Knows how properties are resolved via the prototype chain
- Knows the differences between prefix and postfix increment operators
- Knows about variable/function-declaration hoisting
- Knows the exact difference between x==null and x===null
- Knows the difference between function declarations and function expressions
Level 5 maintainer
- Knows how to check types without using typeof or instanceof
- Knows why
new Number(1) != new Number(1) - Knows regular expressions well (lookarounds, capture vs. no-capture, greed)
- Knows what named function expressions are
- Knows the side effects of bitwise operations in JavaScript
Level 6 maintainer (Eich level)
- Knows what values are returned by all JavaScript operators
- Knows all precedence and associativity of all operators
- Knows where ASI will put semi-colons
- Knows the subtle differences between ES 3.1, 5.1 and 6th Edition draft
*** The above is not exhaustive and is very subjective. I don’t wish to compartmentalize understanding into levels — it’s misleading. I just wanted to somehow portray these assumptions in a clearly understandable way.
I think most devs simply do not consider a future maintainer that is any less capable than themselves, and perhaps this is a problem. If you mark yourself as a maintainer 4.5 then is it fair for you to write in a way that presumes that all future readers of your code are <= 4.5? Is this reasonable?
I suppose it depends on what the code does (e.g. high level MVC sugar vs. low-level 3D rendering) and the people that you’re currently working with, for it is from them that you are likely to draw your estimation of an appropriate maintainer level.
I’m interested to hear other peoples’ opinions on this.
What level do you assume in your future maintainers? How clever does your code get before you deem it reasonable to add a comment or opt for a more explicit and clearer technique?
I expect them to be capable developers who aren’t too moronic/retarded to lookup things they don’t already know.
I think it makes zero sense to not use a language to its full extent (doesn’t mean using nasty hacks etc) just because some crappy programmer might not understand it.
@TM, And how do you define ‘capable’…? I really don’t think it’s all as black-and-white as your comment suggests. And also, when you say “use a language to its full extent” does that include very subtle side-effects that happen implicitly? Flooring using bitwise operators comes to mind (implicit ToInt32 operation), as does the implicit coercion that happens with non-strict equality operators.
I wish it were as simple as being able to use every subtlety and feature of the language as I like… but I think that entirely misses the point of writing code for the benefit of future maintainers.
I typically only leave comments when understanding why I wrote a block of code is difficult and almost never when what the block is doing is difficult. I expect any future maintainer that doesn’t understand what a block of code does in terms of operations to look up, or figure out somehow, what the code is doing. On the other hand, I feel it can be difficult to discern *why* I had to perform a certain RegExp match, so I might leave a comment for why, but not intentionally explaining *what* the RegExp is looking for (unless the RegExp is very complex, which I try to avoid or break down).
I’ve been taught to comment code without regard to the level of expertise of those who may follow. If a block represents an object or some other unit of contstruction it gets a comment. If a line or function represents novel or perhaps more complex use of a language, it gets a comment.
In practice, I tend to follow the above fairly closely when AS3.0 is involved. For some reason (probably because the common observation is “CSS, oh that’s easy”) I tend to not comment CSS. This in spite of the fact that, I use every CSS coding opportunity to employ techniques and styles that may well have been developed, or been adopted by a browser author, as recently as today.
This post, like others in your blog, has the added bonus of forcing me to rethink and formalise previously unspoken assumptions I held since my own “js adolescence” (you might think I never fully grew out of it since I still declare all the variables at the top of the functions, and to me it makes perfect logical sense – but still.).
To answer your invite to debate, I have in my mind an image of the maintainer which changes according to the average quality of the existing codebase or shop I am working in/for. Apart from the obvious hacks, the switch fallthroughs, the exotic syntax and the intentional code horrors which I always make the object of comment incontinence, I am usually quite restrained: I’ve been lucky enough, at least in recent years, to work in situations where the best maintainers could be taken for granted. Were I to contribute to a project that lives in the wild of the opensource, I would probably be overly defensive and comment the hell out of it.
But! Am I right in this case? It’s pretty obvious what’s wrong with too little comments, but what’s wrong with too many comments? Apart from a tiny risk of strained fingers, there’s the possibility of looking pandering, maybe even patronising… but not much else. Sometimes, a few semi-superfluous comments that explain your train of thought make the code a cinch to understand at a glance by someone who might be the best programmer but, who knows, is not familiar with the codebase yet.
So, I started this very comment thinking there was nothing wrong with my comment style, but I end it thinking that I should probably comment on average more, even in the most comfortable work situation.
I try to make things explicit in many cases. I prefer String(1) over ” + 1 and sometimes, but not always, I’ll even write Number(foo) instead of +foo.
Competent JS devs should know everything up to your Level 4. Anything less than that is asking for trouble. Most devs seem to know JS up to Level 1 or 2 and they manage to get by so maybe I have high expectations.
I try to comment *why* I’m doing something, not *how* I’m doing it (unless, as Sam mentions, there’s a nontrivial RegExp involved). My litmus is that if I find myself writing code “tricky” enough to merit excitedly pointing it out to a coworker, then it’s time to break it down into something more easily digestible; after I’m done bathing in my own smug superiority that is
I might not get the point of your article, but imho the more readable the code you write the easier it is for anyone coming back to the code, including oneself. And since most of our time is spent reading code, we should simply make code as readable as possible. I think that using magic as we learn it for example in http://www.140byt.es should be avoided in code you want to survive, and not rewritten because it’s too cryptic. (I love 140bytes, but it’s more like a coding dojo.) Even patterns can be made readable, and as I learned myself when I jumped onto Python, it’s awesome to learn on code. But making code cryptic by “hacks” like “+x” which has a readable alternative “parseInt(x)” is counter-productive. And if speed is the issue, I suggest you first find the bottleneck, before you start making source unreadable before hand.
And as I read somewhere “programming is the art to write human-readable code, that happens to be interpreted by a machine” (unfortunately I can’t find the original author of it).
#2cents
Truth be told, I never consider the guy maintaining my code, because I don’t think I’ve ever come across an instance in my professional career where code is “maintained.” It’s either changed or it’s not, and most of the times that I see it being changed is because whoever is reading it at the time thinks it sucks for whatever reason and decides to rewrite it. Make it better (for whom is not specified.)
Thus, the code I write I generally couldn’t care less about the maintainer of it, because I know they’ll either love it or hate it, keep it or rewrite it. Thus, I write code that I’ll understand when trying to comprehend it again a couple of weeks later. If I don’t, the cycle restarts and I start hating my own code, and so it is rewritten.
The “write code for the next guy” mentality is, in my opinion, a waste of time.
I would want 2 things from a maintainer of code I’m using:
1) be a good citizen
2) be a good writer
In JS/DOM land, I think it’s more important for someone to know how to be a good citizen rather than what kind of binding named function expression creates, or what that weird-looking [[DontDelete]] thingie is. This means writing future-proof code, not polluting, not touching host objects, degrading gracefully, using experimenatl features with caution, etc.
For the second part — not being too clever and writing readable code. Code that I’ll be able to read and understand. I would want author to have read “Refactoring” and “Clean Code” and/or understand what well-written code is. Unit tests are a plus, although it’s still hard to come across libraries/scripts that have them.
Just realized you’re talking about _future_ maintainer, while I was talking about “maintainer” of 3-rd party code I’m using
Don’t assume everyone knows all the language tricks like you do. I think the best way is to establish best practices for a company, everyone may not agree, but everyone will code mostly the same way. I hope I never have to maintain/edit/delete/rewrite code that Marcus Spade wrote (since he doesn’t care about future maintainers). Marcus Spade, did you never have to fix a bug that was really hard because the original coder had some mysterious assumption in their code? Or because you weren’t quite sure of the precedence rules and wished they had put in some extra parentheses?
Dear Juan Mendez,
There’s a very real possibility you’ll never have to deal with my code, ever. I’m fairly certain this won’t have an adverse effect on your life or career, just like it won’t matter much to me either. In fact, I couldn’t really care less. Glad we got that out of the way.
I have never in the span of my career had to fix a bug that was hard to track down – which is what I assume you mean – because the original author had expert knowledge that more often than not comes from years of professional, validated experience. On many occasions have I come across code I didn’t understand. As a more inexperienced developer I often swore over it, but these days I find it curious. It’s like coming across a piece of prose, the meaning of which is yet to be determined. In the end, I generally learn something from it; even if the lesson learned is that the code is incompatible with my palate and thus won’t be something I’ll produce. These days I never lament having to deal with someone else’s code. If I don’t like it, but understand its meaning and can verify the end result (i.e. test it) I’ll quite likely just rewrite it – if I care enough that is. Life is short, my time is probably better spent elsewhere.
My point is that in the end these things are subjective, and it doesn’t matter much in the grand scheme of things. Certainly not if you’re on your own. If you’re part of a team, it’s more important to have the team jell and what you refer to as coding standards and practices will just naturally evolve. At least, that’s the experience I have from working on several successful teams, and even more unsuccessful ones. The latter usually decides to spend time to codify standards and processes; the former just works.
Oh and the name is Marcus Stade – not Spade. Glad we could get that sorted out as well.