Coding Principles - DRY and KISS

The basic rules and ideas that guide the design of software code are known as coding principles. These criteria assist programmers in writing code that is scalable, legible, and error-free. Following coding best practices can lead to more stable and long-lasting software.

We already discussed the SOLID concept in the last post, so let’s move on to other principles in this area. Let us now look at the principles.

  • DRY - Don’t Repeat Yourself
  • KISS - Keep it Simple, Stupid

DRY: (Don’t Repeat Yourself)

The DRY (Don’t Repeat Yourself) principle is a fundamental coding principle that emphasizes the importance of avoiding code duplication in software development. Instead of duplicating the same code in multiple places within a codebase, developers are encouraged to create reusable functions, methods, or classes to encapsulate common functionality.

To apply the DRY principle effectively, developers should identify patterns of duplicated code and refactor them into reusable components. These components can take the form of functions, methods, classes, or even libraries and frameworks, depending on the programming language and the nature of the code.

Use Case Overview

Our use case involves the automated sending of emails under specific conditions. Whenever user details are saved or updated in the database, an email should be sent to the Admin Team. In case of any exceptions during this process, the Tech Support Team should also be notified via email. Similarly, when an order is placed, the Admin Team should be informed to initiate order processing, and any exceptions related to orders should trigger an email notification to the Tech Support Team.

namespace PrinciplesPart2.DRYNotApplied
{ 
    public class UserModel
    {
        // USER PROPERTIES
    }
    public class OrderModel
    {
        // ORDER PROPERTIES
    }
    public class UserBusinessLogic
    {
        public UserBusinessLogic()
        {
 
        }
 
        public void SaveUpdateUserDetails(UserModel userModel)
        {
            try
            {
                // Save or Update User details in the Database.
 
                // Send Mail to Admin
                MailMessage mailMessage = new MailMessage("[email protected]", "[email protected]");
                SmtpClient smtpClient = new SmtpClient();
                smtpClient.Send(mailMessage);
            }
            catch (Exception ex)
            {
                MailMessage mailMessage = new MailMessage("[email protected]", "[email protected]");
                mailMessage.Body = ex.Message;
                SmtpClient smtpClient = new SmtpClient();
                smtpClient.Send(mailMessage);
            }
        }
    }
    public class OrderBusinessLogic
    {
        public OrderBusinessLogic()
        {
 
        }
        public void OrderUpdate(OrderModel orderModel)
        {
            try
            {
                // Update details in the Database.
 
                // Send Mail to Admin
                MailMessage mailMessage = new MailMessage("[email protected]", "[email protected]","Order Placed","");
                SmtpClient smtpClient = new SmtpClient();
                smtpClient.Send(mailMessage);
            }
            catch (Exception ex)
            {
                MailMessage mailMessage = new MailMessage("[email protected]", "[email protected]");
                mailMessage.Body = ex.Message;
                SmtpClient smtpClient = new SmtpClient();
                smtpClient.Send(mailMessage);
            }
        }
    }
}

Challenges in the above code

One significant issue we face is redundancy in our codebase. For each of these business logic scenarios, we’ve written separate code segments to send emails. This redundancy has led to a total of 14 lines of code, and the duplication percentage exceeds 100%.

Implementing the DRY Principle

We’ve taken steps to adhere to the DRY (Don’t Repeat Yourself) principle in our codebase. To achieve this, we’ve introduced a new class called “EmailUtil.” This class is responsible for handling email sending operations through a static method called “SendWithMailMessage.” This method accepts a parameter of type MailMessage for email content and delivery.

By adopting this approach, we’ve successfully reduced code duplication in our existing business logic. This change not only enhances maintainability but also aligns with the Single Responsibility Principle (SRP). Furthermore, we’re working towards extending this design to support the Open-Closed Principle (OCP) and the Interface Segregation Principle (ISP) in the future.

See below code,

namespace PrinciplesPart2.DRYApplied
{
    public class UserModel
    {
        // USER PROPERTIES
    }
    public class OrderModel
    {
        // ORDER PROPERTIES
    }
 
