Degrees of freedom in code

Last updated: Jun 15, 2025
Created: Jun 11, 2025
#needs/editing
State of the note
This was written in one go on Wednesday Jun 11 2025, I’m relatively happy with where it ended, but it needs some polishing and editing.

If you program, you’ve almost certainly written a line of code that’s something like x = x + 1. Maybe the variable was i instead of x, but point is: you’ve written this.

I believe that this probably did what you wanted it to. I also believe that you think that it works like you meant for it to. However, I claim that this line is not what you meant.

I think Code is an act of rhetoric, and that therefore, every choice we make communicates something about how we think about the task at hand. I want to show you what I see when I read x = x + 1, and tell you what I think you meant instead.

You might think this exercise pedantic, and it is.

Also, just to be clear on what I am and am not saying:

So what is a degree of freedom?

When I see x = x + 1, I understand there to be about 4 things I can change:

Notably, the following lines of code are all conceptually very close to x = x + 1:

You should (appropriately) seek to reduce degrees of freedom

I think that code should reduce these degrees of freedom as much as appropriate. Because code is written for an audience, you get to decide what “appropriate” is.

Less degrees of freedom is better for writing code: it’s harder to make an error when initially writing the code.

Less degrees of freedom is also better for reading code: it makes it easier to understand the intent.

Reducing degrees of freedom

Making this about ourselves

In Python, we can remove one degree of freedom by writing x += 1. I want to spend a little bit more time than you might think necessary explaining why this is a big deal, and it is not about the 2 keystrokes we just saved.

There is something really subtle in the first line of code: that the variable being modified is the same as the variable being used in the computation. However, nowhere in our code was this requirement explicit. When writing, we had to be careful to use the same variable, and when reading, we had to notice that, and keep it in mind.

This new line captures this entirely. The following lines of code are the new conceptual neighbors of x += 1.

All of these lines of code have now captured this key insight of “the new value of the variable is based off of its existing value”. Even if we switch our variable, this is still true.

Counting vs adding

Other languages allow us to go further. In C, C++, Java, and many others, we would probably write this as x++, which removes another degree of freedom.

Every time we reduce a degree of freedom, we are adding constraints to our code. In other words, we are capturing something fundamental about the computing act being performed.

The ++ operator is significant because it is capturing something fundamental. We don’t actually care about doing an addition here, we care about getting the next number.

Again, you might think this pedantic, and it is. But I’m willing to bet that when you count up “one, two, three, four, …”, you are not thinking “oh, what is the result of adding 1 to the number I just said”: you are thinking of “what is the number that comes after the one I just said”. The difference between ++ and += 1 is the difference between counting and addition.

Going further

At this point, we might be tempted to not go any further. We have, after all, reduced this to “count on x”. This does really feel like we’ve removed all the degrees of freedom we have available.

However, we can go look at context. Almost every time I’ve seen someone count a variable up, they are either iterating an indexing variable in a loop, or trying to gather a count of something. (Yes, you can come up with other use cases for x++, but they are less common.)

Both of these can usually be expressed fully without using a counter at all.

In Python, if we need the index of what we are iterating, we can go:

for index, element in enumerate(values):
	# ....

Again, this isn’t just about saving a line of code, it’s about clearly stating the relationships in our code. Here, index is not something we manage: we are guaranteed, by enumerate, that this will correspond to the index of element. No counting required.

(I would also challenge whether you need the for loop in the first place: can you replace this with values.map(...) or something similar?)

In the case of counting, I think it’s often better written using something like:

count = len(values.filter(...))

Where the entire process of counting is now abstracted away into the low-level function len. If this is a frequent operation, it’s worth defining or finding a procedure that takes the filter as a lambda and returns the count.

I argue that’s a lot closer to what was intended here. Not only is that much easier to read, but it’s also a lot less prone to bugs: it’s hard to make this code accidentally say something else. Future coders are also given the ability to change meaningful parts (what the count variable is, the filtering function), without being given access to meaningless boilerplate.

Functions reduce degrees of freedom

The entire point of functions/classes/interfaces is that they provide a separation of degrees of freedom.

The internal state of a function is not accessible to the wider world. This instantly removes all of those degrees of freedom for any caller of that function.

We can Reframe programming advice in terms of degrees of freedom.

A worked example reducing degrees of freedom


Log

Sunday Jun 15 2025