Skip to content

Commit 8adacec

Browse files
committed
New blog
1 parent f9df217 commit 8adacec

File tree

1 file changed

+240
-0
lines changed

1 file changed

+240
-0
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
---
2+
layout: post
3+
title: Self-Documenting Code: No Comment
4+
excerpt: What's the role of comments in self-documenting code? Here's an example from another blog, with some thoughts.
5+
tags: [Programming Practices, JavaScript]
6+
images_dir: '2020-07-11/'
7+
---
8+
9+
I recently Googled 'Best way to document JavaScript', and one of the first results was
10+
[this blog](https://gomakethings.com/whats-the-best-way-to-document-javascript), which I thought
11+
contained some good info, as well as some things I don't agree with. Top of the list would be:
12+
13+
> Self-documenting code is bullshit.
14+
15+
## Self-Documenting Code
16+
17+
Self-documenting code is code written in such a way that it's easy to read and understand its intention.
18+
It doesn't make all formal documentation redundant - even a system made of perfectly
19+
self-documenting code can benefit from documentation describing its high-level structures and goals -
20+
but it removes the need for _a lot_ of documentation and code comments.
21+
22+
## For Example
23+
24+
The blog gives the following example of documenting code with comments:
25+
26+
```js
27+
/**
28+
* Toggle visibility of a content tab
29+
* @param {String} selector Selector for the element
30+
* @param {Node} toggle The element that triggered the tab
31+
*/
32+
var toggleVisibility = function (selector, toggle) {
33+
34+
// If there's no selector, bail
35+
if (!selector) return;
36+
37+
// Get the tab to show
38+
var elem = document.querySelector(selector);
39+
if (!elem) return;
40+
41+
// Show the element
42+
elem.classList.add('active');
43+
44+
// If a toggle element was provided, add an .active class
45+
// for styling
46+
if (toggle) {
47+
toggle.classList.add('active');
48+
}
49+
50+
// Bring the newly visible element into focus
51+
elem.focus()
52+
53+
// If elem.focus() didn't work, add tabindex="-1" and try
54+
// again (elements that aren't focusable by default need a
55+
// tabindex)
56+
if (document.activeElement.matches(selector)) return;
57+
elem.setAttribute('tabindex', '-1');
58+
elem.focus();
59+
};
60+
```
61+
62+
This is pretty neatly-written code - let's take it line-by-line, consider which comments add value, and
63+
if we can make the code more self-documenting.
64+
65+
### JSDoc Documentation
66+
67+
The function is declared with [JSDoc](https://jsdoc.app) documentation:
68+
69+
```js
70+
/**
71+
* Toggle visibility of a content tab
72+
* @param {String} selector Selector for the element
73+
* @param {Node} toggle The element that triggered the tab
74+
*/
75+
var toggleVisibility = function (selector, toggle) {
76+
```
77+
78+
According to the documentation, this function toggles the visibility of a tab - but its name and the
79+
names of its arguments make no mention of tabs. Maybe the file containing this function is tab-specific,
80+
so within [its context](/naming-things-is-hard-namespace-interface-class-method-context) this makes
81+
sense, but maybe not. In any case, this can be more self-documenting:
82+
83+
```js
84+
var toggleTabVisibility = function (tabSelector, triggerElement) {
85+
```
86+
87+
The function name and selector argument now mention tabs, which makes it more clear what this function
88+
is for, and what to pass in. I've also renamed `toggle` to `triggerElement`, as according to the
89+
documentation, `toggle` is the 'element that triggered the tab'. With these changes, I'd argue the
90+
JSDoc block is redundant.
91+
92+
### Redundant Comments
93+
94+
Next statement:
95+
96+
```js
97+
// If there's no selector, bail
98+
if (!tabSelector) return;
99+
```
100+
101+
This is one of those comments which basically repeats its code. It's the sort of comment Uncle Bob
102+
recommends not to write in [Clean Code](https://smile.amazon.co.uk/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882).
103+
It's noise, and we can remove it.
104+
105+
### Generic Names and Lies
106+
107+
Next statement:
108+
109+
```js
110+
// Get the tab to show
111+
var elem = document.querySelector(tabSelector);
112+
if (!elem) return;
113+
```
114+
115+
This statement is already a little easier to understand as `selector` is now named `tabSelector`, but
116+
it can be improved. `elem` is a completely generic variable name - it gives us a hint what type the
117+
variable is, but tells us nothing about its purpose.
118+
119+
Secondly, is the comment lying? It says we're getting the tab to 'show', but doesn't this function
120+
_toggle_ visibility? The JSDoc said 'Toggle visibility of a content tab' - I'd expect from that that it
121+
would show hidden tabs, and hide visible ones. Sure enough though, it only _shows_ tabs, it doesn't
122+
hide them. This is one of the problems with documentation and comments - unlike code, they can _lie_.
123+
124+
So with a quick detour to rename the function:
125+
126+
```js
127+
var showTab = function (tabSelector, triggerElement) {
128+
```
129+
130+
...we can make the comment redundant with a descriptive variable name:
131+
132+
```js
133+
var tabToShow = document.querySelector(tabSelector);
134+
if (!tabToShow) return;
135+
```
136+
137+
### Self-Documenting CSS
138+
139+
Next statement:
140+
141+
```js
142+
// Show the element
143+
tabToShow.classList.add('active');
144+
```
145+
146+
It's not 100% clear that adding the `active` class will cause `tabToShow` to become visible, but with
147+
a more self-documenting CSS class name:
148+
149+
```js
150+
tabToShow.classList.add('visible');
151+
```
152+
153+
...the comment is redundant.
154+
155+
The comments for the next two statements again pretty much repeat what
156+
their code is doing, especially with our new naming:
157+
158+
```js
159+
// If a toggle element was provided, add an .active class
160+
// for styling
161+
if (triggerElement) {
162+
triggerElement.classList.add('visible');
163+
}
164+
165+
// Bring the newly visible element into focus
166+
tabToShow.focus()
167+
```
168+
169+
...so we can remove them. They're noise.
170+
171+
### Worthwhile Comments
172+
173+
The comment for the _next_ statements actually adds value:
174+
175+
```js
176+
// If elem.focus() didn't work, add tabindex="-1" and try
177+
// again (elements that aren't focusable by default need a
178+
// tabindex)
179+
if (document.activeElement.matches(tabSelector)) return;
180+
tabToShow.setAttribute('tabindex', '-1');
181+
tabToShow.focus();
182+
```
183+
184+
It explains how we ended up at the final two lines, and why we're adding a `tabindex` - that information
185+
would be tricky to convey in the code itself, so the comment has earned its place.
186+
187+
We can however, self-document a little more by adding a helper function:
188+
189+
```js
190+
function hasFocus(elementSelector) {
191+
return document.activeElement.matches(elementSelector);
192+
}
193+
194+
...
195+
196+
if (hasFocus(tabSelector)) return;
197+
198+
// tabToShow.focus() didn't work, add tabindex="-1" and try
199+
// again (elements that aren't focusable by default need a
200+
// tabindex)
201+
tabToShow.setAttribute('tabindex', '-1');
202+
tabToShow.focus();
203+
```
204+
205+
...the slightly-altered comment remains though, and still serves a purpose.
206+
207+
## Self-Documented
208+
209+
Our final, refactored function looks like this:
210+
211+
```js
212+
var showTab = function (tabSelector, triggerElement) {
213+
214+
if (!tabSelector) return;
215+
216+
var tabToShow = document.querySelector(tabSelector);
217+
if (!tabToShow) return;
218+
219+
tabToShow.classList.add('visible');
220+
221+
if (triggerElement) {
222+
triggerElement.classList.add('visible');
223+
}
224+
225+
tabToShow.focus()
226+
227+
if (hasFocus(tabSelector)) return;
228+
229+
// tabToShow.focus() didn't work, add tabindex="-1" and try
230+
// again (elements that aren't focusable by default need a
231+
// tabindex)
232+
tabToShow.setAttribute('tabindex', '-1');
233+
tabToShow.focus();
234+
};
235+
```
236+
237+
With more self-documenting function, parameter and variable names, the documentation and almost every
238+
comment is redundant.
239+
240+
No bullshit!

0 commit comments

Comments
 (0)