DEV Community

Jonathan Silvestri
Jonathan Silvestri

Posted on • Updated on

Hard to Read Code is Not Empathetic

Prompt

You just finish creating a chain of composed functions with a higher order function wrapper that abstracts many layers of complex conditional logic behind a few function calls. It works, its decently performant, and it just got merged to master. The feature it supports works fairly well without a hitch.

A new edge case in one of the inputs is introduced that breaks your code. However, you aren't the one assessing the problem. A colleague notices it, and they are not nearly as familiar with or comfortable with heavy functional approaches. They struggle mightily to identify the problem, and come to you for help. You explain the entire process and how the composed functions work. You end up needing to implement the fix, which adds more complexity under the hood.

Your colleague points out that the logic could be dramatically simplified by a couple of if statements and one or two functions. Sure, it might not be as performant or might lead to having to account for a few more edge-cases, but it is easier to debug and read.

Empathy in Code

While my prompt is a bit specific (and in no way calling out functional programming -- I have no problem with the concepts in general -- I think it illustrates my point, that the harder code is to read, understand, and explain, the harder it is for other people to make changes to or contribute updates to your code. It's definitely a good idea to find ways to make you and your team better. But hard to read code is not the answer.

The more senior you are, the more vital it is to keep in mind some important questions when implementing code:

  • Can I explain this code to someone who is less experienced than me?
  • Can I explain this code to someone who is onboarding to my team?
  • Can I explain this code to myself after not having seen it for a long time?
  • If I add documentation for this code, is it spending more time describing the purpose of the feature set, or describing how the code works?

Less is more

We've heard this saying before. But I'm not talking about code in this case. I'm focusing on complexity.

We should strive to have code that is readable, maintainable, and resilient to change. Each one of these things is influenced by human factors - I'd argue that for all of them, the human factor is the most important one. If a feature set can be described and implemented with clear, straight-forward implementations that focus on fundamentals rather than more complex and advanced concepts, the former is the right decisions and approach.

Don't be put off by explicitness

Consider the classic example of adding an array of numbers together:

// Explicit

function sum(array) {
  let sum = 0;
  for(let index = 0; index < array.length; index++) {
    sum = sum + array[index];
  }

  return sum;
}

// Functional

function sum(array) {
  return array.reduce(function(total, number) {
    return total + number;
  }, 0)
}

I much prefer the first approach, which is very clear as to what the end result of the function should be, what variable is used to track the running sum, and how to iterate through the array to increment the sum. You could argue that some of the same approaches can be taken with the second approach, but that doesn't take away from needing to have the context of how reduce's callback function works (and then, what a callback is and how reduce uses it). It's a lot of added knowledge to perform a simple operation that people who have a lot of strength working with reduce take for granted.

Yes, the first example is easier to implement and thus, perhaps, a lot more basic. My argument for that is...so what?

Parting Thoughts

Code is the implementation detail of engineering. How you are able to expand, explain, and collaborate is where the skills lie. Easier to read code opens the door to more collaborative cultures and makes your team one other people will want to join. Embrace simplicity.

Discussion (6)

Collapse
bradtaniguchi profile image
Brad • Edited on

Yes, the first example is easier to implement and thus, perhaps, a lot more basic.

Depends right? Here's the code again:

function sum(array) {
  let sum = 0;
  for(let index = 0; index <= array.length; index++) {
    sum = sum + array[index];
  }

  return sum;
}

Did you notice what happened? I added a = in the for condition, the new result is and will always will be NaN instead of the expected sum.

This is a reason why I don't prefer the first approach. There are tons of ways to loop in JS, I consider the "classic" for loop to be the worst option in most cases due to its verbose-ness and ease of it turning into a foot gun.

Now its possible this is the only for loop known by the developer and thus it is used. This is why I think its more about context than the actual code when it comes to choices like this. If the entire dev team is all about the "functional life" then more power to you. If the entire code base uses .reduce 1 liners, then it might make sense to stick with it for better or worse. If the entire code base looks like it was written back in ES3, I'd question why, it could be a technical reason, or a "human" reason. Its possible no one knows how or what .reduce is so using it out of the blue would probably be a bad idea 馃槅

I personally believe in leveraging as much of the existing JS API as possible when it comes to code like this. As such I'd go with this version of the code:

export const sum = arr => 
  arr.reduce((sum, num) => sum + num);
Collapse
buinauskas profile image
Evaldas

Quoting on this again.

Yes, the first example is easier to implement and thus, perhaps, a lot more basic.

This is really because this feels familiar and most developers are used to C like syntax.

Collapse
silvestricodes profile image
Jonathan Silvestri Author

I'm not sure it's all that easy to turn into a foot gun if you understand how for loops work :). Sorry, this doesn't really sway me much, and I feel like you're talking about something different here.

Collapse
bradtaniguchi profile image
Brad • Edited on

I'm not sure it's all that easy to turn into a foot gun if you understand how for loops work

If you know its a foot gun it usually stops being a foot gun.

I think we are assuming something we shouldn't.
If we are being totally truthful, its not a given the developer knows how the classic for loop works. In the same fashion its not a given the developer knows how reduce works. You could argue all JS developers know about all types of loops, but then we all know that's false at some level.

Its not about one loop being better than the other in terms of being understood. Its the idea one loop is better than the other for everyone and anyone. It really depends on who the audience is.

I don't like being this nitpicky, but I wanted to bring it up because I feel like that's the goal of the post right? Yes I know how the classic for loop works. No I don't use it and no I don't think its easier to read.

Is there a right answer? NO, but I think there is a wrong way to go about thinking about it. Where we assume what we feel is "right" is right for everyone.

Its not about the code per say, its about the people reading the code.

Collapse
simov profile image
simo

You always have to balance between expressiveness and robustness/reliability.

Your short code snippet about the for loop is nice, but once your function start to balloon into more lines, having more and more state variables makes your code harder to read/maintain and much more prone to errors.

On the other hand writing functional code, just to save yourself from having a proper conditional if, and a state, because that implies it, is not always worth it in terms of maintainability of your code.

Collapse
char_bone profile image
Charlotte

I agree. I've been on Leetcode a few times and seen extremely over-complicated, hard to read code and it really gets me. I think that readable code is a must have.