Anti-patterns, Clean Code and Beyond

A guide on how-to deal with programming challenges, keep your code clean and survive any programming course - by a guy who did it all wrong.

Table of contents

  1. How to approach programming problems.
  2. Think like a programmer - lumberjack's problem
  3. Think like a programmer - the card challenge
  4. Good programmer vs bad programmer
  5. Coding convention and Esperanto
  6. The tools
  7. Clean code vs bad code
  8. Writing a clean code
  9. Clean code and refactoring
  10. Anti-patterns

How to approach programming problems.

There are many ways of solving programming problems. Some of the most known are:

You rarely use them separately, usually you mix them up. Even though there are some significant differences between these methods. All of them share the same thing namely, they divide a BIG problem into smaller problems (small enough to be solved). Thus, in my opinion Divide and Conquer is the best technique for problem solving.

Think like a programmer - lumberjack's problem

Lets say that you are a lumberjack. Valentine's day is coming and your spouse wishes for a rocking-chair. You have no experience with carpentering, and you don't want to disappoint your spouse. Ambitiously, you decide to make the best rocking-chair ever made. From IKEA you got blueprint (so you now know how-to assembly a chair), and you are ready to go.

You start doing what you do the best. Namely, you cut (get) the tree. Obviously, entire tree is to big, so you chop the tree into smaller pieces of a raw wood. After x strokes and 30 minutes later, you have 33 raw wood pieces. You take first piece of a wood, and you make from it a part of a chair (an arm), you take second piece of a wood, and you make from it (a leg) etc.. After, a few hours of sculpting and carving in the wood, a raw materials became valid pieces of a chair. Having all elements ready, you start assembly the chair -following blueprint instructions. Few steps, and several hours later you have the best rocking-chair ever made that your spouse will never sit on. I am joking here. Good job!. What previously was nothing more than a huge tree now became a rocking-chair.

Lets Divide & Conquer (D&C) lumberjack story into a rocking-chair algorithm. Along we will count number of steps that it takes to produce a rocking-chair.

My Rocking Algorithm (TODO list)

total_nr_of_steps = 1 + 33 + 33 + 33

Now we have to ask ourselves a question. Is there any way that we can make these steps understandable by a human, but expressed in a computer like a language? The answer is YES. We can use Pseudocode - a computer like language understandable by humans.

Pseudocode

"""
Program: Lumberjack rocking-chair maker
Description: Pseudocode for making rocking-chairs
Version: 1.0
Author: J.Lumberjack
Comments: Even though, language looks like a python it is not a python code.
"""
Start
 
  # cut (get) the tree (1) 
  tree = get_tree()
 
  tree_cut = false
  raw_wood_pieces_container = []
  RAW_WOOD_PICES_NEEDED = 33
 
  # chop the tree into smaller pieces of a raw wood (33) 
  while tree_cut is false
    if size(raw_wood_pieces_container) <= RAW_WOOD_PICES_NEEDED
      raw_piece = chop(tree)
      raw_wood_pieces_container.add(raw_piece)
    else
      tree_cut = true
 
  # take a piece of wood and you make from it part of a chair 
  good_chair_pieces_container = None
  for piece in raw_wood_pieces_container
    chair_piece = make_chair_piece(piece)
    good_chair_pieces_container.add(chair_piece)
 
  # start assembly the chair pieces 
  rocking_chair = None
  for piece in good_chair_pieces_container
    rocking_chair = assemble_piece(pieceblueprint="IKEA")
 
  # give the chair to your spouse 
  return rocking_chair
 
Stop
Conclusions

As we can see anyone can talk and write Pseudocode. What's left is to translate Pseudocode to any programming language of your choice (Python, JavaScript, Cobal or Asembler).

Think like a programmer - the card challenge

I am more than sure that many of programming challenges will be written more in to TODO list rather than lumberjack story so, It should be easier for you to come up with an algorithm.
Lets have a look at below problem.

