Skip to content

Can Code be Too Elegant?

First of all, what do I mean exactly when I say “elegant code”? I’m not specifically talking about code that implements an algorithm like you might think of when you hear about “elegant” code. I have a much more rudimentary definition, but I think it’s justified. The way I look at it, there are 3 broad classifications of desireable code:

Simple code

This is code that is clean, plain, and easy to read and understand. “Clean code” is much less prone to hard-to-find bugs and other mistakes. Most of the time this is what you should be aiming to create.

Impressively complex code

This really probably shouldn’t be called desireable, but this is what I’m talking about when I talk about code that is “too elegant”. Impressively complex code is usually seen when looking at other programmers solutions to a specific and usually common problem. It’s complex and gets a lot done in relatively very few lines, but often sacrifices readability and is difficult to understand or extend.

Elegant code

What I really consider to be “elegant” code is sort of a mix between the previous two. It is still readable if you’re proficient enough and you take a minute to understand it, but it also aims to solve a problem in a way that is efficient on resources and lines, and is easily modified and extended.

So can code be “too elegant”? Well if you agree with my definitions, no, not really. But it can be too clever to the point of being overly complex, and new programmers will often confuse the most complex code as being the best code. Code that achieves a lot in ridiculously few lines is fun to look at or challenge yourself to write, but it should be avoided in real projects.

A good example of this can be demonstrated using three variations of the classic FizzBuzz program. If you don’t know what FizzBuzz is, it’s a challenge that’s been used a lot in technical interviews for programmers. The challenge is to loop through a set of numbers—such as 1 through 20—and print out each number, but when a number is divisible by 3, print out “Fizz” instead. If it’s divisible by 5, print out “Buzz” instead. And if it’s divisible by 3 and 5, print out “FizzBuzz” instead.

Simple FizzBuzz

for (var i=1; i <= 20; i++) {
  if (i % 15 == 0) {
    console.log("FizzBuzz");
  }else if (i % 3 == 0) {
    console.log("Fizz");
  }else if (i % 5 == 0) {
    console.log("Buzz");
  }else {
    console.log(i);
  }
}

This is the “simple code” solution. Of the three solutions here, it most closely follows the plain english description of the challenge. It’s very easy to read and understand—even for very new programmers—and bugs and mistakes are easy to find and correct. All of that is good, and this solution isn’t bad, but I don’t think it’s the best.

Impressively Complex FizzBuzz

for(i=0;++i<20;console.log(i%5?x||i:x+'Buzz'))x=i%3?'':'Fizz';

Wow, ok. So this solution produces the exact same output as the previous one believe it or not, but it’s one line. I’ve seen a lot of new programmers trying to get their solutions to FizzBuzz—and other common challenges—close to this. All the problems noted above apply. It’s not readable, it’s difficult to understand, it takes up very few lines, bugs are much harder to find, and good luck extending it.

But maybe it’s not all bad. Being able to reduce a solution down to something like this—or even write it like this right out of the gate—does show an understanding of the language’s order of operations and demonstrates your ability to handle complexity. Those could be valuable things to an interviewer. It’s fun to challenge yourself to do, but for real project code, stay away from artificially inflated complexity.

Elegant FizzBuzz

for (var i = 1; i <= 20; i++) {
  var output = "";
  if (i % 3 == 0) { output += "Fizz" }
  if (i % 5 == 0) { output += "Buzz" }
  console.log(output || i);
}

Calling this code elegant might be a bit of a stretch, but FizzBuzz is a relatively simple problem, and as far as I’m concerned, this solution is as good as it gets. The main difference between the first solution and this one, is that this solution concatenates the output onto the constructor string “output”. The code is still very human-readable, but it’s a little shorter, and it has one main advantage over the other solutions: it’s very easy to alter or extend.

If you wrote this solution and an interview came back and said “Okay, now make it work for 1 through 100 and add support for divisibles of 10.” You could do that very easily like this:

for (var i = 1; i <= 100; i++) {
  var output = "";
  if (i % 3 == 0) { output += "Fizz" }
  if (i % 5 == 0) { output += "Buzz" }
  if (i % 10 == 0) { output += "Fuzz" }
  console.log(output || i);
}

You’ve only added one more line, and it only took you maybe 10 seconds. If the interview asked you to make it work for the numbers 6 and 8 rather than 3 and 5, you could also do that more easily than the first solution. You wouldn’t have to bother with changing 15 to 24 for times when the number is divisible by both numbers. It’s a small victory, but it counts, especially as the code increases in size.

Wrapping Up

In conclusion, the point of this post was to demonstrate—mainly to new programmers—that making your code small, and utilising every tool in the language to do so, isn’t everything. Good code should avoid unneeded complexity. It should be easy to read and easy to understand whenever possible. Simplicity and efficiency is the name of the game. And at times when you can take it a little further and account for the need to change or extend how a piece of code works in the future, and simplify that process, all the better.

Published inUncategorized