Regions Code Smell: A Dangerous Approach to Write the Code?


Keeping your code organized is one of the most essential aspects of writing good software. This is especially true for any company that wants to keep its coding standards clean, elegant and consistent across applications and in different programming languages.

Using #regions in code can be a good or a bad thing. If you are new to it, it may seem like a great thing to have in the code. But is #region really a good practice?

Regions are collapsible blocks of code that help developers easily navigate and understand the code. However, improper use of the #region has made it an anti-pattern or a code smell.

This article discusses regions, how to use them in code, whether they can become software anti-patterns or code smell, and the problems of using #region inside methods. The article also explains the best possible uses of regions in your code.

Are #regions an anti-pattern or code smell?

Before diving deeper into #regions, let’s first understand what a code smell and an anti-pattern mean.

What is a code smell?

A code smell is a potentially deeper problem in the code you can easily spot. However, having code smells does not necessarily mean that they are bugs or technically incorrect code impacting the program’s execution.

code smells

But they violate software design principles that make the program vulnerable to bugs that cause severe problems in the future. Also, the code smells slow down the development process and can become a reason for technical debts.

What is an anti-pattern?

Unlike using software patterns which are reusable solutions often used for making software development efficient, anti-patterns are patterns developers should avoid using.

However, developers often use them because they think they are good practices and provide quick solutions to their problems. Nevertheless, anti-patterns make the code difficult to maintain and can cause unnecessary bugs in the program.

Now that you know code smells and anti-patterns, we will next understand what’ regions’ are and how they become code smells or anti-patterns.

What are regions in code?

Regions are blocks of code collapsible in the code editor and marked using special syntax in C#. For example, your code can use regions to mark different methods such as constructors, static methods, getters, and setters.

By using regions, developers can navigate a long code hiding and revealing those collapsible areas as they want. Also, it can keep the code organized and see an overview of various parts in the code, such as different methods, fields, properties, classes, etc., improving the code readability.

How do you use #region?

In C#, you use the ‘#region’ and ‘#endregion” keyword directives to specify a block of code as a region. Also, the use of a meaningful name with the #region directive indicates the purpose of the code block. Following is a simple example that shows how to use regions in C#.

class RegionExample
{
    #region print name method
    private static void PrintName()
    {
        System.Console.WriteLine("Hello, Suchi!");
    }
    #endregion

    #region execution point
    static void Main(string[] args)
    {
        PrintName();
        System.Console.ReadLine();
    }
    #endregion
}

This declaration enables you to hide the code, which will look like the following in the Visual Studio code editor.

Are #regions an anti-pattern or code smell?

code smells

Although regions seem to be improving the code readability, they can become anti-patterns and code smells if developers use them to hide code that needs to be improved.

  • Regions enable grouping methods and classes that need refactoring.

Since regions allow a programmer to hide larger blocks of code, he can use them to hide a large class or methods that probably need refactoring. For example, you can declare regions inside methods to indicate blocks of codes with different functionalities, which we will discuss in the next section.

That means regions can be used to hide classes and methods that are unnecessarily large and do not serve one purpose but multiple purposes. Also, developers can hide classes and methods without implementation and forget to clean them later, violating good software design practices.

  • Regions enable hiding members that should be refactored into separate classes.

Also, some developers use it for grouping similar members together. Suppose you have a class that has a larger number of fields. In that case, developers may be tempted to hide them inside regions and forget them as you go on with the implementation. However, this is not good software development practice. You should use the Extract Class refactoring instead to reduce the number of fields in a class.

  • Regions persuade developers to group code components only by their visibility. Regions do not encourage programmers to hide code blocks based on their functionality. Developers are tempted to hide the code components based only on what they see but not what those components do in the program.
  • Regions do not have proper usability in relatively shorter codes.

In addition, regions do not have useful purposes in small classes. Because if it is small enough, developers do not have to bother hiding the code.

Hence, using regions can become anti-patterns or code smells. It is harder for developers to notice design issues in code and work towards refactoring them, which can lead to bugs in the program.