You are given a deck of cards represented by their values (from 0 to 52). A card combines two values a suit ('Clubs', 'Spades', 'Diamonds', 'Hearts') and a rank ('2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace', 'Joker'). Yours task is to ask a user for a card number. The program will determine the card suit and rank base on provided number. Example card nr.24 is the Queen of Spades. Additionally, if a card is a Joker then you print "Why so serious?". As long as the user provides numbers, the program ask for another card number. To quit program write "exit".

Hints.

Solution

As we can see, the card challenge is similar to the lumberjack story - maybe it is not so pictorial but, at the bottom these stories are the same.

Since we are wiser about Divide & Conquer method, lets use it to our advantage. We start with defining TODO list that will become our algorithm.

Joker Program (TODO list) - algorithm

Simple algorithm isn't it ?

Lets proceed to the next step. We scrap the text of computing challenge to find useful information. By this I mean candidates for variables (nouns) and candidates for function (verbs). We search for all information that we know, and information that we can infer from the text and save it in KNOWLEDGE list.

Joker Program (KNOWLEDGE list)

Implementation:

Step 1

We create an empty file called joker.py. We rewrite TODO and KNOWLEDGE lists into comments small enough to be implemented (comments will become the skeleton for our algorithm). Moreover, we like to keep things net so we provide basic information about our program.

#!/usr/bin/python3 
 
"""
Author: my@email.com
Version: 0.5
Date: 12.02.2016
License: MIT
Comments: You will be given several cards represented by their values (from 0 to 52).
You will need to print out their names.
"""
 
## ask for a card number from 0 to 52. 
## As long as user provides numbers the program ask for a card number. 
## negative cards numbers and numbers greater than 52 are not allowed 
## To quit program write "exit". Otherwise, program determine the card. 
## To determine card's suit divide its value by size of ranks (14), rounding down 
## To determine card's rank, take the remainder of division by size of ranks (14) 
## if a card is Joker then you print "Why so serious?" 

Step 2

We start implementing the comments.

## ask for a card number from 0 to 52. 
user_input = input("Please enter a card number [0-52] (To quit write 'exit'): ")

Step 3

When we are convinced that or first comment is ready we proceed with testing and we run the script.

$: python3 joker.py
Please enter a card number [0-52] (To quit write 'exit'):

We see that program does what we intended, so we stay convinced that the program is working. We proceed to the next comment. As we are adding new features we continue testing. Code > Test > Code > Test > Code > Test > Code > Test > Code > Test > Code > Test. On the way we discover that we cannot implement code in the same order as we mentioned in the comments. That is fine with us, as long as the program does what it should do.

After 11 steps, and fixing some bugs on the way we have our first program version 0.5. Yeha!!!

Step 11

This is our program version 0.5. We proceed to Step 12

#!/usr/bin/python3 
 
"""
Author: my@email.com
Version: 0.5
Date: 12.02.2016
License: MIT
Comments:
 
You are given a deck of cards represented by their values (from 0 to 52).
A card combines two values a suit ('Clubs', 'Spades', 'Diamonds', 'Hearts')
and a rank ('2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace', 'Joker').
Yours task is to ask a user for a card (number from 0-52), and program determine the card.
Example card nr.23 is the Queen of Spades.Additionally, if a card is Joker then you print "Why so serious?".
As long as user provides numbers the program ask for a card number. To quit program write "exit".
 
## ask for a card number from 0 to 52.
## As long as user provides numbers the program ask for a card number.
## negative cards numbers and numbers greater than 52 are not allowed
## To quit program write "exit". Otherwise, program determine the card.
## To determine card's suit **divide** its value by size of ranks (14), rounding down
## To determine card's rank, take the **remainder of division** by size of ranks (14)
## if a card is Joker then you print "Why so serious?"
"""
 
# ask for a card number from 0 to 52. 
user_input = input("Please enter a card number [0-52] (To quit write 'exit'): ")
 
