It's easier to code review Rust than Python
Monday, May 15, 2023
On Monday, I was talking to a friend about programming and I mentioned that I prefer to review Rust code over Python code. He asked why, and I had some rambling answer, but I had to take some time to think about it. It boils down to the fact that I can give a much better review of Rust code, despite having much more exposure to Python.
The main reason for this is because of the compiler and what guarantees it gives us. When I read Rust code, as long as CI checks pass then I know that it compiles and should run. With Python, we don't have those same assurances. The code could run, or it could be nonsense.
If there's an undefined variable, then Python won't yell at you, it'll just run until it hits that point. This means that we need to catch these cases in different ways. You need a lot more tests, and those tests have to hit every path through the code or those untested paths could contain showstoppers like invalid code. You have to pay attention to this in code review. If you want to make sure the code under review will work, then you have to look at whether or not there is adequate test coverage, if those tests adequately exercise all paths of the code.
And that's not to mention inputs into functions! Rust doesn't let you pass the wrong things into functions, whether it's the wrong type or if it's too many or too few arguments. Python is more than happy to let you write code using too few or too many or the wrong arguments, and doesn't do anything about it until you're trying to execute it1.
A secondary reason is the formatting and linting tools that are ubiquitous in Rust but less common in Python.
With Rust, you can generally assume code will be formatted with
cargo fmt and often it will also utilize
cargo clippy to lint it.
These together mean that the code is generally easier to read because it will be a consistent style.
The superficial aspects will be standardized and we can focus on the unique logic of this program.
In contrast, Python has myriad different formatters available, with multiple styles available for each, and so when you encounter Python code it could be in a different format.
This ever so slightly increases the cognitive load of reading each line of code, which makes it so that it's more taxing to review it and you can't review it as well.
This all leads us to a big question: why do we do code review? Generally I think it's a bad idea to rely on code review to catch bugs. You want to catch obvious ones if you see them, but the focus should be on whether or not the code solves the problem adequately, whether it's of high quality, and general structure and improvements.
But even though we're not focused on specifically whether the code works, that looming question can cast a shadow over the whole code review. If you're not sure whether the code works, it takes extra cognitive effort to examine an odd bit of code to see if it works and is just odd, or if it's a legitimate bug.
In Rust, you don't run into this (generally). If it compiles, it'll run in some form or another, so if you see something odd you can puzzle out what it's doing and how it is (or isn't) solving the problem. And that means you can put extra attention on ways to resolve and remove oddness, to make the code better!
The more things you can remove from your plate during code review, the more effective you can be at reviewing the things that matter. We all have a limited amount of energy and we cannot spend all of it on code review. Rust lets me focus on more of the things that matter and put less of my attention toward the incidental things that we shouldn't have to focus on in code review. That's why, for me, Rust is much better to review than Python, and why I can give a higher quality review.
I'm always curious to hear if someone prefers a different language for code review or (*gasp*) has the opposite opinion, so I'd love to hear from you if that's the case!
Python's optional static typechecker, mypy, helps with this a great deal. It's not a panacea, though. I've run into too many cases where code either doesn't have types or where mypy doesn't detect legitimate errors, so this is still something that demand attention during review. It's so inconsistent that you cannot count on the tooling doing the right thing, which also adds cognitive load.