DEV Community

Cover image for No to Imperative Code! Because Debugging Spaghetti is No Fun
Bruno Noriller
Bruno Noriller

Posted on • Originally published at Medium

No to Imperative Code! Because Debugging Spaghetti is No Fun

It's tempting to write imperative code because it can be easier to understand and write, especially for those who are new to programming. But let's be real, who wants to be a newbie forever?

Here are some reasons why you should avoid imperative code, even when using classes, object-oriented programming (OOP), or functional paradigm.

Just because you're writing in "hipster functional" languages or "old school" OOP languages, people will always find a way to add imperative spaghetti code. It's kind of amazing, really.

Imperative Code is Harder to Maintain and Debug

Imperative code often relies on a lot of explicit details and state changes, which can make it difficult to understand the overall flow of the code. This leads to more, harder-to-fix, bugs.

Debugging imperative code is like trying to find a needle in a haystack, except the haystack is on fire and the needle is actually a piece of spaghetti. Good luck with that.

Imperative Code is Less Flexible and Reusable

Imperative code often focuses on how to accomplish a specific task, rather than on the overall logic and abstractions. This can make it harder to adapt the code for different use cases or to integrate it with other code.

Trying to reuse imperative code is like trying to fit a square peg in a round hole; it might work for a little bit, but eventually, it's going to cause more problems than it solves.

A Better Approach: Declarative Code

Declarative code focuses on what the code should do, rather than on how to do it. Declarative code is often easier to understand, maintain, and reuse because it relies on abstractions and high-level logic.

Hey, let's be real, the problem is not really using imperative code, sometimes the brute force solution is the best... first solution. After it works, you then refactor it to make it better. (And maybe/probably before that, you would test to make sure you're not breaking anything.)

For example, rather than writing code that defines every step in detail, mutating variables and states, you can use higher-order functions and immutable data structures to define the logic of your code.

Here are some examples of imperative and declarative code using TypeScript:

Imperative code:

function findMax(arr: number[]): number {
    let max = arr[0];
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

console.log(findMax([1, 9, 5, 3, 7]));  // 9
Enter fullscreen mode Exit fullscreen mode

Declarative code:

function findMax(arr: number[]): number {
    return arr.reduce((acc, cur) => (cur > acc ? cur : acc), -Infinity);
}

console.log(findMax([1, 9, 5, 3, 7]));  // 9
Enter fullscreen mode Exit fullscreen mode

Let’s compare

And I'll first start with the declarative one, and please do disregard the small detail of it being only one line. If you understand what reduce does, then you know it starts with the lowest number possible (minus infinity), and then you check which of the accumulator or current is larger and return the larger one.

Yes, you need to know a lot more beforehand; it's an upfront investment, and then you can easily read, understand, and debug the code.

(And yes, you could simply use Math.max, but where would be the fun in that?)

Meanwhile... the imperative one. You start with a number, you loop through all of them, if it is bigger you change the max variable, and when you are finished you return max.

It's straightforward, it works, but wait... why did the loop start at index 1?

Go read it back, I'm waiting...

Ah... We already started with index 0 at the max variable.

See? Even in this short example, you have to pay double the attention. Now, check your most recent push for this type of spaghetti and beat yourself with your keyboard if you find any. You can also pester your pasta chef colleagues for the same, I hear mechanical keyboards make a nice sound when hitting heads (Yes, I tested and I know you will too. Please send videos.).

I can’t stress enough how much I hate imperative code

In conclusion, you write code once and read it every time after that. Not only that, I might be the one reading it and I don't really want to see what was in your head at that moment just to understand that piece of pasta code.

So, let's do us all a favor and just not use imperative statements, okay? Just remember that declarative code is like a well-oiled machine. It may take a little more effort to set up initially, but it will pay off in the end.


Cover Photo by Christine Sandu on Unsplash

Top comments (10)

Collapse
 
noriller profile image
Bruno Noriller

Totally!

But most of the time, the difference just doesn't matter (especially in the frontend).
When you get to a scale and maturity to check for places to improve performance (and it will probably be in the backend) then you would probably have no problem writing a clean, more performant, function using the low level apis.

The problem I see is wanting to do that for a function that runs on time every now and then with like... barely dozens of items at a time.

(I blame leetcode, hackerrank and similars... =p )

Collapse
 
mcsee profile image
Maxi Contieri • Edited

nice article

naming variables 'acc' , 'max', 'arr' and 'cur' is not declarative enough. what do these name's mean?

Collapse
 
jbristow profile image
Jon Bristow

Accumulator, maximum, array. Common names for library development in javascript.

However, the code above is also only slightly declarative because it’s still a command.

100% Declarative style imo doesn’t really exist in Javascript. Spend some time getting to know Prolog and you’ll see the difference.

Collapse
 
mcsee profile image
Maxi Contieri

IMHO, using abbreviations makes code less readable and less declarative
Even being common in many mainstream languages they are code smells
Again, in my opinion.
to achieve 100% declarativeness, you should stop using abbreviations.
I've used both prolog and JS

Thread Thread
 
jbristow profile image
Jon Bristow

The variable names don't really come into whether it's imperative or declarative.

Abbreviations are a readability issue. I'd agree with you that they're generally less clear in most places except for examples and generics.

Thread Thread
 
mcsee profile image
Maxi Contieri

good names are clean and declarative.
bad names are not imperative, they are just bad

Thread Thread
 
jbristow profile image
Jon Bristow

Yes, but the way you’re using the terms “declarative and imperative” is not aligned with the computer science definitions of those words.

Imperative code tells the computer to do something. “Reduce over this list of orders by summing their cost”

Declarative states an axiom or truth. “This value is the result of applying a fold right operation using the function a on x

Thread Thread
 
mcsee profile image
Maxi Contieri

you are right.
I am abusing the concept.
It is declarative code with bad names

Thread Thread
 
noriller profile image
Bruno Noriller

I don't agree because it should never be set on stone.

"Max" is pretty descriptive even if you don't use "maximum", inside that scope it's ok. Wouldn't be true if "m" were used.

However, in this example:

const productsIds = products.map(p => p.id)
Enter fullscreen mode Exit fullscreen mode

I would say one letter is the correct choice, since:

const productsIds = products.map(product => product.id)
Enter fullscreen mode Exit fullscreen mode

Having no abbreviations actually hurt how to read and generate just extra clutter.

My rule of thumb is to have the name length inversely proportional to the scope.

A dozens of lines function calls for bigger, descriptive names.
A one liner? Maybe one letter is enough.

Thread Thread
 
mcsee profile image
Maxi Contieri

'My rule of thumb is to have the name length inversely proportional to the scope'

It is fine.