# As long as user provides numbers the program ask for a card number. 
while user_input != "exit":
    card_number = int(user_input)
    SUIT = ['Clubs', 'Spades', 'Diamonds', 'Hearts']
    RANK = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace', 'Joker']
    LOWER_CARD_LIMIT = 0
    UPPER_CARD_LIMIT = 52
    # negative cards numbers and numbers greater than 52 are not allowed 
    if not (card_number >= LOWER_CARD_LIMIT and card_number <= UPPER_CARD_LIMIT):
        print("Allowed cards range is from 0 to 52 !!!")
    else:
        # To determine card's rank, take the remainder of division by size of ranks (14) 
        card_rank = int(card_number % len(RANK))
        card_rank_value = RANK[card_rank]
        # if a card is Joker then you print "Why so serious?" 
        if card_rank_value == "Joker":
            print("Why so serious?")
        else:
            # To determine card's suit divide its value by size of ranks (14), rounding down 
            card_suit = int(card_number / len(RANK))
            card_suit_value = SUIT[card_suit]
            print("The card is: "card_rank_value'of'card_suit_value)
 
    user_input = input("Please enter a card number [0-52] (To quit write 'exit'): ")
 
print("It was pleaseure to have you. Bye bye !!!")

Step 12 Refactoring

We start refactoring - making better code. We are aiming to achieve following goals.

#!/usr/bin/python3 
 
"""
Author: my@email.com
Version: 1.0
Date: 12.02.2016
License: MIT
Links: http://www.codeabbey.com/index/task_view/card-names
Comments: Given several cards represented by their values (from 0 to 52).
You will need to print out their names.
"""
 
user_input = input("Please enter card number [0-52] (To quit write 'exit'): ")
 
while user_input != "exit":
    card_number = int(user_input)
 
    MIN_CARD_NR = 0
    MAX_CARD_NR = 52
    SUIT = ['Clubs', 'Spades', 'Diamonds', 'Hearts']
    RANK = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace', 'Joker']
    RANK_SIZE = len(RANK)
 
    valid_card = (card_number >= MIN_CARD_NR and card_number <= MAX_CARD_NR)
    if not valid_card:
        print("Allowed cards range is from 0 to 52 !!!")
 
    if valid_card:
        # Card's rank = rounded down number of a Card number % size of ranks 
        card_rank_index = int(card_number % RANK_SIZE)
        card_rank = RANK[card_rank_index]
 
        if card_rank == "Joker":
            print("Why so serious?")
 
        if card_rank != "Joker":
            # Card's suit = rounded down number of a Card number / size of ranks 
            card_suit_index = int(card_number / RANK_SIZE)
            card_suit = SUIT[card_suit_index]
            print("The card is: "card_rank'of'card_suit)
 
    user_input = input("Please enter a card number [0-52] (To quit write 'exit'): ")
 
print("It was a pleaseure to have you. Bye bye !!!")

When we are done with refactoring, we test our program again and check if the changes that we made didn't break our program.

Conclusions

You can see that programing is not so difficult, as long as you know what to do. By looking at the examples above you will see a pattern that can be applied to any programming challenge.

The pattern:

Programming is neither so easy as the above examples. Often, you don't have all information related to a problem. E.g. What if I removed the hints from the card challenge, so you would not know how-to calculate the card's rank and card's suit. Hmm.. This is where the real programming starts. You have to come up with a solution base on limited knowladge and limited time, and you have to do it well !!!

Programming is a mixture of two feelings: frustration and satisfaction. A good programming problem will not let you sleep at night, and will keep you up. An insane programming problem will not let you sleep at night and will keep you down. Solving both gives you satisfaction. However, satisfaction in both cases has different taste.

Last but not least, programming is about improvement, and with improvement comes a new languages, concept, technologies and tools. Despite many stereotypes, programming is a highly social job. You constantly communicate and cope with other people (client, user, manager, fellow programmer, community etc..). Therefore, if you lack this skill then you better sharpen it by going for a drink in a weekend evening and enjoy time with your friends :)

Good programmer vs bad programmer

Stretching a programmer

