// check whether user logged in
if (user.loggedInSince(date))
// increase activeUserCount
activeUserCount++;
Comments are always failures.
He’s wrong!
I don’t care about the code!
I’m in a hurry,
what happens when I use it?
pre- & postconditions
return value range
side-effects
thread-safety
(im)mutability
I’m in a hurry!
good names, small methods, SOLID, …
only go so far
how many interface implementations
am I supposed to check?
what if I have to implement an interface?
I care about the code!
I have to change it,
why does it look like this?
considered alternatives
code-shaping requirements
overall architecture
design principles
I have to change it!
good names, small methods, …
are almost useless
SOLID helps! π
ubiquitous patterns help
what about lesser known ones?
Still, most information
not expressible in code!
I care even less about the tests!
How do you test this?
"for all" propositions
thread-safety
(im)mutability
interfaces
Test coverage better be > 90%!
Tests are observations:
take time to generalize
are not a contract
only explain what, never why
Ever heard RTFT?
(Read The &*β # Tests)
No?
What was I thinking about?
Self-documenting code and tests are great for:
understanding little pieces of code
editing code
They are really bad for:
quickly determining expected behavior
understanding larger concepts
conveying rationale for decisions
A common fallacy is to assume authors of incomprehensible code will somehow be able to express themselves lucidly and clearly in comments.
A common fallacy is to assume developers incapable to express themselves lucidly and clearly in a natural language will somehow be able to write lucid and clean code.
That’s like saying
"Car’s crash".
So? Do we get rid of them?
Crashes:
are caused by negligence
are often punishable by law
are acceptable given the benefits
What about names?
do they age?
are they updated?
Yes?!
But comments can’t be? π€
Costs And Benefits |
|
|
|
Kinds Of Comments |
Clean Comments |
initial composition
maintenance
confusion
obstruction
Writing a comment is harder…
the later it is done
the more complex the topic is
the more precise the comment is
Usually affordable
compared to writing code and tests.
What to do with comments
when code changes?
nothing β may cause confusion
update β takes time
delete β wastes past efforts
Whatever schema you decide on
maintenance is critical!
changing comments takes little time
but finding them can be hard
β Comments and code must be close!
Comments that are…
outdated
ambiguous
lack detail
…cause confusion!
Costs are unpredictable
but potentially enormous!
Deteriorates trust in comments.
Takes up screen space.
(Fold that shit!)
they may be hard to write
they turn nothing green
nobody gets a pat on the back
for a clever comment
Get over it!
Costs And Benefits |
|
|
|
Kinds Of Comments |
Clean Comments |
explain what happens
keep abstractions intact
enable top-down learning
document intent
spur redesign
save lives!
// check whether user logged in
if (user.loggedInSince(date))
// increase activeUserCount
activeUserCount++;
Duh!
intrinsically redundant
can cause confusion
May be helpful when using
arcane language features.
public interface Map<K, V> {
/**
* Associates the specified value
* with the specified key in this
* map (optional operation).
*/
V put(K key, V value);
}
Every unit of code provides an abstraction:
does one thing and does it well
hides how it does it
should not require us
to look past the abstraction
The core to modularizing any non-trivial problem!
Abstractions provide two benefits:
reuse code
reuse understanding
Comments can help with both!
most people learn better top-down
clean code, tests, … are bottom-up approaches
Comments can be signposts,
helping to stay on
the right level of abstraction.
public class ArrayList<E> {
// non-private to simplify nested class access
transient Object[] elementData;
}
Context is invaluable when revisiting code!
other tools may contain this info
working through them takes time
and is transient!
Comments can be 2nd line of defense.
Itβs funny how writing documentation can spur redesign: itβs easier to simplify a complex API than try to document it completely.
(Source)
If it takes a long time to…
describe behavior on a high level
cover all special cases
explain rationale behind a decision
…maybe the code needs to be improved.
You become your own reviewer!
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. Code for readability.
(Source)
Costs And Benefits |
|
|
|
Kinds Of Comments |
Clean Comments |
currency
proximity
alternatives
(No, not money; "up-to-dateness")
some comments need to be very up-to-date
others can be slightly or even totally outdated
This influences costs:
maintenance
confusion
How far can a comment be away
from what it describes?
some must basically be on the same line
others can be some classes away
because they span several concepts
It is hard to update far-away comments!
There are plenty of alternatives to comments:
clean code
tests, demos
version control
issue tracker
wiki
But not all work for every use case.
Costs And Benefits |
Kinds Of Comments |
|
|
|
|
Clean Comments |
public int activeUserCountSince(Date date) {
int activeUserCount = 0;
// loop through the users
for (User user : users)
// check whether user logged in
if (user.loggedInSince(date))
// increase activeUserCount
activeUserCount++;
return activeUserCount;
}
Usually inline comments.
explain what the code does
speak on a very technical level
very high, need to be absolutely up to date!
right on the narrated line(s)
good names
transparent design
well-known patterns
<T extends Closeable & Iterator<String>> T
createCloseableIterator() { /*...*/ }
<E, T extends Closeable & Iterator<E>> Optional<E>
firstMatch(T elements, Predicate<E> p) { /*...*/ }
printFirstDollarWord() {
var elements = createCloseableIterator();
Optional<String> dollarWord =
firstMatch(elements, s -> s.startsWith("$"));
System.out.println(dollarWord);
}
Narrations suck!
Unless all of these are met:
arcane language features are used
there is no alternative
there is no way to improve readability
Costs And Benefits |
Kinds Of Comments |
|
|
|
|
Clean Comments |
public interface Map<K, V> {
/**
* ...............................
* ................................
* ....................................
* ....................................
*
* @return .......................
* ....................
*/
int size();
}
public interface Map<K, V> {
/**
* Returns the number of key-value
* mappings in this map. ..........
* ....................................
* ....................................
*
* @return the number of key-value
* mappings in this map
*/
int size();
}
public interface Map<K, V> {
/**
* Returns the number of key-value
* mappings in this map. If the map
* contains more than Integer.MAX_VALUE
* elements, returns Integer.MAX_VALUE.
*
* @return the number of key-value
* mappings in this map
*/
int size();
}
Usually JavaDoc on classes and methods.
They define:
the code’s central abstraction
pre- and postconditions
quirks
high
right on the class or method
good names (limited)
tests (limited)
It’s complicated.
costs are high if code changes often
benefits are high if code is read often
β The more stable & reused the code,
the better the outcome!
Costs And Benefits |
Kinds Of Comments |
|
|
|
|
Clean Comments |
/**
* When keys are Comparable, this class may
* use comparison order among keys to help
* break ties.
*/
public class HashMap<K, V> { }
/**
* When keys are Comparable, this class may
* use comparison order among keys to help
* break ties.
*/
public class HashMap<K, V> {
/* This map usually acts as a binned
* (bucketed) hash table, but when bins
* get too large, they are transformed
* into bins of TreeNodes, each
* structured similarly to those in
* java.util.TreeMap. */
}
Often non-Javadoc blocks
at the beginning of a class/method.
(Check out
@apiNote
and @implNote
.)
clarify what code is for
when to use it and when not
explain implementation details
(This is not a contract!)
not that important
not that important
demos
existing code (limited)
A clear winner!
costs are moderate
benefits are considerable
Costs And Benefits |
Kinds Of Comments |
|
|
|
|
Clean Comments |
public Color randomFavoriteColor() {
while (true)
Color favorite = randomColor();
if (isNotWhiteish(favorite))
return favorite;
}
public Color randomFavoriteColor() {
while (true)
Color favorite = randomColor();
/* In China white is often seen
* as a color of mourning and
* some Chinese users did not
* like it. We hence disallow
* whitish colors as favorites.
* For details see issue #1534.
*/
if (isNotWhitish(favorite))
return favorite;
}
clarifies why (oh why?)
code looks the way it does
can document paths not taken
negligible
not that important
commit messages
issues, wikis
Even better!
costs are negligible
benefits are considerable
These are great breadcrumbs
when tracking down bugs!
Costs And Benefits |
Kinds Of Comments |
|
|
Clean Comments |
(Real comments ahead; more.)
long john; // silver
stop(); // Hammertime!
// sometimes I believe compiler
// ignores all my comments
// I'm sorry.
// I am not sure if we need this,
// but too scared to delete.
// I am not responsible of this code.
// They made me write it, against my will.
// When I wrote this, only God and I
// understood what I was doing
// Now, God only knows
// I dedicate all this code, all my work,
// to my wife, Darlene, who will have to
// support me and our three children and
// the dog once it gets released into the
// public.
// Replaces with spaces the braces
// in cases where braces in places
// cause stasis
$str = str_replace(array("\{","\}")," ",$str);
We’re working with unfeeling material.
Comments can be therapeutic! π
Costs And Benefits |
Kinds Of Comments |
Clean Comments |
Let’s move away from:
All comments are failures.
Instead:
Comments are a tool to facilitate understanding. We should use it wisely!
Write clean code and good tests.
Then add clean comments:
make obvious which kind they are
strive for high proximity
use unambiguous language
consider API users and maintainers
Get comments reviewed!
What?
avoid whenever possible
strive for clean code
How?
use line comments (//
in Java)
place directly on line
What?
describe every top-level element’s
(class, interface, endpoint, etc.)
central abstraction
From there on, it’s up for debate:
prefer stable elements
prefer reused elements
heed diminishing returns
How many and how detailed
depends on many factors:
team:
size π
technical backgrounds π
stability π
code base:
size π
technologies π
ownership π
How?
use tools
for Java: proper JavaDoc (/** */
)
for REST: Swagger or Spring REST Docs
promise results not steps
(declarative, not imperative)
promise only what is required and tested
What?
non-trivial implementation
trade-off between alternatives
operational or developmental requirements
How?
look into @apiNote
and @implNote
multi-line comments (/* */
)
at start of type or method
strive for high-level description
cite/link issues, wikis, etc.
What?
non-obvious user requirements
"temporary" solutions (and alternatives)
How?
use phrases like "at the time of writing"
multi-line comments (/* */
)
cite/link issues, wikis, etc.
Include non-source files:
build tooling ("Why this version of Guava?")
configurations ("Why does CI store artifacts?")
documentation (yay, recursion!)
etc.
What’s impacted when you change code?
surrounding class
callers
tests
Check comments on those elements!
("Reverse-proximity")
Pair programming? Code reviews?
Keep comments in mind!
(N.B., developers complaining of aging comments
are usually the ones letting them age.)
Bottom line:
avoid narrations
describe central abstractions
decide how many/detailed contracts
always provide context
My recommendations:
get the team together and
speak freely about comments
go through the code base and
discuss concrete examples
settle on a shared approach and
include it in your style guide
use pair programming or code reviews
to adapt and enforce
π» codefx.org
βΆ youtube.com/c/codefx
π¦ @nipafx
β Get my book!
You can hire me:
training (Java 8-11, JUnit 5)
consulting (Java 8-11)