Enforce Naming Conventions¶
General Best Practices¶
First of all, Python has several naming conventions outlined in PEP-8, like using snake case for variables and camel case for classes. The Naming Rules of the Google Python Style Guide help you enforce these conventions.
Project-Specific Naming Conventions¶
Besides Pythonic practices, each codebase has its own naming conventions. These are influenced not just by engineering best practices but also by the domain of your software. And often also by events specific to your company, like a re-branding.
In this recipe, we're showing some examples of how to detect misleading names with Sourcery custom rules.
Mismatch Between the Object's Type and Name¶
This usually occurs as a reminiscence of a significant business or technology change. Imagine the following scenario:
- Your application started out as a bookstore.
- Then with time it got popular and started to sell other products as well.
Now, probably you have a Product
class and a Book
class inheriting from it.
class Book(Product):
- Some of your old code is still relevant only for
Book
s, like thetag_authors
function. - Other parts of the code are relevant for all kind of
Product
s, like thestock_forecast
function. - And in some cases, it's messed up: an object's type has been changed to
Product
, but it's still calledbook
.
To spot these cases, you can use some Sourcery custom rules. The following 2
rules detect variables and function arguments with the name book
and type
Product
.
- id: book-vs-product-var
pattern: |
${var}: ${type}
condition: var.contains("book") and type.contains("Product")
description: Don't name a `Product` object "book"
tests:
- match: |
book: Product
- match: |
books: List[Product]
- no-match: |
book: OtherType
- id: book-vs-product-arg
pattern: |
def ...(...,${arg_name}: ${type?} = ${default_value?},...):
...
condition: arg_name.contains("book") and type.contains("Product")
description: Don't name a `Product` object "book"
Too Many Synonyms¶
Synonyms make human languages more vivid. In code, however, they only cause
confusion. Why do we have objects called holiday
and vacation
? Do they refer
to the same concept? 🤔 If not what's the difference and where is it documented?
It makes sense to pick one term and stick to it. And to use a Sourcery custom rule to find all occurrences of the other term:
- id: no-holiday-names
pattern: |
def ${func_name}(...,${arg_name}: ${type?} = ${default_value?},...):
...
condition: func_name.contains("holiday") or arg_name.contains("holiday")
description: Use the term "vacation" instead of "holiday"
Domain-Driven Design
In Domain-Driven Design, the very first recommendation after the introductory chapter is to create a "ubiquitous language". Communication gets much smoother if you ensure that the same terminology is used throughout the project. This includes code, documentation, but also emails and meetings.
Consistent Interface¶
Similarly, it makes sense to stick to one name for technical concepts.
Let's say your API has multiple modules providing CRUD functionality. But while
some modules have an update
function, the respective function is called edit
or change
in other modules. Let's create a rule that detects these
inconsistencies:
- id: use-update
pattern: |
def ${func_name}(...):
...
condition: func_name.contains("edit") or func_name.contains("change")
description: Use the verb "update" instead of "edit" or "change"
paths:
include:
- api
Note the usage of the paths.include
property in the rule above.
While some naming conventions are valid in your whole codebase, some might be relevant only for a part of it. Also while consistent naming is crucial for your public interface, it might be just a nice-to-have for helper modules or local variables.