A programmer is a n-dimensional being. He/She can be good in 1st dimension, suck in 2nd and be exceptional in 3rd. However, it's hard to kick asses in all dimensions.

Programming dimensions:

A good programmer

"Good programmers write code that humans can understand." - Martin Fowler

Being a good programmer doesn't necessary mean having a lot of experience. I met a college who claimed that he had 10 years of experience (at that time he was ~23), and didn't know how to check if an image uploaded to the server was captured in portrait or landscape mode (based on image dimension). Therefore, good programmer != experienced programmer != an old programmer.

My other colleague finished his education at gymnasium level. At the time I met him he had 5 years of experience. Always on top with the newest technologies, tools, programming concepts, but he had no idea what Pythagorean was. My other colleague similar to the previous one, had 3-4 years of experience and was the best at debugging and seeing connections within the code - it was like his brain was directly connected to a debugger. Our common colleague said he would not be able to write a factorial in a recursive way (I had a hard time believing it).

The last one, my dear friend, a university graduate, an 'A' student. He is very good at maths, computational concepts, excellent at teaching, a scientist and the best pal. He is responsible for his work and reputation. He eats mathematical/physical challenges and just by looking at code, knows which Big O it fits in. He uses tools well known to him (nothing fancy compare to others), is quite outdated with technologies, and don't care much about coding standards. The best developer that I have ever worked with.

All the colleagues mentioned above are exceptional programmers that I had the chance to work with. The common denominator for all of them is that they are passionate about what they do and, they don't stop learning. Most people would call them geeks or nerds.

A bad programmer

"Any fool can write code that a computer can understand." - Martin Fowler

A bad programmer is a person who deviates from the above list. In short, a bad programmer does a job half done, avoids responsibility, doesn't strive for improvement and the worst of all, doesn't learn from mistakes.

Conclusions

Good programmers are not born. One become a good programmer by making mistakes, learning from them, working and striving for the best. Good programmer is as good as the problem. Some of the programmers whom I mentioned would not fit into mathematical problems, but they would fit into technical problems and vice versa. At the end being a good programmer is like having a driving license - it's hard to get and easy to loose.

Coding convention and Esperanto

Bonan matenon means good morning in Esperanto language. It might be the first time that you hear about this language, after all it's only 129 years old. This Indo-European language is spoken by 2 millions people (including 2000 native speakers) scattered all over the world. As any natural language, it has a grammar, tenses, and new vocabulary is created by prefixing or suffixing the root of a word. Esperanto was born in a city that intersected four different cultures. The author L. L. Zamenhof wanted to create an easy-to-learn, politically neutral language that would transcend nationality and foster peace and international understanding between people with different languages.

Every programming language has its' Coding convention/style. Python has PEP-8, PHP has PSR-2, and Java has something here. A coding convention and Esperanto language are similar. They both have a set of rules that governs the language, and aim in unification.

Unfortunately, there is a major difference between Esperanto and coding convention. The Esperanto language, has no country, unknown culture and poor identity, making it nothing more than a fancy language to know. Where is a coding convention used by every programmer in the world. It's impossible to create professional software without following a coding convention, and I'm certain that it's impossible to create clean code without a coding convention.

Conclusions

Good programmer sticks to a coding convention. Personally, I follow conventions to 98% of guidelines. Why is that ? Convention places handcuffs on programmers hands, and that sometimes makes it hard to write cleaner code.

The tools

"A Craftsman is only as good as his tools." - of course this is a cliche saying. However, in the programming world, good tools or tools that you are fluent with can make your life easier.

Lets talk for a moment about bad tools. E.g. If you using simplistic tools like notepad) (or any of that kind) to write your python scripts then, it's like you were eating soup with a fork. From the other hand, if you are using Visual Studio for writing a bash scripts then, it's like you were eating soup with a spoon that is the size of a table.

