Umamaheswaran

Personal Blog


Unraveling the Tapestry of Code Complexity: Exploring the Web of Small Things

Introduction

In the realm of software development, complexity is an ever-present challenge. While we often associate complexity with large-scale architectural designs or intricate algorithms, it is essential to acknowledge the impact of hundreds of small things that intricately weave together to create a tapestry of complexity within our codebases. In this blog, we will dive into the realm of code complexity, shedding light on the myriad small factors that contribute to this phenomenon. Through code examples, we will unravel the intricacies and explore strategies to tame the complexity web.

Long Methods

Long methods are a common source of code complexity. When a method is too long, it can be difficult to understand what it does and how it works. Here is an example of a long method that calculates the total price of a shopping cart:

public decimal CalculateTotalPrice(List<CartItem> cartItems)
{
    decimal totalPrice = 0;
    foreach (var item in cartItems)
    {
        decimal itemPrice = item.Product.Price * item.Quantity;
        if (item.Product.IsOnSale)
        {
            itemPrice *= 0.8m; // 20% discount
        }
        if (item.Product.Category == "Electronics")
        {
            itemPrice *= 1.1m; // 10% tax
        }
        totalPrice += itemPrice;
    }
    return totalPrice;
}

While the logic is straightforward, the method is too long and could be broken down into smaller, more manageable methods.

Here’s an example of how we can do that

public decimal CalculateTotalPrice(List<CartItem> cartItems)
{
    decimal totalPrice = 0;
    foreach (var item in cartItems)
    {
        decimal itemPrice = CalculateItemPrice(item);
        totalPrice += itemPrice;
    }
    return totalPrice;
}

private decimal CalculateItemPrice(CartItem item)
{
    decimal itemPrice = item.Product.Price * item.Quantity;
    if (item.Product.IsOnSale)
    {
        itemPrice *= 0.8m; // 20% discount
    }
    if (item.Product.Category == "Electronics")
    {
        itemPrice *= 1.1m; // 10% tax
    }
    return itemPrice;
}

In this refactored version, we’ve extracted the logic for calculating the price of an individual item into a separate method called CalculateItemPrice. We then call this method for each item in the shopping cart in the CalculateTotalPrice method. This makes the code easier to read and understand, and also makes it easier to test and maintain.

Deep Nesting

Deep nesting is another source of code complexity. When code is nested too deeply, it can be difficult to understand the flow of the code. Here is an example of deep nesting that checks if a customer is eligible for free shipping:

if (cart.TotalPrice >= 100)
{
    if (customer.IsPremium)
    {
        if (customer.Address.Country == "USA")
        {
            if (customer.Address.State == "AK" || customer.Address.State == "HI")
            {
                // Not eligible for free shipping
            }
            else
            {
                // Eligible for free shipping
            }
        }
        else
        {
            // Not eligible for free shipping
        }
    }
    else
    {
        // Not eligible for free shipping
    }
}
else
{
    // Not eligible for free shipping
}

This code checks if a customer is eligible for free shipping based on their cart total, premium status, and address. While the logic is straightforward, the code is nested too deeply and could be refactored to reduce the nesting.

Large Classes

Large classes are another source of code complexity. When a class is too large, it can be difficult to understand what it does and how it works. Here is an example of a large class that manages a customer’s orders:

public class Customer
{
    public List<Order> Orders { get; set; }

    public void PlaceOrder(Order order)
    {
        // Place the order
    }

    public void CancelOrder(Order order)
    {
        // Cancel the order
    }

    public void ReturnOrder(Order order)
    {
        // Return the order
    }

    // 97 more methods...
}

This class has 100 methods, which is too many. The class could be refactored into smaller, more manageable classes.

Complex Conditionals

Complex conditionals are another source of code complexity. When conditionals are too complex, it can be difficult to understand what they do and how they work. Here is an example of a complex conditional that checks if a product is eligible for a discount:

if (product.Price >= 50 && product.Price <= 100 && product.Category == "Clothing" && product.Color == "Red" && product.Size == "M")
{
    // Eligible for discount
}

This code checks if a product is eligible for a discount based on its price, category, color, and size. While the logic is straightforward, the conditional is too complex and could be refactored to be more readable.

Magic Numbers

Magic numbers are hard-coded values that are used in code. They can be a source of code complexity because they are difficult to understand and maintain. Here is an example of a magic number that represents the maximum number of items in a shopping cart:

public const int MaxCartItems = 10;

This code defines a constant that represents the maximum number of items in a shopping cart. While the logic is straightforward, the number 10 is a magic number and could be replaced with a named constant.

Unused Code

Unused code is another source of code complexity. When code is not used, it can be difficult to understand why it is there and what it does. Here is an example of unused code that defines a method for calculating the tax on a product:

public decimal CalculateTax(Product product)
{
    decimal taxRate = 0.1m; // 10% tax
    decimal tax = product.Price * taxRate;
    return tax;
}

This code defines a method for calculating the tax on a product, but the method is not used. The unused method could be removed to reduce code complexity.

Duplication Code

Duplicate code is another source of code complexity. When code is duplicated, it can be difficult to understand what it does and how it works. Here is an example of duplicate code that defines two methods for adding and removing items from a shopping cart:

public void AddToCart(Product product, int quantity)
{
    // Add the product to the cart
}

public void RemoveFromCart(Product product, int quantity)
{
    // Remove the product from the cart
}

This code defines two methods that do similar things. The duplicate code could be refactored into a single method to reduce code complexity.

Inconsistent Naming

Inconsistent naming is another source of code complexity. When names are inconsistent, it can be difficult to understand what they represent and how they are used. Here is an example of inconsistent naming that defines a method for calculating the total price of a shopping cart:

public decimal CalculateTotal(List<CartItem> cartItems)
{
    decimal totalPrice = 0;
    foreach (var item in cartItems)
    {
        decimal itemPrice = item.Product.Price * item.Quantity;
        if (item.Product.IsOnSale)
        {
            itemPrice *= 0.8m; // 20% discount
        }
        if (item.Product.Category == "Electronics")
        {
            itemPrice *= 1.1m; // 10% tax
        }
        totalPrice += itemPrice;
    }
    return totalPrice;
}

This code calculates the total price of a shopping cart, but the method name “CalculateTotal” is inconsistent with the parameter name “cartItems”. The names could be made consistent to improve readability.

Unused Variables

Unused variables are another source of code complexity. When variables are not used, it can be difficult to understand why they are there and what they represent. Here is an example of an unused variable that represents the shipping cost of an order:

public void PlaceOrder(Order order)
{
    decimal shippingCost = CalculateShippingCost(order);
    // Place the order
}

private decimal CalculateShippingCost(Order order)
{
    // Calculate the shipping cost
}

This code calculates the shipping cost of an order, but the variable “shippingCost” is not used. The unused variable could be removed to reduce code complexity.

Overly Complex Logic

Overly complex logic is another source of code complexity. When logic is too complex, it can be difficult to understand what it does and how it works. Here is an example of overly complex logic that checks if a customer is eligible for a loyalty discount:

if (customer.Orders.Count >= 5 && customer.Orders.Sum(o => o.TotalPrice) >= 500 && customer.Orders.All(o => o.Status == OrderStatus.Completed))
{
    // Eligible for loyalty discount
}

This code checks if a customer is eligible for a loyalty discount based on their order history. While the logic is straightforward, the code is overly complex and difficult to understand. The logic could be simplified to reduce code complexity.

Conclusion

Code complexity is an important factor in the maintainability of a codebase. By understanding the sources of code complexity and how to manage them, developers can write code that is easier to understand and maintain.



Leave a comment