Writing Clean Code
Styling
“Programs must be written for people to read, and only incidentally for machines to execute."
- Harold Abelson, Structure and Interpretation of Computer Programs
One rule to rule them all
The 80 char rule
no 80 char
top_result = df.sort_values('age', ascending=True).groupby('city').head(int(top_item.limit))
80 char
top_result = (df
.sort_values('age', ascending=True)
.groupby('city')
.head(int(top_item.limit))
)
Readability Counts
if (company.name == 'Orcablue' and company.domain == 'tech' and session is not None and cache is not None):
# do something
if ((company.name == 'Orcablue') and
(company.domain == 'tech') and
(session is not None) and
(cache is not None)):
# do something
Split up expressions into multiple lines
Readability Counts
def adder(a, b):
return a + b
net_pay = adder(90 if employee.designation == 'Manager' else 100, employee.bonus if employee.bonus and employee.bonus > 0 else 0)
def adder(a, b):
return a + b
salary = 90 if employee.designation == 'Manager' else 100
bonus = employee.bonus if employee.bonus and employee.bonus > 0 else 0
net_pay = adder(salary, bonus)
Do not pass expressions as parameters
Readability Counts
def check_if_measure(parse_node, tree_nodes):
result = False
ops = None
parse_node_i = None
for i, node in enumerate(tree_nodes):
if node == parse_node:
parse_node_i = i
if parse_node_i and parse_node_i > 0:
if tree_nodes[parse_node_i - 1].c_tag == '0'
result = True
ops = parse_node.parent.t_tag
return result,ops
def check_if_measure(parse_node, tree_nodes):
result = False
ops = None
parse_node_i = None
for i, node in enumerate(tree_nodes):
if node == parse_node:
parse_node_i = i
if parse_node_i and parse_node_i > 0:
if tree_nodes[parse_node_i - 1].c_tag == '0'
result = True
ops = parse_node.parent.t_tag
return result,ops
Split logic into visual contexts
Readability Counts
-
Number of lines of code matter much less than readability.
-
Split up multiple expressions into multiple lines
-
Do not pass expressions as parameters
- Split up logic into visual contexts
Naming
class Person:
def __init__(self, field1, field2, field3, field3):
self.field1 = field1
self.field2 = field2
self.field3 = field3
self.field4 = field4
class Employee:
def __init__(self, name, age, designation, salary):
self.name = name
self.age = age
self.designation = designation
self.salary = salary
Naming
class Akira:
def __init__(self, org_name, org_domain, created):
self.org_name = org_name
self.org_domain = org_domain
self.created = created
self.employees = {}
self.engagements = {}
def add_employee(self, p):
self.employees[p.field1] = p
def new_engagement(self, name, c, emp):
eng_status = 'BEGIN'
self.engagements[name] = (c, e, eng_status)
def engagement_over(self, name):
eng_status = 'TERMINATED'
c, e, _ = self.engagements[name]
self.engagements[engagement_name] = (c, e, eng_status)
def expenditure(self):
temp = 0
for e in self.employees:
temp += e.field4
return temp
def unassigned_employees(self):
unassigned = []
for e in self.employees:
for p_name, p in self.projects.items():
if p[1].field1 == e.field1:
unassigned.append(e)
return unassigned
class Organization:
def __init__(self, name, domain, incorporated):
self.name = name
self.domain = domain
self.incorporated = incorporated
self.employees = {}
self.projects = {}
def hire(self, employee):
self.employees[employee.name]] = employee
def start_project(self, project_name, client, employee):
status = 'ONGOING'
self.projects[project_name] = (client, employee, status)
def finish_project(self, project_name):
status = 'FINISHED'
client, employee, _ = self.projects[project_name]
self.projects[project_name] = (client, employee, status)
def calculate_expenditure(self):
sum_of_salaries = 0
for employee in self.employees:
sum_of_salaries = employee.salary
return sum_of_salaries
def get_unassigned_employees(self):
assigned_employees = []
for (_, employee, _) in self.projects.values():
assigned_employees.append(employee)
unassigned_employees = (
set(self.employees) - set(assigned_employees))
return unassigned_employees
Naming
-
Rumpelstiltskin Principle
"When you give a name to something, you gain power over it"
-
Is something VS Does something
Nouns for objects. Verbs for functions and methods.
-
Self documenting code
Use long, explanatory variable names if they make the logic
clearer.
-
Avoid generic names
holder, value, field, data, my_list, single letter variables, var etc.
Code Smells
“I like my code to be elegant and efficient."
- Bjarne Stroustrup, creator of C++
Code Smells
-
Overused conditional branches
Lots of If-else blocks, whether nested or sequential.
-
DRY
Don't Repeat Yourself. If some logic is getting repeated
more than twice, it might be a good candidate for refactoring.
-
Jack of all trades functions and methods
One function should do one thing. If complexity of logic, it
should be abstracted away to other functions.
- Passing expressions as arguments to functions
Code Smells
-
God Object
A class or function that is doing too many things and has grown too large.
-
Feature Envy
A class that uses methods of another class excessively.
-
The Good Samaritan
A class or a function that is handling logic that should be handled by another class or function.
-
Shotgun Surgery
The same change needs to be made in multiple places.
Unix Philosophy
-
Write programs that do one thing and do it well.
-
Write programs to work together.
- Write programs to handle text streams, because that is a universal interface.
Debugging
Debugging
-
print statements
-
debuggers - pdb, ipython.embed
- logging
Logging
-
Log liberally - Drastically reduces debugging effort.
-
Log handlers
-
Log Levels - Error, Info, Warn, Debug
- Python - %s vs %r
Git Good Practices
Commits
-
Commit often - commits should be small and related.
-
Make commit messages as explanatory as possible.
-
Commit messages should ideally begin with verbs -
Fixes, Adds, Changes, Refactors, Deletes, Removes, etc.
-
Try and tell why the commit was made in the commit message.
- It's OK to commit very small changes with a single commit. It's NOT OK to commit very large changes with a single commit.
Feature Branches
# On master branch
git checkout -b new_feature
...
# On new_feature branch
git add ...
git commit -m "Adds skeleton for new class"
git add ...
git commit -m "Adds methods for conncting to database"
git add ...
git commit -m "Fixes issue #71. None return value needed to be handled."
git checkout master
# On master branch
git merge new_feature
git branch -d new_feature
git push
Push and PULL
-
Always pull before pushing your code
-
git pull --rebase OR git pull && git rebase
- Handling merge conflicts
Fixing Commit Mistakes
-
git commit --amend
- git commit --amend --no-edit
Writing Clean Code
By sngsahil
Writing Clean Code
- 588