Skip to content

Declarative vs imperative code

There are many ways we can write code to solve a particular problem. You already know that.

But, in general, we can say that there are two different ways to structure code, and therefore, we can say that there are two different ways of programming: declaratively and imperatively.

Say what?

Let’s define those two as:

  • Imperative programming is telling the compiler how to do something.
  • Declarative programming is telling the compiler what you would like it to do.

So, in a way, I guess you could say that imperative programming is kind of “low level” and declarative programming would be kind of “high level”.

Or, if you wish, when I write imperative code I feel like I am micromanaging the compiler, giving it detailed step by step instructions, while when writing declarative code, I feel more like I am trusting other entities within my system to do what they need to do in order to make me happy.

Wait, aren’t those two different ways of reaching the same goal?

Indeed, they are. When writing declarative code, you set the expectation of the end result you have, when writing imperative code, you provide the compiler with whatever instructions are necessary to reach the same end result.

But then, why do we need a post about this?

Easy: because declarative code is easier to read, easier to understand, and therefore easier to maintain. Also, and this is what is usually overlooked, because declarative code makes easier to build high cohesive and loosely decoupled systems.

Why? Because thinking in terms of expectations (declarative), instead of implementation details (imperative) helps identify abstractions. And abstractions are what make complexity manageable.

Give me an example

This is something I have seen in production code a thousand times. It does not matter if it is Objective-C, Swift, or Java, this is a pattern repeated over and over again.

Let’s assume we want to build a login screen, containing two textfields for data entry (login and password), two labels for displaying validation errors, and a button to trigger the validation action.

This is more or less what most of us would do as a attempt to implement this login screen (this example is a iOS View Controller – Swift, an Android Activity would be similar, only on onCreate)

Have you read clean code? I’m sure you have. If for some weird reason, you have not, go read it now. In it you will find, among others, this very quotable sentence:

Don’t mix different levels of abstraction

What does that have to do with the previous method? Well, that method, the way it is right now, is doing multiple different things. It is setting fonts, font colors, opacities and disabling buttons. Yes, it is initialising the Login UI, but it is doing it in an imperative way, that is providing step by step instructions about what to do, not by declaring the expectation of the outcome.

Let’s begin by grouping those lines of imperative code by the UI element they try to prepare, by just adding white lines.

If you look at the code again, you should clearly see the different responsibilities, grouped together in blocks.

Extract all the things

Now that the different responsibilities within that method are clear, it is time for the next step. Extract each one of those responsibilities to a separate method:

You might say “but now we have many more methods, and if I want to know how the login button is prepared, I have to navigate to a different method, the details about how that button is prepared are not available in the viewDidLoad method. How is this any better?”

Well, if you need to check the implementation of a given method, just command+click on any of its invocations. That’s not a big deal.

Second, what you call extra complexity, I call better encapsulation. Now, each of the configuration methods deal with just one UI element.

Third, and more important, viewDidLoad does not need to be aware about any of the details regarding UI configuration. It just needs to trust that another method will do it. That introduces a level of abstraction and indirection that opens up a new world of possibilities. For example, if the way an specific UI element is prepared changes, viewDidLoad won’t need to be touched. If preparing an UI element requires any extra effort (for example, prefetching something from a remote server, or applying a theme, or whatever), that extra effort can be done in the specific method that prepares that specific UI element.

Fourth, and even more important, the code is declarative now. viewDidLoad sets expectations (prepare this, prepare that…) and trusts other to fulfil those expectations. The code is easier to read and easier to understand. It is a clean and neat method.

For more info, check out:

Declarative programming

Imperative programming

4 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *