Skip to main content
Published on

Writing Clean Code Part 2

Authors

    Examples of Clean Code

    on this second part of the blog, we'll delve into some examples. you'll gain a deeper understanding of the practices and principles that make up clean code, and learn how to apply them in your own projects to improve readability, maintainability, and overall software development experience. Stay tuned as we explore various scenarios and discuss the thought processes behind crafting elegant, efficient, and well-documented solutions.


    1. Choosing Descriptive Names:

    a) Bad

    def calc(x, y):
    	z = x + y * 2
    	return z / 4
    

    b) good

    def calculate_average_order_value(order_total, num_items):
    	average_value = order_total / num_items
    	return average_value
    

    2. Functions

    a) Bad
    Issues:

    • Combines multiple responsibilities: This function calculates order totals, applies discounts, processes payments, and sends emails.
    # Bad example
    def process_user_order(username, items, address, promo_code):
    	total_cost = 0
    	for item in items:
    		total_cost += item['price']
    			if promo_code:
    				total_cost *= 0.9 # Apply 10% discount
    			# Charge the user's credit card (omitted for simplicity)
    			# Send confirmation email (omitted for simplicity)
    

    b) good
    Improvements:

    • Focused responsibilities: Each function has a single, clear purpose.
    • Improved readability: Smaller functions are easier to understand at a glance.
    • Enhanced reusability: Individual functions could now be used in other parts of the application.
    • Better maintainability: Changes to one process (e.g., payment methods) won't affect unrelated logic.
    # Good example
    def calculate_order_total(items):
    	total_cost = 0
    	for item in items:
    		total_cost += item['price']
    		return total_cost
    
    def apply_promo_code(total_cost, promo_code):
    	if promo_code:
    		return total_cost * 0.9
    	else:
    		return total_cost
    
    def process_payment(total_cost):
    	# Charge credit card logic here
    
    def send_confirmation_email(username, order_details):
    	# Send email logic here
    

    3. Commenting

    a) Bad
    Issues:

    • Comments mainly explain elementary operations that are apparent from the code.
    #include <stdio.h>
    
    int main() {
        int x = 5;      // Declare an integer variable named x and assign value 5
        int y = 10;     // Declare and initialize integer variable y with value 10
        int sum = x + y; // Calculate the sum of x and y and store it in 'sum'
    
        printf("The sum is: %d\n", sum); // Print the value of 'sum'
        return 0;        // Return 0 to indicate successful execution
    }
    

    b) good
    Improvements:

    • Explaining the constant: The comment clarifies the purpose of PI.
    • Highlighting the formula: It reminds the reader of the area formula, which might not be immediately recognizable for everyone.
    #include <stdio.h>
    
    int main() {
        const double PI = 3.14159;
        double radius = 5.0;
    
        // Calculate the area (note the use of a constant for PI)
        double area = PI * radius * radius;
    
        printf("The area of the circle is: %f\n", area);
        return 0;
    }
    

    4. Formatting:

    a) Bad
    Issues:

    • Inconsistent indentation: Makes it difficult to visually follow the code's structure.
    • Missing spaces: Everything seems crammed together, hindering readability.
    • Irregular bracing: Inconsistent placement of curly braces makes blocks less clear.
    function calculateArea(width, height) {let result = width * height if (result > 50) {console.log('Area is large') }
      return result
    }
    

    b) good
    Improvements:

    • Consistent indentation: Uses spaces to visually distinguish code blocks.
    • Spacing around operators: Improves visual clarity around mathematical and comparison operators.
    • Consistent brace placement: Braces follow a consistent style for easy identification of blocks.
    function calculateArea(width, height) {
      let result = width * height
    
      if (result > 50) {
        console.log('Area is large')
      }
    
      return result
    }
    

    5. Error Handling:

    a) Bad
    Issues:

    • Error handling mixed with core logic: The try-except blocks interrupt the flow of the main profile processing logic.
    • Multiple exit points: The function can return from different places within the try-except blocks, making it harder to reason about.
    def get_user_profile(user_id):
        try:
            file = open(f"user_{user_id}.json", "r")
            data = file.read()
            profile = json.loads(data)
            file.close()
    
            # Lots of complex processing logic with the profile data...
    
            return profile
        except FileNotFoundError:
            print("User profile not found.")
            return None
        except json.JSONDecodeError:
            print("Invalid JSON data.")
            return None
    
    

    b) good
    Improvements:

    • Separation of concerns: Error handling is moved outside the process_profile_data function.
    • Cleaner core logic: The process_profile_data function focuses solely on profile processing.
    • Context-aware error messages: The top-level function can log errors or provide more specific messages.
    • Using "with" statement: Ensures the file is automatically closed.
    def get_user_profile(user_id):
        try:
            with open(f"user_{user_id}.json", "r") as file:
                data = json.load(file)
                return process_profile_data(data)  # Complex logic extracted
        except FileNotFoundError:
            print("User profile not found.")
        except json.JSONDecodeError:
            print("Invalid JSON data.")
    
    def process_profile_data(data):
        # Lots of complex processing logic with the profile data...
        # (No error handling needed within this function)
    

    6. Unit Tests:

    a) The Code
    code to calculate the factorial of a number.

    // calculateFactorial.js
    function calculateFactorial(num) {
      if (num === 0) {
        return 1
      } else {
        return num * calculateFactorial(num - 1)
      }
    }
    

    b) Unit Test
    The associated code test

    // calculateFactorial.test.js
    const calculateFactorial = require('./calculateFactorial')
    
    // Use 'describe' to group related tests
    describe('calculateFactorial', () => {
      // Use 'it' or 'test' for individual test cases
      it('calculates the correct factorial of 0', () => {
        const result = calculateFactorial(0)
        expect(result).toBe(1)
      })
    
      it('calculates the correct factorial of a positive number', () => {
        const result = calculateFactorial(5)
        expect(result).toBe(120)
      })
    
      it('handles negative numbers', () => {
        expect(() => calculateFactorial(-1)).toThrow()
      })
    })
    

    7. Refactoring:

    a) Un refactored Code

    def calculate_shipping(country, weight, has_insurance):
      base_cost = 5  # Flat rate
      if country == "US":
        shipping_per_kg = 2
      elif country == "Canada":
        shipping_per_kg = 3
      else:
        shipping_per_kg = 5
    
      total_shipping = base_cost + weight * shipping_per_kg
      if has_insurance:
        total_shipping += 10
    
      return total_shipping
    

    b) Refactored Code

    • Extract Country-Specific Logic
    • Simplify Calculation
    def get_shipping_cost_per_kg(country):
        if country == "US":
            return 2
        elif country == "Canada":
            return 3
        else:
            return 5
    
    def calculate_shipping(country, weight, has_insurance):
        base_cost = 5
        total_shipping = base_cost + weight * get_shipping_cost_per_kg(country)
        insurance_cost = 10 if has_insurance else 0
        total_shipping += insurance_cost
        return total_shipping