DEV Community

Washing your code: don’t make me think

Artem Sapegin on December 01, 2024

You’re reading an excerpt from my book on clean code, “Washing your code.” Available as PDF, EPUB, and as a paperback and Kindle edition. Get you...
Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

Another obscure use of the bitwise NOT operator is to discard the fractional portion of a number. Use Math.floor() instead:

Be VERY careful here. Math.floor and ~~ are not the same thing, and are not interchangeable. Math.trunc and ~~ ARE pretty much the same though.

Collapse
 
sapegin profile image
Artem Sapegin

More reasons to avoid ~~!

Collapse
 
jonrandy profile image
Info Comment hidden by post author - thread only accessible via permalink
Jon Randy 🎖️ • Edited

We should not just 'blanket ban' it without understanding it, and knowing that it could be advantageous. Depending on what you are doing, ~~ may be a superior choice - it is MUCH faster (up to 50% faster on Chrome). I know people will say that premature optimisation is evil, or that we shouldn't favour performance over readability... but that is not always true and sometimes speed IS essential.

Dogmatically preaching like this decreases understanding of the language and makes for less capable developers.

 
sapegin profile image
Artem Sapegin

Dogmatically preaching like this

Lol

Collapse
 
sapegin profile image
Artem Sapegin

I did change it though, so thanks for pointing this out:

github.com/sapegin/washingcode-boo...

Collapse
 
josefjelinek profile image
Josef Jelinek

Math.trunc() works on 64 bit floats (double), while ~~ works on 32 bit integers (JS converts it before applying the bitwise operators), so

Math.trunc(5000000000) => 5000000000
Enter fullscreen mode Exit fullscreen mode

while

~~5000000000 => 705032704
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jonrandy profile image
Info Comment hidden by post author - thread only accessible via permalink
Jon Randy 🎖️ • Edited

👍 Yup! That's why I said 'pretty much' the same (didn't have time to write out the full explanation - thanks for doing that).

It's also worth noting that if you're doing a large amount of processing, and the precision (or lack thereof) of using ~~ is acceptable... then you should probably use it as it can be (environment dependent) much faster than Math.trunc... on Chrome it is about 50% faster.

Sometimes performance trumps 'cleanliness' in requirements.

 
josefjelinek profile image
Josef Jelinek

Sure. However, I think in that case, you will likely use some more performant way like in32 array buffer to store results, since it would likely matter only when you do a lot of processing of this particular type, otherwise both operations would disappear in the noise...

You should try adding this to your test cases for a big surprise:

const out = new Int32Array(data.length)
for (let i = 0; i < data.length; i++) {
  out[i] = data[i]
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
open768 profile image
sunil

I do a lot of reverse engineering for work when the original developers have left and no-one truly knows how the code does what it does. I'm brought in to give new developers something to start with, to understand where to start to look in the code.

A couple of things i'm a fan of is

  • //------------------------------------------------------------------------------ visual breaks in the code, with comment lines that have no purpose other than separating a method or class from the next one //------------------------------------------------------------------------------
  • variable unpacking. yes the language allows method1()->method2()->method3(), but it makes it vary hard to read what the intent of the programmer was. its more readable to break the steps into broken out statements. chained methods are not necessarily faster, as the compiler will do the better optimisations than a typical coder can.
Collapse
 
pengeszikra profile image
Peter Vivo • Edited

Imho I using that paralell code as arrow function, which has only one return.
In my taste is ; put on same column as ? and : because in later much easier change that two lines and don't need to be worry about ; after name

const RecipeName = ({ name, subrecipe }) => 
  (subrecipe) 
    ? <Link href={`/recipes/${subrecipe.slug}`}>{name}</Link>
    : name
    ;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sapegin profile image
Artem Sapegin

This could be a good topic to discuss some ten years ago, and I do agree it's very readable. Now with Prettier it doesn't make any difference though.

(There's a chapter on code style in the book with similar examples.)

Collapse
 
jonrandy profile image
Jon Randy 🎖️

Example 4 is wrong. The two snippets of code do 2 different things

Collapse
 
sapegin profile image
Artem Sapegin

Claiming something is wrong without explaining why isn't very helpful.

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

You need || not &&. The second example is checking that both arrays are not empty. The first just checks that at least one has content.

My preference here would be:

if (dogs.length || cats.length) {
...
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
sapegin profile image
Artem Sapegin

You're right! And yet — another reason to avoid clever code ;-)

Thread Thread
 
jonrandy profile image
Info Comment hidden by post author - thread only accessible via permalink
Jon Randy 🎖️

But the problem was in your 'clean' version. You had no problem describing what the 'non-clean' version did

Collapse
 
jonrandy profile image
Info Comment hidden by post author - thread only accessible via permalink
Jon Randy 🎖️

A balance needs to be struck here though... Avoiding 'clever' code can actually have a detrimental effect on the quality of developers over time (and also on the efficiency of the code):

Some comments have been hidden by the post's author - find out more