Why clean code
Anybody can write code that a computer can understand. Good programmers write code that humans can understand.
Clean code refers to code that is simple to read, understand, and maintain. This term was coined by Robert Martin, also known as Uncle Bob.
Code is read more often than written.
This is why we want to keep our code as readable as possible, especially when working in a team.
Code smells, first part
Below you will find an example for a small script that we want to discuss. We want to take a closer look and ask ourselves how it can be improved. The idea behind this script is to make a request to a server, fetch a list of customers and filter for specific users (in this case users from Spain that are of a minimum certain age). After that we send the data to a server.
Code smells, second part
In part one of our series we discussed a small (Python) script. We went through the most obvious code smells and fixed them in the code below.
http_user_service_url = "https://api.coding-on-my-mind.com/users"
http_user_service_username = os.envviron.get("HTTP_USER_SERVICE_USERNAME")
http_user_service_password = os.envviron.get("HTTP_USER_SERVICE_PASSWORD")
age_in_years = 18
userdata_filename = "./clients.csv"
from datetime import datetime, timedelta
import sys
import requests
from requests.auth import HTTPBasicAuth
from configuration import http_user_service_url, http_user_service_username, http_user_service_password,
userdata_filename, age_in_years
import csv
f = open(userdata_filename, 'w')
writer = csv.writer(f)
now = datetime.now()
min_age = now - timedelta(days=age_in_years * 365)
response = requests.get(http_user_service_url, auth=HTTPBasicAuth('u938453', 'Wxc6$78gfsgOi8kr'))
# print(response)
if response.status_code != 200:
print(f"Could not make a successful request to ${url}")
sys.exit(1)
response_dict = response.json()
all_users = response_dict['results']
counter = 0
for user in all_users:
birth_date_str = user['birth_date']
birth_date = datetime.fromisoformat(birth_date_str)
if birth_date < min_age:
if user['country'] == "Spain":
new_user = {
"name": f"{user["title"]} {user["first_name"]} {user["last_name"]}",
"street": user["street"],
"city": f"{user["postal_code"]} {user["city"]}",
"country": user["country"],
}
if counter == 0:
writer.writerow(new_user.keys())
writer.writerow(new_user.values())
counter = counter + 1
else:
print(f"User with id ${user['id']} is not from Spain")
else:
print(f"User with id ${user['id']} is not old enough and will be skipped")
f.close()
We improved the code a little bit in comparison to the code from our first part.
Code smells, third part
In part one and two of our series we discussed a small (Python) script. We went through the most obvious code smells and fixed them in the code below.
from datetime import datetime, timedelta
import sys
import requests
from requests.auth import HTTPBasicAuth
from configuration import http_user_service_url, http_user_service_username, http_user_service_password,
userdata_filename, age_in_years
import csv
f = open(userdata_filename, 'w')
writer = csv.writer(f)
now = datetime.now()
min_age = now - timedelta(days=age_in_years * 365)
authentication = HTTPBasicAuth(http_user_service_username, http_user_service_password)
response = requests.get(http_user_service_url, auth=authentication)
LOG.debug(f"Fetching data from user service with response code {response.code}")
if response.status_code != 200:
LOG.error(f"Could not make a successful request to ${http_user_service_url}. The error message is {response.text}".)
sys.exit(1)
response_dict = response.json()
all_users = response_dict['results']
filtered_users = 0
total_users = 0
for user in all_users:
total_users = total_users + 1
birth_date_str = user['birth_date']
birth_date = datetime.fromisoformat(birth_date_str)
if birth_date < min_age:
if user['country'] == "Spain":
new_user = {
"name": f"{user["title"]} {user["first_name"]} {user["last_name"]}",
"street": user["street"],
"city": f"{user["postal_code"]} {user["city"]}",
"country": user["country"],
}
if filtered_users == 0:
writer.writerow(new_user.keys())
writer.writerow(new_user.values())
filtered_users = filtered_users + 1
LOG.debug(f"User with id ${user['id']} added to the list.")
else:
LOG.debug(f"User with id ${user['id']} is not from Spain.")
else:
LOG.debug(f"User with id ${user['id']} is not old enough and will be skipped.")
LOG.debug(f"{filtered_users} from {total_users} total users found.")
f.close()
We are not done yet!
Code smells, fourth part
In our first three parts of our series we discussed a small (Python) script. We went through the most obvious code smells and fixed them in the code below.
from datetime import datetime, timedelta
import sys
import requests
from requests.auth import HTTPBasicAuth
from configuration import http_user_service_url, http_user_service_username, http_user_service_password,
userdata_filename, age_in_years
import csv
authentication = None
min_age = None
file_handler = None
writer = None
def setup():
global authentication
global min_age
global writer
global file_handler
authentication = HTTPBasicAuth(http_user_service_username, http_user_service_password)
min_age = datetime.now() - timedelta(days=age_in_years * 365)
file_handler = open(userdata_filename, 'w')
writer = csv.writer(file_handler)
def tear_down():
file_handler.close()
def is_user_from_country(user):
if user['country'] == "Spain":
return True
LOG.debug(f"User with id ${user['id']} is not from Spain.")
return False
def is_user_old_enough(user):
global min_age
birth_date_str = user['birth_date']
birth_date = datetime.fromisoformat(birth_date_str)
is_old_enough = birth_date < min_age
if not is_old_enough:
LOG.debug(f"User with id ${user['id']} is not old enough and will be skipped.")
return is_old_enough
def get_all_users():
global authentication
LOG.debug(f"Fetching data from user service with response code {response.code}.")
response = requests.get(http_user_service_url, auth=authentication)
if response.status_code != 200:
LOG.error(f"Could not make a successful request to ${http_user_service_url}. The error message is {response.text}.")
sys.exit(1)
response_dict = response.json()
all_users = response_dict['results']
def write_user(user, user_count):
new_user = {
"name": f"{user["title"]} {user["first_name"]} {user["last_name"]}",
"street": user["street"],
"city": f"{user["postal_code"]} {user["city"]}",
"country": user["country"],
}
writer.writerow(user.keys())
def filter_users_of_legal_drinking_age():
all_users = get_all_users()
filtered_users_count = 0
for user in all_users:
if not is_old_enough(user):
continue
if not is_user_from_country(user):
continue
write_user(user, filtered_users_count)
filtered_users_count = filtered_users_count + 1
LOG.debug(f"{filtered_users_count} from {len(all_users)} total users found.")
if __main__ == main():
setup()
filter_users_of_legal_drinking_age()
tear_down()
We reorganized our code and put everything in dedicated functions that explain better what they do.
As one example, we created the function filter_users_of_legal_drinking_age that now explains
what we are really doing, namely extracting all the users that are allowed to have a beer or two.
Code smells, fifth part
In our first four parts of our series we discussed a small (Python) script. We went through the most obvious code smells and fixed them in every step.
from datetime import datetime, timedelta
import sys
import requests
from requests.auth import HTTPBasicAuth
from configuration import http_user_service_url, http_user_service_username, http_user_service_password,
userdata_filename, age_in_years
import csv
authentication = None
min_age = None
file_handler = None
writer = None
def setup():
global authentication
global min_age
global writer
global file_handler
authentication = HTTPBasicAuth(http_user_service_username, http_user_service_password)
min_age = datetime.now() - timedelta(days=age_in_years * 365)
file_handler = open(userdata_filename, 'w')
writer = csv.writer(file_handler)
def tear_down():
file_handler.close()
def is_user_from_country(user):
if user['country'] == "Spain":
return True
LOG.debug(f"User with id ${user['id']} is not from Spain.")
return False
def is_user_old_enough(user):
global min_age
birth_date_str = user['birth_date']
birth_date = datetime.fromisoformat(birth_date_str)
is_old_enough = birth_date < min_age
if not is_old_enough:
LOG.debug(f"User with id ${user['id']} is not old enough and will be skipped.")
return is_old_enough
def get_all_users():
global authentication
LOG.debug(f"Fetching data from user service with response code {response.code}.")
response = requests.get(http_user_service_url, auth=authentication)
if response.status_code != 200:
LOG.error(f"Could not make a successful request to ${http_user_service_url}. The error message is {response.text}.")
sys.exit(1)
response_dict = response.json()
all_users = response_dict['results']
def write_user(user, user_count):
new_user = {
"name": f"{user["title"]} {user["first_name"]} {user["last_name"]}",
"street": user["street"],
"city": f"{user["postal_code"]} {user["city"]}",
"country": user["country"],
}
writer.writerow(user.keys())
def filter_users_of_legal_drinking_age():
all_users = get_all_users()
filtered_users_count = 0
for user in all_users:
if not is_old_enough(user):
continue
if not is_user_from_country(user):
continue
write_user(user, filtered_users_count)
filtered_users_count = filtered_users_count + 1
LOG.debug(f"{filtered_users_count} from {len(all_users)} total users found.")
if __main__ == main():
setup()
filter_users_of_legal_drinking_age()
tear_down()
We went a long way. Maybe now is a good moment to think about the quality of our code should have.
Code smells, fifth part
In our first four parts of our series we discussed a small (Python) script. We went through the most obvious code smells and fixed them in every step.
from datetime import datetime, timedelta
import sys
import requests
from requests.auth import HTTPBasicAuth
from configuration import http_user_service_url, http_user_service_username, http_user_service_password,
userdata_filename, age_in_years
import csv
authentication = None
min_age = None
file_handler = None
writer = None
def setup():
global authentication
global min_age
global writer
global file_handler
authentication = HTTPBasicAuth(http_user_service_username, http_user_service_password)
min_age = datetime.now() - timedelta(days=age_in_years * 365)
file_handler = open(userdata_filename, 'w')
writer = csv.writer(file_handler)
def tear_down():
file_handler.close()
def is_user_from_country(user):
if user['country'] == "Spain":
return True
LOG.debug(f"User with id ${user['id']} is not from Spain.")
return False
def is_user_old_enough(user):
global min_age
birth_date_str = user['birth_date']
birth_date = datetime.fromisoformat(birth_date_str)
is_old_enough = birth_date < min_age
if not is_old_enough:
LOG.debug(f"User with id ${user['id']} is not old enough and will be skipped.")
return is_old_enough
def get_all_users():
global authentication
LOG.debug(f"Fetching data from user service with response code {response.code}.")
response = requests.get(http_user_service_url, auth=authentication)
if response.status_code != 200:
LOG.error(f"Could not make a successful request to ${http_user_service_url}. The error message is {response.text}.")
sys.exit(1)
response_dict = response.json()
all_users = response_dict['results']
def write_user(user, user_count):
new_user = {
"name": f"{user["title"]} {user["first_name"]} {user["last_name"]}",
"street": user["street"],
"city": f"{user["postal_code"]} {user["city"]}",
"country": user["country"],
}
writer.writerow(user.keys())
def filter_users_of_legal_drinking_age():
all_users = get_all_users()
filtered_users_count = 0
for user in all_users:
if not is_old_enough(user):
continue
if not is_user_from_country(user):
continue
write_user(user, filtered_users_count)
filtered_users_count = filtered_users_count + 1
LOG.debug(f"{filtered_users_count} from {len(all_users)} total users found.")
if __main__ == main():
setup()
filter_users_of_legal_drinking_age()
tear_down()
Before we move on, let’s get rid of some nitty grittiest, smaller things, but I would like to address them so that we do not stumble upon these issues in the next chapters.