Sven Marcus
Dennis Gläser
Sören Peters
[1] Jo Hannay et al. “How Do Scientists Develop and Use Scientific Software?”
[2] Monya Baker. “1,500 scientists lift the lid on reproducibility”.
[3] Zeeya Merali. “Computational science: Error, why scientific programming does not compute”.
[4] Lucas Joppa et al. “Troubling Trends in Scientific Software Use”.
[5] Mark De Rond and Alan N Miller. “Publish or perish: bane or boon of academic life?”
“Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.” - Robert C. Martin
“Clean code always looks like it was written by someone who cares.”
- Michael Feathers
“Clean code reads like well-written prose.”
- Grady Booch
“Clean code can be read, and enhanced by a developer other than its original author.”
- Dave Thomas
❌
var = 7 # num days in week
✅
number_of_days_in_week = 7
❌
def copy_chars(a1, a2):
for i in range(0, len(a1)):
a2[i] = a1[i]
✅
def copy_chars(source, destination):
for i in range(0, len(source)):
destination[i] = source[i]
❌
def genymdhms(): # generate date, year, month, day, minutes
✅
def generate_timestamp():
❌
def do_the_next_thing():
✅
def apply_neumann_boundary_condition():
Use domain names!
“Don't comment bad code - rewrite it.”
- Brian W. Kernighan and P.J. Plaugher
“The proper use of comments is to compensate for our failure to express ourself in code.
Note that I used the word failure. I meant it. Comments are always failures.”
- Robert C. Martin
If a comment must be used, it should describe the why and not the what or how
❌
# check if i is a prime number
prime = True
for j in range(i):
if i % j == 0:
prime = False
break
✅
def is_prime(number: int) -> bool:
for divisor in range(number):
if number % divisor == 0:
return False
return True
Express your intent in code instead of comments
❌
class Container:
def __init__(self, logger) -> None:
# The logger associated with this container
self.logger = logger
🤔
def load_config(parser: ConfigParser, config_path: str) -> list[str]:
"""
Loads configuration from a config file.
Falls back to the 'defaults' file on fail.
"""
config = parser.read(config_path)
if not config:
config = parser.read(".defaults")
return config
It's a lie! 😱
def load_config(parser: ConfigParser, config_path: str) -> list[str]:
"""
Loads configuration from a config file.
Falls back to the 'defaults' file on fail.
"""
config = parser.read(config_path)
if not config:
config = parser.read(".defaults")
return config
Explanation of intent
# We're doing this because...
Warning
# This code could lead to a dead lock, couldn't fix it yet
Amplification
# Cutting out spaces here is important because...
❌
a = [1, 2, 3, 4]
with open("log_a.txt", "w") as f:
for num in a:
f.write(str(num) + "\n")
b = [1, 2, 3, 4]
with open("log_b.txt", "w") as f:
for num in b:
f.write(str(num) + "\n")
✅
def write_list_to_file(
a_list: list[int], filename: str
) -> None:
with open(filename, "w") as f:
for num in a:
f.write(str(num) + "\n")
write_list_to_file(a, "log_a.txt")
write_list_to_file(b, "log_b.txt")
DRY - Don't repeat yourself!