I worked with following tools: Eclipse, Visual Studio, Aptana, Android Studio, Sublime Text, Python IDLE and Atom. Some of them are very good to work with single language (Eclipse, Pytohn IDLE), other are good to work with many languages (Sublime Text, Atom). So far I like Atom the most, it was created by a community of programmers for programmers. What I like in Atom the most are awesome plug-ins that facilitates writing a code E.g Linter informs me about syntax errors related to a specific programming language and convention. Atom-Beautify auto format my code.

Conclusions

Go with the flow and pick the tools that are common or trending within the community or a language that you are working with. There must be a good reason that new tools are constantly invented.

Clean code vs bad code

One and only measure of clean code is the number of "What The Fuck" phrase counted during a code review. This section contains wisdom of people respected within the community of programmers. I highly recommend to read Clean Code book.


code review

Clean code

Clean code is like a good book - you read it with one breath, and you don't want to stop reading it. Clean code is neat and elegant, it's divided by logical sections. Clean code is self explanatory, by just looking at it you know what programmer is expressing or want to achieve. Clean code is not necessary a computational efficient code - but it's easier to make clean code efficient rather than efficient code clean. Clean code contains solution that strikes with simplicity. Usually, clean code expresses only one thought per line. Clean code is not overloaded with comments. Clean code is neither too cluttered or too loose. Clean code uses well known principles and paradigms. Clean code is maintainable. Writing clean code requires discipline.

Bad code

When you see a bad code (code that smells) three words automatically pops out of your mouth "What The Fuck!" - I'm not joking here. A bad code is like Tiger shop, full of crap that nobody wants. Bad code is duplicated and over commented. Comments in bad code are poorly written, doesn't match the code therefore, have no value. Can you imagine a book without paragraphs? this is an example where the code is too cluttered, where there is no logical distinctions between block of codes. Now can you imagine a book that every sentence (or two) a new paragraph is placed? this is an example of too loose code. Bad code doesn't follow convention - one space here, two spaces there, no spaces there, it's just a mess. A bad code does too many things in one line. A bad code has magic numbers - values that only the author understand. Bad code is incomprehensible and nobody can maintain a bad code thus, the code has to be written from scratch. Bad code is written in rush by people with "don't care attitude". A bad code is both a mystery and misery. Bad code originates from the intention of code being read only by the author of the code.

Conclusions

Recognize code smells and don't write a bad code!

Writing a clean code

In this section I will talk about software principles (philosophies) that you need to know in order to keep your code clean.
I will show you differences between a bad code and a clean code. I will take an examples of a bad code and I will refactor (rewrite) it to the clean one.

If you would like to know more about making your code better I recommend Refactoring book.

Can you smell it ?

Look at the below code and please tell me what this code does.

for i in data:
  if i.status() == "premium":
    dis = 20
  elif len(i.basket()) >= 3:
    dis = 15
  else:
    dis = 0

If above code makes sense to you then please explain to me what dis, i and 3 is?
Does dist stand for a distance, display or discount? Is i an index or an item ? Why number 3 is so important ?

Have a look at the below code and tell me what it does.

ITEMS_DISCOUNT_COUNT = 3
 
for customer in customers:
  discount = 0.0
  if customer.status() == "premium":
    discount = 0.20
 
  items_in_basket = len(customer.basket())
  if items_in_basket >= ITEMS_DISCOUNT_COUNT:
    discount = 0.15

Can you see the difference? Clean code is self explanatory, unambiguous and comprehensive.
From the other hand, code that smells is non-informative, being far from comprehensive and its understanding is based on assumptions.

Software development principles that you have to know

DRY

Don't Repeat Yourself it says that duplicated code is not allowed. Do everything to avoid code repetition!

WET

Write Everything Twice (more than twice).
It says that first time you write a code, you are writing it for the solution, second for comprehension, third for efficiency and last for your sake.

"First do it, then do it right, then do it better." - Addy Osmani

KISS

Keep It Small and Simple minimalism and simplicity is everything in programing. Simplicity over complicity, shorter over longer.
Avoid writing long methods (longer than 4 lines of code), and make solution as simple as possible. When you reach this, then you make it even shorter and simpler.

