Software Developers, Communicate Your Intentions (TLDR: do it using tests)

Updated: Dec 24, 2019


Fun fact #1: When programming, we actually spend more time reading code than writing it. That makes sense, since most of the time we develop new features or fix bugs in existing systems. Developing a new feature requires clarity of the best way to incorporate it into the existing code, and when fixing a bug one must figure out what the code does and where the problem is. In each of the above you must read all the relevant code before you start writing any code.


Fun fact #2: If code is not maintained, it rots. Thus, if your code does not read well, that is redundant or misunderstood, you should modify it (as opposed to the “don't touch what works” approach).


If you don't want someone, including your future self, to mistakenly break your code, clearly communicate your intentions when you write it; Someday, someone will touch it, and so what the code is meant to do better be clear to them.


* * *


How can you communicate your intentions? Here is a story to help make the point.

We recently had a bug added to our system (how could that have happened?? 😊). In an attempt to find the reason for the bug, I realized that one of my colleagues had made a small change to one of our methods, having no clue that it introduced the bug. The following is the code I bumped into:



This is the original code, which I wrote a while back:



nameToImgUrlMap is a map from image names to image URLs. This method takes an array of image-names and returns an array of image URLs using nameToImgUrlMap. My teammate assumed that the `filter` functionality was redundant, and so he removed it. What that row actually did was remove empty array values, which results in original image names that don’t appear in the nameToImageUrlMap’s keys.

How could I have communicated my intentions to the person who read it to prevent this bug?


* * *


I could verbally explain the code, either in meetings or on demand, but that does not usually work because (a) it is not scalable, (b) I typically do not remember what I’ve written a few days after I write it, and (c) the person that wrote the code can leave.


I can write comments explaining what happens in the code, or document it, but both become outdated because we don’t have to change them when we change our code. Our eyes get used to skipping comments and so they end up nicely documenting some code, just not the current one.


Cleaning your code could be the answer! I’m truly a big Clean Code enthusiast. If you want your code to live long, Clean Code must be in your toolbox. Problem is, the most common way to convey your code’s intentions is by naming it. However, as you probably know - naming is one of the hardest things in computer science. Also, even if your code is super clean and self-explanatory, it does not ensure that you handle all your edge cases clearly. But that’s all for a different blog post.


Going back to our code example above - I could have just taken the filter part out to a different function and named it “filterEmptyItems”, specifying what it does. Arguably a better named “filterEmptyItems” function is nothing more than an "alias" to an already existing function; this would not qualify as "good coding". On top of all of that, a name that is totally self-explanatory to you may have another meaning to others.


Type safety can help communicate, like explained in this blog post (which got me writing this post!). But not all languages are type safe. Also, IMHO types may be great for APIs, but still they can not clear every bit of code, and our code above is a good example.


* * *


Unfortunately, the communication methods described above are just not good enough. The best way to communicate your intentions in a way that will always be relevant and noticed, is through tests. By writing them you get both code coverage and up-to-date documentation.


Tests are written down. Tests remember. People forget.

Tests stay in your codebase forever, and people leave. Another advantage is that your colleagues can understand you without actually talking to you. This means less meetings, less people coming to ask you questions about things you’ve already forgotten, cutting your line of thought when you were just about to solve one of the world’s problems. Besides, not all programmers like to talk :). Oh, and also— you can peacefully go on a vacation knowing you have the tests to cover for you and explain the code on your behalf.



Tests are always up to date. Unlike documentation or comments, the test has to change if it is irrelevant. Otherwise your test will fail and will not be deployed to production *.


All scenarios, as well as all edge cases, can be tested. In the example above I could have written a test which checks that the code filters undefined values.


* * *


The bug in our example got deployed only because the functionality I wrote was not covered by tests.


If I did have a test for that case, my colleague would have changed the code only to see a failing test and therefore stopped there. He would have read the test in order to understand what the original intention was. If the test was clear he would have also understood it. Then, he could have chosen to either revert his changes or delete the test because the product has changed and the test is no longer relevant.


* * *


Tests ensure that you welcome only well-based and bug-free changes to the code, because tests are a live documentation of your code. Every small test documenting your code could describe that piece of code’s intentions to your colleagues. And, as explained above, it is always up to date.


If you have already been testing your code, think about looking at your tests in that state of mind, and not only as if they are there to prevent regressions. By making sure you treat your tests like you treat documentation, you will have a new way of communication which is written down and up to date.

It’s up to you to prevent bugs and have a live documentation. Test your code and make your tests speak for you!


---

* At Wix, We practice CI, which among other things means that our tests run before we deploy to production. If a test fails, I technically cannot deploy my app to production.


Photo 1 by Daria Shevtsova on Unsplash

Photo 2 by Simon Migaj on Unsplash


This post was written by Yael Zaritsky

You can follow her on Twitter




For more engineering updates and insights:


  • Black Twitter Icon
  • Black YouTube Icon

At Wix Engineering we develop some of the most innovative cloud-based web applications that influence our +180 million users worldwide.

Have any questions?
Email: wixeng@wix.com

Trademarks and logos of other parties appearing in this post are the property of their respective holders.

Get Wix Engineering Straight to Your e-mail