How to improve your open source code (1) - if statements

This post was written during my trip through Iceland. It is part of a series on how to improve ones open-source code. Topics (will) contain programming style, habits, project planning, management and everything that relates to these topics. Suggestions welcome.

Please note that this is totally biased and might not represent the ideas of the broad community.

During my trip through Iceland I had a really great time seeing the awesome landscapes of Iceland, the Geysir, the Fyords and the Highlands. But in the evening, when the wind got harsher and the temperatures went down, I had some time for reading and thinking.

This is when I started this blog article series. I thought about general rules that would help me improving my open source code.

I started thinking about this subject after reading a great article once Fluent C++, about how to make if statements more understandable. This article really got me thinking about this subject, because it makes some really good points and if you haven’t read it yet, you clearly should invest a few minutes reading it (and this blog in general, of course). I’ll wait and we’ll continue if you’re ready reading it.

Why thinking about this in the first place?

Well, everyone knows you’re already writing the best code possible and everyone who doesn’t understand it is not worth your almighty code! Some people sadly think like this. And of course this is not true at all.

Once I’ve read this great statement, I guess it was also on the Fluent C++ Blog (again: read this blog, it is awesome) which goes approximately like this:

If you look at code you’ve written six months ago and you cannot think of a way to improve it, you haven’t learned anything in six months, and this is as bad as it can get

If you think about this for one minute, it is absolutely right and you really don’t want to be at this point. So, you have to start to think about your code. But where to start? Well, at the beginning, you might think. And that’s absolutely right. You have to think about the small things first, so lets start with if statements.

Making if statements more understandable

Basically what Jonathan Boccara said in Fluent C++ said. I won’t repeat what he has written, just shortly conclude: give long if expressions names by defining functions for the conditions, represent the domain in your if statements, don’t be more general than your domain specifications.

The last of these is the point I want to focus on in this article. Full quote:

Don’t compress an if statement more than in the spec

But in open source software development we often do not have any spec. If you’re working on a hobby project in your free time, improving someones code or contributing some functionality to an open source project you’re interested in, you only have the idea in your (and maybe also in someone elses) head. Sometimes you or some other people already had a discussion about the feature you’re about to implement and a rough idea how it should be done. Maybe even a concrete idea. But you’ll almost never have a specification where edge cases, preconditions and invariants of your functionaly are defined. And most of the time you don’t need one. In open source, people come together who have a similar interest and goal - no specifications required, because all contributors involved know what a functionality should do and what not.

Show me code

In the following, I’m using Rust as language for my examples. I’m not doing anything Rust specific here, so people without knowledge of the Rust programming language shouldn’t have to learn new things to understand my point, it is just that I’m most comfortable with this language right now.

So what Jonathan already said, one should not make if statements arbitrarily long and complex. Helper functions should be used for statements, even if these functions are only used once. It can heavily improve the readability of your code.

if (car.has_wheels(context.required_num_of_wheels()) && car.max_speed() > SpeedUnit::KMH(25)) || car.building_year() > Time::Year(2000) {
    // ...
}

the condition from above can be greatly improved in readability by moving it to a helper function.

fn car_is_new_or_fast(car: &Car, context: &Context) -> bool {
    car.has_wheels(context.required_num_of_wheels()) && car.max_speed() > SpeedUnit::KMH(25)) || car.building_year() > Time::Year(2000)
}

//...

if car_is_new_or_fast(&car, &context) {
    // ...
}

You might think that does not improve the code at all, just moving the complexity somewhere else - that’s not entirely true. If you have only five-line-functions, yes. But if your functions are ten, fifteen, fifty or even a hundred lines long and you have several if statement of similar complexity, moving away such things can improve it a lot.

Also, you can make complex conditions testable by moving them to functions, which is also a nice-to-have.

But, but, but… speed?

One might come up now with the obvious question: Does my code get slower because of this? I would say it depends. Fluent C++ has answered this question for C++, and I would guess that this also holds for Rust, maybe even without the 2%/7% speed decrease Jonathan is experiencing, especially if the code is inlined by the Rust compiler. Even though you might get a bit slower code, you have to think of the one question that I greatly value, not only when it comes to execution speed, but also in other cases: Does it matter?

Does it matter whether your code gets a bit slower? Is this particular piece of code crucial for your domain? If not - expressiveness first, speed second! If it does, write the expressive version first and then: measure. If the expressive version has a performance impact you cannot tolerate, you can still optimize it later.

Next…

What’s up next? Well, I don’t know. I will get myself inspired by other blog posts and articles and maybe I’ll publish the next article for this series soonish. But maybe it takes a month or two, maybe even more, until I got some content. I don’t want to make this a weekly thing or something like that, so I’ll leave it undefined when the next article of this series will be published.

Thanks for reading.