Conclusions

Develop habit of KISS-ing, and keeping your code DRY and WET.

Clean code and refactoring

Names

Choose names, as you would choose a name for your children - Uncle Bob.

It's all about names. Names in the code are everywhere, from variable names through the function names and classes to the packages names etc... names, names and even more names.
Therfore, you have to pick names wisely. Names have to be short, informative, unambiguous and descriptive. Everything that deviates from this rule is just bad!

Bad variables names are non-informative, either too short or too long, don't follow conventions and have names that makes code hard to understand.

Below you will find examples that illustrate bad vs good cases.

Convention

Having no convention equals to being inconsistent.

Bad

thesum = 0
reversecomplementstrand = ""
gravitational_force = 9.81

Above example is perfect case of a bad variables names.

Below example shows better and the best candidates for the same variable names.

Good

# better, the best 
summation = 0
total_sum = 0
 
# better, the best 
rev_compl_strand = ""
reverse_complement_strand = ""
 
# better, the best 
GRAVITATION = 9.81
GRAVITATIONAL_FORCE = 9.81

Conclusions

Remember to keep your names descriptive and informative. Follow the convention and stick to it!

Information

Lack of information is bad.

Bad

# elapsed time in days 
d = 12
delay = 10000
name_string = "Jerry"

In the example above if we didn't have comment above variable d it wouldn't be possible to say what this variable is responsible for. Additionally, value 1000 of delay variable is specified in minutes, hours or light years ? Furthermore, programmer choose to suffix the variable name with data type string. What will happen if you use casting operation then, string will no longer become string.

Good

delay_ms = 10000
elapsed_time_in_days = 12
name = "Jerry"

Conclusions

Good variable name carries information about its usage purpose or origin.

Disinformation

Bad variable names are nothing more than a variables with a magic values. What is worse than bad variable names with magic values are magic values itself.

Bad

state = 0
if state == 1:
  # do something 
  state = 0
else:
  # do something 
  state = 1

In the example above you have no certainty that state is a flag or an actual state. Moreover, you have no idea if state changes value to 3,4 or n further in the code.

Good

state = False
if state is True:
  # do something 
  state = False
else:
  # do something 
  state = True

Conclusions

Use booleans over integers - booleans increase readability and reduce uncertainty.

Ambiguity

Context shapes variable name.

Bad

in_file = open('myfile.dat')
for line in in_file:
  if "$" in line:
    print("US & A")

In the example above you might think that in_file is a good name for a variable that stores content of a file.
Look what happens when the context changes. Usage of in_file in the for loop makes the entire 2nd line sounds like a broken English.

Good

data_file = open('myfile.dat')
for line in data_file:
  if "$" in line:
    print("US & A")

Conclusions

Think of context where the variable is used and based on it create/change variable name.

Conflicted names

Good variable name is short and informative.

Bad

max = 32
min = -10

In the example above there is no doubt that max and min are good name for a variable that store maximum and minimum values.
However, in python max and min are function names that by default comes with the language.
Therefore, creating min and max variables programmer overrides native functionality of the language.

Good

maximum = 32
minimum = -10

Conclusions

Be aware of reserved words and functions that comes with a programming language.

Clueless distinctions

Have a look at the example below and tell me what's wrong with it ?

Bad

for i in range(0len(data)2):
  print(data[i])

There is nothing wrong with the above example except that it's understandable only by people who know how the range function works.
However, for the people who know nothing about pytohn and range function it's difficult to figure out what 1st, 2nd and 3rd parameter is responsible for.

Good

startstopstep = 0len(data)2
for index in range(startstopstep):
  print(data[index])

Conclusions

Use names that have meaningful distinction, such as source, target, start, stop, step etc.

Function names

"Master programmers think of a system as stories to be told rather than programs to be written"* - Uncle Bob

If variables are containers for data then, functions are actions performed on data. Variables are nouns and functions as verbs.
Good function do one and one thing only - tells one story only. Below you will find perfect cases of bad function names().