    public class EmailUtil
    {
        public static void SendWithMailMessage(MailMessage message)
        {
            SmtpClient smtpClient = new SmtpClient("",587);
            smtpClient.Send(message);
        }
    }
 
    public class UserBusinessLogic
    {
        public UserBusinessLogic()
        {
 
        }
 
        public void SaveUpdateUserDetails(UserModel userModel)
        {
            try
            {
                // Save or Update User details in the Database.
 
                // Send Mail to Admin
                EmailUtil.SendWithMailMessage(new MailMessage("[email protected]", "[email protected]","",""));
               
            }
            catch (Exception ex)
            {
                EmailUtil.SendWithMailMessage(new MailMessage("[email protected]", "[email protected]"));
            }
        }
    }
    public class OrderBusinessLogic
    {
        public OrderBusinessLogic()
        {
 
        }
        public void OrderUpdate(OrderModel orderModel)
        {
            try
            {
                // Update details in the Database.
 
                // Send Mail to Admin
                EmailUtil.SendWithMailMessage(new MailMessage("[email protected]", "[email protected]", "Order Placed", ""));
              
            }
            catch (Exception ex)
            {
                EmailUtil.SendWithMailMessage(new MailMessage("[email protected]", "[email protected]"));
            }
        }
    }
}

KISS (Keep it Simple, Stupid)

The KISS principle, which stands for “Keep ISimple, Stupid,” is a design and programming principle that advocates for simplicity in all elements of software development, including code, architecture, and user interface design. The KISS principle is based on the idea of avoiding needless complexity and keeping things as simple as possible while yet achieving the intended functionality. The KISS will give flexibility to easy to human readable code structure.

This approach can be utilized in a variety of situations:

  • Code Simplicity: The KISS principle encourages developers to avoid overcomplicating solutions while writing code. It recommends employing simple and easy-to-understand algorithms and structures. Complex code can be challenging to maintain, debug, and understand, resulting in errors and extra development time.
  • Architecture Simplicity: The KISS principle in software architecture advocates against constructing unnecessarily complicated, confusing structures. Architects should instead strive towards simple, well-organized designs. Complex architectures might result in poor performance, scalability concerns, and higher project risks.
  • Project Management: The KISS principle applies to project management as well. Maintaining clear project plans and processes might help to minimize excessive bureaucracy and streamline development efforts.

The KISS concept does not imply that all software should be simple or uncomplicated. Rather, it implies that complexity should be justified by actual needs and advantages. When presented with various alternatives, developers and designers should choose the simplest one that fits the project’s requirements.

Following the KISS concept has the following advantages:

  • Improved Maintainability: Because simple code and architecture are more predictable and have fewer moving parts, they are easier to maintain.
  • Bugs are reduced: Complex code is more prone to bugs and errors. Code simplification can lead to more reliable software. Simpler solutions are frequently easier to implement and debug.
  • Improved User Experience: Simple and intuitive interfaces result in improved user experiences.

ComplexCodeMethodOne(int gameType): This method is currently responsible for extracting game types from input values. However, it is challenging to maintain and prone to code growth issues when new game types are introduced.

/// <summary>
/// using IF .. else if... else statment it will hard if conditions are grow.
/// </summary>
/// <param name="gameType"></param>
/// <returns></returns>
public string ComplexCodeMethodOne(int gameType)
{
    if (gameType == 1)
    {
        return "Indoor Game";
    }
    else if (gameType == 2)
    {
        return "Outdoor Game";
    }
    else
    {
        return "NA";
    }           
}

ComplexCodeMethodTwo(int gameType): Similar to the first method, this one also retrieves game types. However, it suffers from complexity in terms of code maintenance and error handling.

/// <summary>
/// Its array index finder, it will create unwanted errors and complex of code in future.
/// </summary>
/// <param name="gameType"></param>
/// <returns></returns>
public string ComplexCodeMethodTwo(int gameType)
{
    string[] gameTypes = new string[2] { "Indoor Game", "Outdoor Game" };
    if ((gameType - 1) == 1 || (gameType - 1) == 0)
    {
        return gameTypes[(gameType - 1)];
    }
    return "NA";
}

