Don’t Confuse Ruff With The Zen of Python

Formatted code does not mean readable code

Andrew Nguonly
4 min readMay 11, 2024
ChatGPT 4 prompt: “Generate an image of a programmer looking at 2 computer screens. One screen has densely formatted code that is unreadable and “ugly”. The other screen has sparsely formatted code that is readable and “beautiful”. The image is in the style of a calm animation.”

Ruff is Rough 🐺

As the name implies, Ruff is rough. It’s a bully. It wrangles and rallies your Python code through twists and turns and formats it into a style that fits one size, dense. Ruff does not suggest spacing. It doesn’t assign variables to alleviate the repetition of visually long, nested dictionary values. It’ll take whatever code you’ve written and squash it through the rules you’ve allowed — probably the default.

Jokes aside, I don’t mean to bash on Ruff. Ruff is great. It’s a nice backstop for ensuring that contributions from multiple developers with varying degrees of experience and seniority conform to some level of consistency. But Ruff is not meant for the individual. Having Ruff as a safety net is not an excuse to write poorly formatted code or code that makes your eyes water at first glance. If careless, linters and formatters may give developers a false sense of quality and readability. It’s important to be self-aware of this reality.

Revisiting The Zen 🪷

The Zen of Python was created by Tim Peters on August 19, 2004, 18 years before the first release of Ruff. Linters and code formatters have come a long way since then, but as practitioners and Pythonistas, we shouldn’t forget the origins of what made us fall in love with the language. The Zen of Python includes several principles regarding “beautiful”, readable code.

Beautiful is better than ugly.

Simple is better than complex.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

The first line, Beautiful is better than ugly, encompasses all highlighted principles. The Python programming language is immensely powerful and its syntax allows for creative and complex implementation. However, it shouldn’t be abused! Let’s look at an example…

for-in-if-in-for-in-if-in-for-in 😵‍💫

honors_courses = ["calculus", "chemistry"]

# list of students and their courses + grades
students = [
("Andrew", [("algebra", 98), ("calculus", 95)]),
("Elizabeth", [("english", 100), ("chemistry", 91)]),
]

# dictionary of students taking honors courses and their grades,
# excluding non-honors courses
{
student: {
course: grade
for course, grade in courses
if course in honors_courses
}
for student, courses in students
if any(
course in honors_courses
for course, _ in courses
)
}

Believe it or not, this is a valid dictionary and it’s running in production somewhere. What’s notable about this dictionary is that it’s initialized with nested comprehensions and there’s no variable assignment. The indentation signals that nesting is present, which is valuable context for the reader, but it’s still difficult to parse quickly.

Personally, I prefer that comprehensions are formatted across three lines — first, the element, then the loop, then the optional if statement. Normally, code is read from top to bottom, left to right. For comprehensions, code is read from the middle (for loop) to the bottom (if statement) to the top (element). In a nested comprehension, this pattern is repeated from bottom to top.

Here’s how Ruff formats the dictionary:

{
student: {course: grade for course, grade in courses if course in honors_courses}
for student, courses in students
if any(course in honors_courses for course, _ in courses)
}

The way this code is formatted is like running a 100-meter dash while holding your breath. There’s no room for comments. There’s no space on either side of the lane. But is either format better? I can’t tell, but my point is that this logic may be garbled no matter how it’s laid out. Auto-formatted code does not mean readable code. Ruff will not save your eyes.

Back to Basics 🍎

Variables and for loops — these are the first two things every programmer learns when they’re starting out. When dealing with sophisticated business logic and complex data structures, err on the side of readability. There are very few cases where a doubly nested comprehension with no variable assignment will make a noticeable difference in performance in a Python application. I’m not suggesting to be egregious with allocating memory, but I am saying be reasonable. Be beautiful.

honors_courses_grades = {}

for name, courses in students:
# check if the student is taking any honors course
course_is_honors = [course in honors_courses for course, _ in courses]

if any(course_is_honors):
# get the grades of the student's honors courses
grades = {}

for course, grade in courses:
if course in honors_courses:
grades[course] = grade

honors_courses_grades[name] = grades

Assigning variables is an opportunity to give context to the reader. Space and comments are oxygen. Imperative style logic makes it easy to see execution paths, which makes writing tests more systematic. It’s not as fancy and maybe it isn’t idiomatic or “Pythonic”, but it is readable. Again, formatters won’t make your code more understandable.

This is just a friendly reminder. If all else equal, write for others, not yourself. Choose Zen.

--

--