Bad

derivative_calculator(xt)
do_stuff()
 
def max(numbers):
  # implementation 
 
def print_full_name(a, b, c):
  print(abc)

The above example shows major flows related to function names.

Good

calculate_derivative(xt)
 
# previously do_stuff() 
notify_users()
 
def maximum(numbers):
  # implementation 
 
def print_full_name(first_name, middle_name, surname):
  print(first_namemiddle_namesurname)

Conclusions

Good function/method name express its' responsibility and functionality. Good function clearly indicates what it does or what it returns.
If you cannot express intention of a function in a one word then use more words - following verb first convention. e.g. set_name(), refresh_access_token().
Good function do one and one thing only and do it well. Never forget to KISS your function.

Comments

Bad code and comments goes together like peas and carrots. When one cannot express himself/herself in code then he/she uses comments.

Bad

# ex.1 mean function 
mean = (num1 + num2) / 2
 
# ex.2 print users addresses with the for loop 
for user in users:
  print(user.address)
 
# ex.3 check if user is club member and is eligible for senior discount 
if user.is_member() and user.age() >= 60:
  discount = 0.50
 
# ex.4 TODO implement hello world function 
hello_world()
 
# ex.5 calculate abs difference between max and min 
# min_number = min(numbers) 
# max_number = max(numbers) 
# min_max_diff = abs(max_number - min_number) 

If you cannot see anything wrong with "# ex.1 mean function" then I have to stop you here. The comment says function however, I cannot see function call or neither function definition.
This comment is an example where implementation != intention making the comment noting more than a lie. Thus, comments are dangerous and can mislead the reader.

"# ex.2 print users addresses with for loop" this text shows case of comments abusing. Comment has no meaningful value and it doesn't explain more than code itself.
Thus, in this case the comment is nothing more than a noise.

"# ex.3 check if user is club member and is eligible for senior discount" this comment provides enough information however, this information is not incorporated to the code.

"# ex.4 TODO implement hello world function" is another example of code noise.
It looks like the hello_world() function was implemented and programmer forgot to remove the comment, by that author is creating a noise.

"# ex.5 calculate abs difference between max and min" this comment has entire section where three lines of code are commented.
If they are commented then it's not compiled. If it's not compiled then nobody uses it, and if nobody uses it why it's still there ? it makes no sense to have a code that is commented. Remove it as soon as your program reach mature state.

In this case I will leave comments in good code as reference. However in reality I would remove them.

Good

# ex.1 
mean = (num1 + num2) / 2
 
# ex.2 
for user in users:
  print(user.address)
 
# ex.3 
if user.eligible_for_senior_member_discount():
  discount = 0.50
 
# OR 
 
user_eligible_for_senior_member_discount = (user.is_member() and user.age() >= 60)
if user_eligible_for_senior_member_discount:
  discount = 0.50
 
# ex.4 
hello_world()

Conclusions

Bad programmer explains him/herself with comments, good programmer explains him/herself with code.
If you can replace comments with explanatory variable of a function then, do it!
Remember to remove commented code when is no longer used.

Code formating

Did you know that nearly, all code read by a computer (interpreter or compiler) is read from left to right from top to bottom ?

I like to think about code as if it was written as a text in a book.
Every line of a code represent a single sentence (a thought, a clue or an expression) and a group of a lines creates a complete paragraph. Each paragraph is separated by a line break

Code formating is the way of distribution code within a file. We have two types of formating Vertical and Horizontal.

Being too cluttered

Can you imagine a book that is written without paragraphs. I'm sure that as a reader you'd have hard time distinguishing where one action starts and other stops.
In one word it would be nightmare to read it and reference to it.
In the example below you will see a code being TOO CLUTTERED which describes the notion of having no paragraphs in the code - thus, no distinction between thoughts.

Bad - Vertical