KISS_SimpleCodeMethod(int sportsType): This method follows the “Keep It Simple, Stupid” (KISS) principle. It is designed for easy understanding and readability, reducing code complexity. Both humans and compilers find it straightforward.

/// <summary>
/// Using Switch Statement for simple and easy understand.
/// </summary>
/// <param name="sportsType"></param>
/// <returns></returns>
public string KISS_SimpleCodeMethod(int sportsType)
{
     switch (sportsType)
    {
        case 1:
            return "Traditional Sports";
        case 2:
            return "Tag Games";
        case 3:
            return "Water Sports";
        case 4:
            return "Team Sports";
        case 5:
            return "Adventure Sports";
        case 6:
            return "Playground Games";
        case 7:
            return "Gardening Games";
        // Will be add more cases
        default:
            return "NA";
    }
}

KISS_AdvancedCodeMethod(int sportsType): Similar to the previous method, this one adheres to the KISS principle, making it easy to comprehend and minimizing code complexity.

/// <summary>
/// Using Switch Expression in C# advance concept for simple and sweet
/// </summary>
/// <param name="sportsType"></param>
/// <returns></returns>
public string KISS_AdvancedCodeMethod(int sportsType)
{
    return sportsType switch
    {
        1 => "Traditional Sports",
        2 => "Tag Games",
        3 => "Water Sports",
        4 => "Team Sports",
        5 => "Adventure Sports",
        6 => "Playground Games",
        7 => "Gardening Games",
        // Will be add more cases
        _ => "NA",
    };
}

Full Code is below,

namespace PrinciplesPart2
{
    public class KISSPrinciple
    {
        public KISSPrinciple()
        {
           
        }
 
        /// <summary>
        /// using IF .. else if... else statment it will hard if conditions are grow.
        /// </summary>
        /// <param name="gameType"></param>
        /// <returns></returns>
        public string ComplexCodeMethodOne(int gameType)
        {
            if (gameType == 1)
            {
                return "Indoor Game";
            }
            else if (gameType == 2)
            {
                return "Outdoor Game";
            }
            else
            {
                return "NA";
            }           
        }
 
        /// <summary>
        /// Its array index finder, it will create unwanted errors and complex of code in future.
        /// </summary>
        /// <param name="gameType"></param>
        /// <returns></returns>
        public string ComplexCodeMethodTwo(int gameType)
        {
            string[] gameTypes = new string[2] { "Indoor Game", "Outdoor Game" };
            if ((gameType - 1) == 1 || (gameType - 1) == 0)
            {
                return gameTypes[(gameType - 1)];
            }
            return "NA";
        }
 
        /// <summary>
        /// Using Switch Statement for simple and easy understand.
        /// </summary>
        /// <param name="sportsType"></param>
        /// <returns></returns>
        public string KISS_SimpleCodeMethod(int sportsType)
        {
             switch (sportsType)
            {
                case 1:
                    return "Traditional Sports";
                case 2:
                    return "Tag Games";
                case 3:
                    return "Water Sports";
                case 4:
                    return "Team Sports";
                case 5:
                    return "Adventure Sports";
                case 6:
                    return "Playground Games";
                case 7:
                    return "Gardening Games";
                // Will be add more cases
                default:
                    return "NA";
            }
        }
 
        /// <summary>
        /// Using Switch Expression in C# advance concept for simple and sweet
        /// </summary>
        /// <param name="sportsType"></param>
        /// <returns></returns>
        public string KISS_AdvancedCodeMethod(int sportsType)
        {
            return sportsType switch
            {
                1 => "Traditional Sports",
                2 => "Tag Games",
                3 => "Water Sports",
                4 => "Team Sports",
                5 => "Adventure Sports",
                6 => "Playground Games",
                7 => "Gardening Games",
                // Will be add more cases
                _ => "NA",
            };
        } 
    }
}

Here is a list of overall benefits:

  • Code Reusability
  • Code Maintenance
  • Readability
  • Debugging and Testing
  • Scalability
  • Consistency

We’ll go into more detail regarding YAGNI (You Ain’t Gonna Need It) in the upcoming post.

Thanks,

Source Code: Vinoar/PrinciplesPart2 (github.com)


Similar Articles