Skip to content

How mutability bit my rear this past week

Mutability is dangerous and I have been aware of the fact for a long time. However, mutability can be sneaky, and make its way into the code without anyone noticing. Sometimes, with disastrous consequences.

First, a little disclaimer. I am a big fan of dependency injection. I have touched the subject in this very blog maybe too many times. I know it is not a golden bullet, I know it is not the solution to every problem, but still, I believe injecting dependencies is, in general, good.

Once that’s out of the way, let’s move on to today’s story. Imagine a view controller that needs to display data fetched from a backend. The integration with said backend is well encapsulated in a class, abstracted by an interface.

A simplified version of this view controller could look like this:

I like this approach: the view controller is provided with everything it needs to work in the constructor, the origin of the data is well abstracted (it could be a remote server, it could be a local database, it could be core data, we don’t care, because we don’t need to know).

But…

This view controller belongs to a storyboard. Dum dum duuuuum.

Here is another disclaimer. I am not a big fan of storyboards for multiple reasons (and the more I use them, the less I like them). But that does not change the fact that this view controller belongs to a storyboard.

So, now, in order to provide it the data service, you have to do something like this:

As far as I know, there is no way to avoid the fact that data can not be assigned a value when the view controller is instantiated. No matter how had I tried to work around it, like making the data variable private and providing a public setter, declaring that setter in a protocol and enforcing compliance in an extension, the fact is that, as far as I know, dataService needs to be a variable, and can not be set when this class is instantiated.

And that is a problem. Because if, for example, you have a segue that leads to this view controller, and you forget to set the value in an override prepareforSegue method, data is going to be nil and DataService.allItems() is not going to be called.

And if you have two segues that lead to this view controller, then you have to make sure that you set the value of data in two different places.

And that’s exactly how my rear got bit. I didn’t notice that second segue, so it was possible to instantiate this view controller without data being set.

You might argue, and you wouldn’t be wrong, that it is my fault, that I am the one that was not setting the value of a variable that is necessary for a part of the system to work properly. Yes, but…

If this class was really immutable (like it is in the first sample), the compiler would enforce that for me. If I do not provide a non-nil value to this view controller’s initialiser, the compiler will not build the project.

And that’s important. Because I am not good at remembering details, in particular details that I somehow consider that I should not need to remember.

In any system, in any codebase of a significant size, special cases, exceptions to the rule, and small details that you have to remember, are a recipe for disaster.

Thanks to multiple painful experiences, I have learnt that this apply to everything. If I need to change a url manually before submitting an app for review, there is going to be a moment when I am going to forget, and submit with the wrong url. If an API needs a parameter named data, and returns data in a parameter named values, sooner or later I am going to mess those up. And if I code a class in a way that it can be initialised in an inconsistent state, by code is going to find a way to initialise that class in the most inconsistent state possible.

I won’t day that “mutability is evil”. There is a place for it. But, this week, I got confirmation, one again, that the best way to avoid problems is allowing the compiler to help me as much as possible.

Be First to Comment

Leave a Reply

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