DEV Community

Rdpfor Carzo
Rdpfor Carzo

Posted on

Stop Writing How, Start Writing What: Declarative vs Imperative Code

Every developer remembers their first programming steps. You probably learned to write loops, modify variables, and instruct the computer step-by-step on exactly how to achieve a result. This style of programming is known as imperative programming.

But as codebases grow, this 'do this, then do that' approach can lead to spaghetti code, mutation bugs, and hard-to-read logic. Enter declarative programming, a paradigm where you describe what you want to achieve, leaving the underlying implementation details to the language.

In this post, we will explore the differences between these two approaches using real-world examples in JavaScript and Python, and see why shifting toward a declarative style can make your code cleaner, more readable, and easier to maintain.


JavaScript: Filtering and Transforming Arrays

Let's start with a common task: taking an array of numbers, filtering out the odd ones, and doubling the remaining even numbers.

The Imperative Way (The 'How')

In an imperative approach, we manually manage the iteration, state, and array mutations.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const doubledEvens = [];

for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        const doubled = numbers[i] * 2;
        doubledEvens.push(doubled);
    }
}

console.log(doubledEvens); // [4, 8, 12, 16, 20]
Enter fullscreen mode Exit fullscreen mode

Why is this less than ideal?

  1. State Mutation: We are continuously updating the doubledEvens array and changing the loop counter i.
  2. Boilerplate: We have to handle loop boundaries, indices, and increments manually.
  3. Cognitive Load: To understand what this code does, a reader has to step through the loop logic line-by-line.

The Declarative Way (The 'What')

Now, let's achieve the exact same result using JavaScript's built-in declarative array methods: filter and map.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const doubledEvens = numbers
    .filter(num => num % 2 === 0)
    .map(num => num * 2);

console.log(doubledEvens); // [4, 8, 12, 16, 20]
Enter fullscreen mode Exit fullscreen mode

Look at how much cleaner this is. We are not telling JavaScript how to loop over the array. Instead, we are declaring: 'Filter this array for even numbers, then map over those numbers to double them.' The state mutation is hidden away, and the code reads almost like English.


Python: Processing Complex Data Structures

Let's switch over to Python. Suppose we have a list of user dictionaries representing users in our system. We want to extract the usernames of all users who are marked as active.

The Imperative Way

Here is how we might write this using an imperative loop:

users = [
    {'username': 'alice', 'active': True},
    {'username': 'bob', 'active': False},
    {'username': 'charlie', 'active': True},
    {'username': 'david', 'active': False}
]

active_usernames = []

for user in users:
    if user['active']:
        active_usernames.append(user['username'])

print(active_usernames) # ['alice', 'charlie']
Enter fullscreen mode Exit fullscreen mode

While this Python code is readable because of Python's clean syntax, it still suffers from imperative pitfalls: we are creating an empty list and manually appending to it inside a conditional loop.

The Declarative Way (List Comprehensions)

Python offers a powerful declarative construct called list comprehensions. It allows us to define the construction of a list in a single, expressive line.

users = [
    {'username': 'alice', 'active': True},
    {'username': 'bob', 'active': False},
    {'username': 'charlie', 'active': True},
    {'username': 'david', 'active': False}
]

active_usernames = [user['username'] for user in users if user['active']]

print(active_usernames) # ['alice', 'charlie']
Enter fullscreen mode Exit fullscreen mode

This declarative approach does not require us to create an empty temporary list or call .append() repeatedly. We simply declare the structure of the data we want to produce.


Why Declarative Programming Wins

Why should you care about this shift? It comes down to three major pillars of software engineering: readability, predictability, and maintainability.

1. Less Shared State, Fewer Bugs

Imperative code relies heavily on keeping track of current state variables (like indices, temp arrays, or boolean flags). Every time you mutate a variable, you introduce a point of failure where another thread or asynchronous operation could read an inconsistent value. Declarative programming treats data as immutable, creating new collections instead of modifying existing ones.

2. Improved Code Readability

When you use functions like map, filter, and reduce (or Python's list comprehensions), your code communicates intent directly. A developer reading your code does not have to parse a loop to find out what it does; they can see the operations instantly.

3. Separation of Concerns

Declarative programming abstracts away the execution mechanism. If your language runtime updates its array processing implementation to run in parallel behind the scenes, your declarative code gains those performance benefits automatically without you changing a single line of your codebase.


When to Remain Imperative

While declarative programming is highly beneficial, it is not a silver bullet. There are times when an imperative approach is necessary:

  • Performance Hotspots: Low-level optimizations sometimes require precise manual memory management or tight loop controls that declarative abstractions obscure.
  • Complex Control Flow: If your loop needs to break early under complex conditions or needs to access neighboring elements in highly specific ways, a classic for loop might actually be cleaner than forcing a declarative structure.

Conclusion

Adopting a declarative mindset will change the way you approach problem-solving in code. Next time you start writing a manual loop or modifying state variables, take a pause. Ask yourself: 'Can I write this by describing what I want, rather than how to get it?' Your future self and your teammates will thank you.

Happy Coding!

Top comments (0)