The problem with using C# regions inside the method

Developers can use #regions inside methods to indicate different functionalities inside them. Suppose your method does completely different tasks T1 and T2, then you can declare two regions indicating their unique functionalities.

However, on the other hand, according to the Single Responsibility Principle, a method should perform only one specific task and should be as short as possible. Not only methods but the principle also requires the modules and class to have a single responsibility.

If the method becomes lengthier with hundreds of lines of codes (LoC), it is good practice to refactor it by separating the different functionalities into separate methods. Thus if you use regions inside methods, it isn’t easy to see that requirement. Take a look at the following example.

class RegionsInsideMethodsExample
{
    public void ProcessData(int a, string b)
    {
        int max = 5;

        #region validate the parameters
        if (a > max)
        {
            throw new ArgumentOutOfRangeException("Parameter a is out of range.");
        }
        if (b == null)
        {
            throw new ArgumentNullException("b");
        }
        #endregion

        #region process data

        //write code to process the data

        #endregion
    }
}

The method ProcessData takes two parameters, and the first task it does is validate the parameters. After validation steps are completed, the method does its real purpose: to process the data.

If there are more validations, you can use regions to hide them. But it will unnecessarily make the method longer and violates the Single Responsibility Principle. Instead of writing the method like that, the correct way is to use the Extract Method refactoring and split the method into smaller ones. You can write one method to validate the arguments and another to do the actual processing.

class RegionsInsideMethodsExample
{
    public void ProcessData(int a, string b)
    {
        ValidateData(a, b);

        DoDataProcessing(a, b);
    }

    private static void ValidateData(int a, string b)
    {
        int max = 5;

        if (a > max)
        {
            throw new ArgumentOutOfRangeException("Parameter a is out of range.");
        }
        if (b == null)
        {
            throw new ArgumentNullException("b");
        }
    }

    private static void DoDataProcessing(int a, string b)
    {
        //write code to process the data
    }
}

In the above example, two new private static methods exist: ValidateData and DoDataProcessing.

This way of writing code improves the code readability, and you no longer need to declare regions inside methods.

Is there a good use for regions?

Although regions are viewed as anti-patterns or code smells because of the above issues, they still have few good uses. For example:

  • Regions are useful in legacy codebases.

While working on legacy codebases, you may find it difficult to refactor them by breaking large code into multiple methods and classes. Anyway, not all lengthy code is possible to split right away. There is a chance that you might break the existing functionality. Especially if you don’t have tests that characterize how the code behaves.

Therefore, you need to make the code more readable and make it possible to extend its functionality easily. Hence, using regions to mark possible grey areas of the code is a good idea when working with a legacy code.

  • Regions can help in grouping similar templates.

Sometimes you may want to use templates inside code. For example, let’s say your code contains different email templates you must send according to the subject heading. In that case, it will be useful for you to include them inside one file without declaring them separately. Then you can use regions to indicate their distinct purposes so that the developer knows what each template does.

  • Regions can helps novice developers to learn the code.

You cannot always eliminate lengthy existing code. If there is a lengthy code containing related methods and classes, having regions eliminates the need to scroll through the whole page to find the method you need to work on. Therefore, having a show and hide functionality inside code is extremely helpful in finding codes easier and putting fixes faster.

In addition, novice developers may find regions extremely helpful. By looking at the region names, they can understand the source code more easily. But only if the regions are split by functionality and not by member type (where you have a region for private fields, constructor, public methods, private methods).

Conclusion

Regions are blocks of code that you can hide and expand. Although regions help the developer to navigate easily, organize, and understand the code, they could become anti-patterns of code smells if they are used to hide code that requires refactoring.

Regions tempt developers to group code without considering their functionality, hiding methods that do many functions or do not have any implementation, etc.

However, using a region directive inside shorter code that serves only one purpose and legacy code helps improve code quality. In general, the recommendation is to refactor the code as much as possible without using regions.

Recent Posts