filename=input("Please enter a filename: ")
columnsum = None #Initiate variable for sum of the columns 
flag = 0
with open(filename'r') as fh:
    for line in fh:
        if flag == 0:
            if line:
                flag = 1
                columnsum = 0
        columnsum+=float(line)
print("The sum of columns in"filename"is"columnsum)

Above code is an example of a vertical code being too cluttered. It's difficult to say where the main thought is and what is the main thought of this code.
In one word code is repulsive. Compare it with refactored version below.

Good - Vertical

total_sum = 0
 
filename = input("Please enter a filename: ")
 
with open(filename) as data_file:
  for number in data_file:
    total_sum += float(number)
 
print("The sum of numbers:"total_sum)

In the example above you can easily see where program has space for variables initialization, and for the program input.
The main thought (paragraph / code block) of the program just pops to your eyes.
If we come back to the analogy of code being a text within a book and count number of line breaks then, we would count 4 paragraphs within this code.

Below example shows a code being too cluttered in the horizontal way.

Bad - Horizontal

if user.get_account_balance() <= 0send_notification(usertype='email')
 
if condition_1 is True and condition_2 is True# Check if conditions are true 

The example above breaks "one thought per line" principle what, makes it difficult to read since one line contains two thoughts making it hard to distinguish where one thoughts starts and other ends.

Good - Horizontal

if user.get_account_balance() <= 0:
  send_notification(usertype='email')
 
# Check if conditions are true 
if condition_1 is True and condition_2 is True:
  # do something 
Being too loose

Lets get back to the analogy of code being a text written within a book. Can you imagine a book that each new sentence starts with a paragraph and each paragraph is separated by a line break (or two). If yes then you can see that a book would count 4000 pages instead of 200. Once again it would be nightmare to read it.

Example below illustrates a code being TOO LOOSE, which describes the notion of having too many paragraphs in the code. Again code gives repulsive impression.

Bad

x = int(input('Enter a number: '))
 
if x > 0:
 
    for i in range(1x + 1):
 
        prod = prod * i
 
    print ("The product of number"x"is"prod)
 
 
elif x == 0:
    print ("The product of 0 is 1")
 
 
else:
    print ("Error")

Good

try:
 
    number = int(input('Enter a number: '))
    assert number >= 0
 
    prod = 1
    if number > 1:
        for i in range(1number + 1):
            prod = prod * i
 
    print('The product of 'number'is'prod)
 
except ValueError:
  print('Invalid value provided')
 
except AssertionError:
  print('Number cannot be negative')

Conclusions

Code formating has tremendous impact on the code readability. Bad formating impedes code comprehension, good formating facilitates reading the code. Keep the trade of don't be too loose or too tight be just right.

Anti-patterns

Anti-pattern is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive.

In this section I will show you anti-patterns. Contrary to previous examples this section only show the bad code and shortly tells why is wrong with it.
Additionally, I mention several anti-patterns that have no code representation but it would be harmful not to know them.

Code Smell

Blind Faith

Spaghetti Code

Race Condition

Yo-yo problem

Error Handling

Duplicated Code

if condition:
    for i in range(1(data + 1)):
        the_sum = i + the_sum
    print ('The sum is'the_sum)
else:
    for i in range(data0):
        the_sum = i + the_sum
    print ('The sum is'the_sum)

Too Loose Code

if x > 0 :
    for i in range (1x+1) :
 
        prod = prod * i
 
    print ("The product of number"x"is"prod)
 
elif x == 0 :
    print ("The product of 0 is 1")
 
 
elif x < 0 :
    print ("Error")

Magic Numbers

total = 1.08 * price

Hard coded values

ROWCOUNT = 20
for i in range(0ROWCOUNT):

Coding by exception

try:
  weather = get_weather()
except WheaterExceptionError:
  weather = 'undefined'

Following no convention

# link: https://www.python.org/dev/peps/pep-0008/ 
reversecomplementstrand
print ('test')
if condition:
  # do something 
elif condintion2
  # do something 2 

Conclusions

Familiarize yourself with Anti-patterns and avoid them by all costs!