Your PR has been in review for four hours. I left four comments. None of them are about the logic.
All four comments are style? I spent all day on that feature. The logic is correct.
The logic is correct. But I tripped three times before I got to it. A function named processData. A constant named maxRetries. A line at 94 characters. My brain had to switch modes — from reading code to decoding style — before I could evaluate what you were actually doing.
processData is readable. I named it after what it does. You knew immediately what it meant.
I did know what it meant. Knowing what it means is not the same as reading it fluently. Python has a naming contract — a shared encoding. When a name breaks the contract, the reader spends a half-second switching modes. Do that forty times in a module and the cognitive cost adds up:
# Breaks the contract three times
def processData(userData): # camelCase — looks like JavaScript
MaxRetries = 3 # looks like a class, is a constant
CURRENT_INDEX = 0 # looks like a constant, is a loop variable
# Follows the contract
def process_data(user_data): # snake_case for functions and variables
MAX_RETRIES = 3 # UPPER_SNAKE for constants — visual warning: do not mutate
current_index = 0 # snake_case — normal variableIn the second version, the shape of each name tells you what kind of thing it is before you read another word.
So naming convention is a type signal. PascalCase means class, snake_case means function or variable, UPPER_SNAKE means constant. The shape carries the category.
Yes. And the signal matters most when you are scanning fast — reviewing someone's PR, looking for a bug at midnight, onboarding to a new module. Experienced Python devs do not consciously parse naming conventions. They react. When they see ProcessData() called as a function, something feels wrong before they can articulate why. That friction is the cost you impose on every reader, every time.
I have been writing camelCase function names since I started. Nobody flagged it in two years of code review.
Reviewers were focused on the logic. Style corrections take conversational energy that most reviewers do not spend unless the violation is egregious. So it accumulates silently. You write fifty camelCase functions and one day a senior dev opens your module and says "this reads like JavaScript." They are not wrong. The slow tax was always there.
What about the other rules? Line length, whitespace, imports — I know PEP 8 has them, I just never understood which ones actually matter.
Two that matter beyond aesthetics. First, trailing commas in multi-line calls:
# Without trailing comma — two lines change when you add an argument
result = some_function(
argument_one,
argument_two
)
# With trailing comma — one line changes
result = some_function(
argument_one,
argument_two,
)Second, import grouping: stdlib first, then third-party, then local, each separated by a blank line. Not aesthetic — it tells you immediately whether a name comes from Python itself, a package you installed, or your own code. When there is a dependency conflict, the grouping shows you where to look.
I have files where all three import groups are in one block. I genuinely thought that was a matter of preference.
Preference the same way driving on the right side of the road is preference. Everyone agreed on a convention so reading any Python file costs the same. When imports are grouped, I skip that section entirely. When they are jumbled, I have to parse it.
PEP 8 is a communication protocol. Naming shape signals type. Import grouping signals origin. Trailing commas reduce diff noise. Every rule encodes information that a reader's eye processes automatically — without consciously applying the rule.
That is the right frame. Tomorrow we run Ruff on your codebase and see every protocol violation at once — style, but also the eight violations in your recent PR that are not style. One of them is a bare except: that catches KeyboardInterrupt. That one matters more than the naming.
PEP 8's naming conventions are not aesthetic preferences — they are a shared encoding that allows Python developers to extract structural information from identifiers before reading the code that uses them. The encoding has three tiers: snake_case signals "this is a function, method, variable, or module — something that holds or produces a value"; PascalCase signals "this is a class — a type, a factory, something you instantiate"; UPPER_SNAKE_CASE signals "this is a constant — a value that should not change after assignment." The encoding is informal (Python does not enforce it) but universal. Every Python codebase that follows the convention allows any Python developer to orient themselves in seconds.
The cognitive cost of broken conventions. When a developer reads ProcessPayment(order), the naming signals "I am constructing something called ProcessPayment." If ProcessPayment is actually a function, the reader has already loaded the wrong mental model for the next few lines. Updating that model takes half a second. Do this forty times in a module and you have added measurable cognitive friction to every future reader of that code. The PEP 8 rule eliminates the friction by making the type information visible in the name.
Import ordering communicates dependency topology. The three-group ordering (stdlib, third-party, local) is not about aesthetics — it communicates where each name comes from without requiring the reader to know every package in the ecosystem. When debugging an ImportError or resolving a version conflict, the grouping tells you immediately whether to look at Python's own library, your requirements.txt, or your own codebase. Mixed import blocks transfer this cognitive work to the reader on every visit.
Trailing commas are a version control optimization. In multi-line function calls and collection literals, trailing commas after the last element produce cleaner diffs. Without a trailing comma, adding a new argument produces a two-line diff (change the last existing line to add a comma, add the new line). With a trailing comma, adding an argument produces a one-line diff (add the new line). Over hundreds of additions and deletions in a long-lived codebase, the cleaner diffs add up to meaningfully less noise in code review and git blame output.
Line length and side-by-side review. The 79-character limit (extended to 88 by Black and most modern teams) exists not because of terminal width — modern terminals are wide. It exists because code review tools display diffs in two panes side by side. A 120-character line forces horizontal scrolling in one or both panes, breaking the spatial relationship between old and new code that makes diffs readable at a glance.
Sign up to write and run code in this lesson.
Your PR has been in review for four hours. I left four comments. None of them are about the logic.
All four comments are style? I spent all day on that feature. The logic is correct.
The logic is correct. But I tripped three times before I got to it. A function named processData. A constant named maxRetries. A line at 94 characters. My brain had to switch modes — from reading code to decoding style — before I could evaluate what you were actually doing.
processData is readable. I named it after what it does. You knew immediately what it meant.
I did know what it meant. Knowing what it means is not the same as reading it fluently. Python has a naming contract — a shared encoding. When a name breaks the contract, the reader spends a half-second switching modes. Do that forty times in a module and the cognitive cost adds up:
# Breaks the contract three times
def processData(userData): # camelCase — looks like JavaScript
MaxRetries = 3 # looks like a class, is a constant
CURRENT_INDEX = 0 # looks like a constant, is a loop variable
# Follows the contract
def process_data(user_data): # snake_case for functions and variables
MAX_RETRIES = 3 # UPPER_SNAKE for constants — visual warning: do not mutate
current_index = 0 # snake_case — normal variableIn the second version, the shape of each name tells you what kind of thing it is before you read another word.
So naming convention is a type signal. PascalCase means class, snake_case means function or variable, UPPER_SNAKE means constant. The shape carries the category.
Yes. And the signal matters most when you are scanning fast — reviewing someone's PR, looking for a bug at midnight, onboarding to a new module. Experienced Python devs do not consciously parse naming conventions. They react. When they see ProcessData() called as a function, something feels wrong before they can articulate why. That friction is the cost you impose on every reader, every time.
I have been writing camelCase function names since I started. Nobody flagged it in two years of code review.
Reviewers were focused on the logic. Style corrections take conversational energy that most reviewers do not spend unless the violation is egregious. So it accumulates silently. You write fifty camelCase functions and one day a senior dev opens your module and says "this reads like JavaScript." They are not wrong. The slow tax was always there.
What about the other rules? Line length, whitespace, imports — I know PEP 8 has them, I just never understood which ones actually matter.
Two that matter beyond aesthetics. First, trailing commas in multi-line calls:
# Without trailing comma — two lines change when you add an argument
result = some_function(
argument_one,
argument_two
)
# With trailing comma — one line changes
result = some_function(
argument_one,
argument_two,
)Second, import grouping: stdlib first, then third-party, then local, each separated by a blank line. Not aesthetic — it tells you immediately whether a name comes from Python itself, a package you installed, or your own code. When there is a dependency conflict, the grouping shows you where to look.
I have files where all three import groups are in one block. I genuinely thought that was a matter of preference.
Preference the same way driving on the right side of the road is preference. Everyone agreed on a convention so reading any Python file costs the same. When imports are grouped, I skip that section entirely. When they are jumbled, I have to parse it.
PEP 8 is a communication protocol. Naming shape signals type. Import grouping signals origin. Trailing commas reduce diff noise. Every rule encodes information that a reader's eye processes automatically — without consciously applying the rule.
That is the right frame. Tomorrow we run Ruff on your codebase and see every protocol violation at once — style, but also the eight violations in your recent PR that are not style. One of them is a bare except: that catches KeyboardInterrupt. That one matters more than the naming.
PEP 8's naming conventions are not aesthetic preferences — they are a shared encoding that allows Python developers to extract structural information from identifiers before reading the code that uses them. The encoding has three tiers: snake_case signals "this is a function, method, variable, or module — something that holds or produces a value"; PascalCase signals "this is a class — a type, a factory, something you instantiate"; UPPER_SNAKE_CASE signals "this is a constant — a value that should not change after assignment." The encoding is informal (Python does not enforce it) but universal. Every Python codebase that follows the convention allows any Python developer to orient themselves in seconds.
The cognitive cost of broken conventions. When a developer reads ProcessPayment(order), the naming signals "I am constructing something called ProcessPayment." If ProcessPayment is actually a function, the reader has already loaded the wrong mental model for the next few lines. Updating that model takes half a second. Do this forty times in a module and you have added measurable cognitive friction to every future reader of that code. The PEP 8 rule eliminates the friction by making the type information visible in the name.
Import ordering communicates dependency topology. The three-group ordering (stdlib, third-party, local) is not about aesthetics — it communicates where each name comes from without requiring the reader to know every package in the ecosystem. When debugging an ImportError or resolving a version conflict, the grouping tells you immediately whether to look at Python's own library, your requirements.txt, or your own codebase. Mixed import blocks transfer this cognitive work to the reader on every visit.
Trailing commas are a version control optimization. In multi-line function calls and collection literals, trailing commas after the last element produce cleaner diffs. Without a trailing comma, adding a new argument produces a two-line diff (change the last existing line to add a comma, add the new line). With a trailing comma, adding an argument produces a one-line diff (add the new line). Over hundreds of additions and deletions in a long-lived codebase, the cleaner diffs add up to meaningfully less noise in code review and git blame output.
Line length and side-by-side review. The 79-character limit (extended to 88 by Black and most modern teams) exists not because of terminal width — modern terminals are wide. It exists because code review tools display diffs in two panes side by side. A 120-character line forces horizontal scrolling in one or both panes, breaking the spatial relationship between old and new code that makes diffs readable at a glance.