How To Write Effective Code

Introduction

The target audience of this article is everyone; a person with 25 years of experience and a person with 25 days of experience. The art of writing effective code is difficult to learn and takes patience from the person who is writing it.

There are two perspectives to writing code. One is to think about the present and get the work done – which is easier and quicker; the other one is to chase the future and make the code reusable, self explanatory, and efficient for others. The former approach is widely chosen when deadlines are at stake while the later is limited to POCs, GitHub projects etc. Let’s deep dive into the nitty-gritty details about effective code writing and how it can be useful for us.

Effective Code

The definition of “effective code” is very broad but in layman's terms, it means the code you write today will work if reused 10 years later. It seems like an absurd statement but consider the scenario of System Namespace. It was written with .NET Framework 2.0 way back in 2005. We still use this namespace in our code with .NET Framework 4.7 in the year 2017. Do you think, they rewrite all the classes from scratch every time they release a new version? I don’t think so. They simply reuse the existing code and write only the new functionalities for every new release. This is what effective code looks like.

How to Write Effective Code

Before jumping to the key features of writing efficient code, let’s see a common scenario where most people fail to choose the right approach to write code and how it impacts the end result. Consider the problem stated below and we’ll try to find out its best solution:

Problem

Write a program to add two numbers.

Description

Whenever I’m taking an interview or talking to fellow developers about best practices in programming, one question I always ask is about the approach to add two numbers. The problem is straightforward, simple, and can be solved with 2/3 lines of code.

Solution

99.9% of the answers I received are like this,

Step 1 - Declare three variables int a, int b, int c

Step 2 - Pass user inputs to the first two variables i.e. a, b

Step 3 - Assign the sum of a, b to c i.e. c = a + b;

Step 4 - Return c.

Program

  1. class Program {  
  2.     static void Main(string[] args) {  
  3.         int a = 10;  
  4.         int b = 10;  
  5.         int c = 0;  
  6.         c = Add(a, b);  
  7.     }  
  8.     public static int Add(int a, int b) {  
  9.         int c = a + b;  
  10.         return c;  
  11.     }  
  12. }  

If this is the answer in your mind, then do read the article till the end. The above written code is good and will work for all the integer values. But is this code efficient? Let’s find out.

Scenario 1

If I pass the inputs as 15 and 25, this program will return me 40. That’s correct and some of the programmers have the imagination limit up to this point while writing the code.

Scenario 2

If I pass the inputs as 15.6 and 22.7, will this program work? Not at all, our written code will fail to execute this scenario, so is the imagination of the programmer while designing the algorithm of this program. The above code is not efficient. Where do you think is the lag. Choosing the data types? Not considering the second scenario? Hold on to your thoughts till the end of the article and carry on reading.
 

Attributes of an Effective Code

I have read articles written by MVPs, authors, senior architects and have seen many expert videos about the coding styles and programming practices one should consider before writing code. Almost all of them focus on the same points which are as follows:

1. Understanding the Requirement

Before even writing a single line of code, a programmer should be absolutely clear about the requirement. If not, ask questions until it is crystal clear. If this simple thing is considered in the above explained scenario, people will never use int as the data type.

In my question, I have never asked to add two integer numbers. My requirement is way too generic – “Write a program to add two numbers”. People should ask me questions, what kind of numbers? Are they only integers? What about real numbers and millions of other questions?

2. Self Explanatory and Reusable

This is the most important point because 8 out of 10 programmers work in teams. They write code for a project on which other people are also working. Until and unless a programmer is working solo on a project which is rare, whatever he writes must be readable & reusable to others.

Let’s take the example of the above scenarios and assume that the Client wants a program to add two integer numbers. In this case, is the code self explanatory and reusable?

Program

  1. class Program    
  2. {    
  3.     static void Main(string[] args)    
  4.     {    
  5.         int a = 10;    
  6.         int b = 10;    
  7.         int c = 0;    
  8.     
  9.         c = Add(a, b);    
  10.     }    
  11.     
  12.     public static int Add(int a, int b)    
  13.     {    
  14.         int c = a + b;    
  15.         return c;    
  16.     }    
  17. }    

First thing to check is can I understand this program without involving its owner. The function name is Add. What does it describe? Add what? As I noticed the parameters of the method are of integer type, I’ll make a guess that it is a program to add two integer numbers. I’m able to make a guess only because this is a very simple example.

What if the method is of 100 lines and does Addition of time (seconds) and return minutes. In that case, the output of Add(60, 60) is 2. Hence our random guess failed to understand the code. Somewhere I read – “Readability > Cleverness” which totally fits in here.

Second thing to check is reusability. Can I reuse it? Reusability is a broad subject but for the sake of this example, can I implement this code or can I inherit it? To reuse this in my code, I have to inherit the whole Program class which is not possible because of two things – What if the accessibility modifier is private or what if some of the methods of Program are not meant to be inherited.

The efficient approach is to create an interface, define the method signature, implement it to a class and then use it as follows,

  1. namespace EffectiveCode    
  2. {    
  3.     class Program    
  4.     {    
  5.         private static void Main(string[] args)    
  6.         {    
  7.             int a = 10;    
  8.             int b = 10;    
  9.             int c = 0;    
  10.     
  11.             IAdd add = new Add();    
  12.             c = add.AddTwoNumber(10, 0);    
  13.             Console.WriteLine(c.ToString());    
  14.             Console.ReadKey();    
  15.         }    
  16.     }    
  17.     
  18.     interface IAdd    
  19.     {    
  20.         int AddTwoNumber(int firstNumber, int secondNumber);    
  21.     }    
  22.     
  23.     class Add : IAdd   
  24.     {    
  25.         public int AddTwoNumber(int firstNumber, int secondNumber)    
  26.         {    
  27.             return firstNumber + secondNumber;    
  28.         }    
  29.     }    
  30. }   

Here, if I have to reuse the code, or to implement it on my own, I don’t have the dependency of Program class. This is what (Single Responsibility Principle) of the famous design principles SOLID says.

3. Coding Style

A long time ago, I wrote a blog post about the two types of programmers in this world. The link for the blog is here

As I’m a big Silicon Valley person, there is a whole episode of the show dedicated to the famous “tab vs. space” debate in programming. Here is the link

The importance of choosing a coding style is consistency. If you don’t have a coding style and still write code, trust me you need help. Coding style makes your code readable and easy to understand. Let’s take an example to understand more.

  1. /// <summary>    
  2. /// Programming Without a Coding Style    
  3. /// </summary>    
  4. class Program    
  5. {    
  6.     private int MyProperty1 { get; set; }    
  7.     private int MyProperty2 { get; set; }    
  8.     private int MyProperty3 { get; set; }    
  9.     public int MyProperty4;    
  10.     public const string MyProperty5 = "test";    
  11. }    
  12.     
  13. /// <summary>    
  14. /// Programming Using a Coding Style    
  15. /// </summary>    
  16. class Program    
  17. {    
  18.     private int _myProperty1 { get; set; }    
  19.     private int _myProperty2 { get; set; }    
  20.     private int _myProperty3 { get; set; }    
  21.     
  22.     public int myProperty4;    
  23.     
  24.     public const string MYPROPERTY5 = "test";    
  25. }  

Here, the first example uses no coding style. We cannot identify which item has what significance. In the second example, I have used the Standard Coding style where the private properties has the naming convention of “_” prefix with camel case, the class variables are written in camel case and the constant variables are written in ALL CAPS. You can see it is very easy to read and differentiate if you follow a coding style. The getaway from this guideline is to use a coding style and be consistent with it throughout the project.

4. Meaningful Code Commenting

One thing I have learned over the years is code commenting is as important as the code written. I once had a Technical Architect in my project who always reviews the code comments more than the code, reason being the Einstein quote – “If you can't explain it to a six year old, you don't understand it yourself.”

In the initial years of my career, I use to get annoyed writing code comments as it is boring and takes time. You have to think of something meaningful every time, otherwise it is of no use. Over the years, it felt like a mandatory step before checking-in the code. If I take the above example and provide coding comments to the code, you will see it becomes very easy to read and understand.

  1. namespace EffectiveCode    
  2. {    
  3.     /// <summary>    
  4.     /// Program - The startup class of the application    
  5.     /// </summary>    
  6.     class Program    
  7.     {     
  8.         private static void Main(string[] args)    
  9.         {    
  10.             int a = 10;    
  11.             int b = 10;    
  12.             int c = 0;    
  13.     
  14.             IAdd add = new Add();    
  15.             c = add.AddTwoNumber(10, 0);    
  16.             Console.WriteLine(c.ToString());    
  17.             Console.ReadKey();    
  18.         }    
  19.     }    
  20.     
  21.     /// <summary>    
  22.     /// IAdd Interface  
  23.     /// </summary>    
  24.     interface IAdd    
  25.     {     
  26.         int AddTwoNumber(int firstNumber, int secondNumber);    
  27.     }    
  28.     
  29.     /// <summary>    
  30.     /// Add Class - Implementation of the IAdd Interface     
  31.     /// </summary>    
  32.     class Add : IAdd   
  33.     {    
  34.         /// <summary>    
  35.         /// This method will take two integer type parameters and returns the sum as an integer type    
  36.         /// </summary>    
  37.         /// <param name="firstNumber">Takes the parameter as integer type</param>    
  38.         /// <param name="secondNumber">Takes the parameter as integer type</param>    
  39.         /// <returns>Returns the result as integer type</returns>    
  40.         public int AddTwoNumber(int firstNumber, int secondNumber)    
  41.         {    
  42.             return firstNumber + secondNumber;    
  43.         }    
  44.     }    
  45. }    

5. Best Practices

This is more of an expertise rather than the guideline. If you are fresher and still in learning phase, you will write code with more lines and less functionality whereas as your experience grows and you learn best practices, your lines of code will automatically reduce for the same functionality. Let’s take the same above example:

Beginner Approach

  1. /// <summary>    
  2. /// Method Implementation of AddTwoNumber    
  3. /// </summary>    
  4. /// <param name="firstNumber">Takes the parameter of integer type</param>    
  5. /// <param name="secondNumber">Takes the parameter of integer type</param>    
  6. /// <returns>Returns the result of integer type</returns>    
  7. public int AddTwoNumber(int firstNumber, int secondNumber)    
  8. {    
  9.     int result = 0;    
  10.     result = firstNumber + secondNumber;    
  11.     return result;    
  12. }   

Experienced Approach

  1. /// <summary>    
  2. /// Method Implementation of AddTwoNumber    
  3. /// </summary>    
  4. /// <param name="firstNumber">Takes the parameter of integer type</param>    
  5. /// <param name="secondNumber">Takes the parameter of integer type</param>    
  6. /// <returns>Returns the result of integer type</returns>    
  7. public int AddTwoNumber(int firstNumber, int secondNumber)    
  8. {    
  9.     return firstNumber + secondNumber;    
  10. }    

You can see, both the example are correct and will yield the same result. The only difference is saving memory by avoiding “result” variable and reducing the lines of code.

6. Debug and Trace

This is critical and should be used efficiently. Recently I developed an application and tested it on my system. It was working perfectly but as soon as I move the code to production, the application stopped. What options do I have to troubleshoot my code if my code was not efficient? None. Consider the above example and assume that you have published the code to the production.

  1. namespace EffectiveCode    
  2. {    
  3.     /// <summary>    
  4.     /// Program - The startup class of the application    
  5.     /// </summary>    
  6.     class Program    
  7.     {    
  8.         private static void Main(string[] args)    
  9.         {    
  10.             int a = 10;    
  11.             int b = 10;    
  12.             int c = 0;    
  13.     
  14.             IAdd add = new Add();    
  15.             c = add.AddTwoNumber(10, 0);    
  16.             Console.WriteLine(c.ToString());    
  17.             Console.ReadKey();    
  18.         }    
  19.     }    
  20.     
  21.     /// <summary>    
  22.     /// IAdd Interface    
  23.     /// </summary>    
  24.     interface IAdd    
  25.     {    
  26.         int AddTwoNumber(int firstNumber, int secondNumber);    
  27.     }    
  28.     
  29.     /// <summary>    
  30.     /// Add Class - Implementation of the IAdd Interface     
  31.     /// </summary>    
  32.     class Add : IAdd    
  33.     {    
  34.         /// <summary>    
  35.         /// Method Implementation of AddTwoNumber – This method will take two integer type parameters and     
  36.         /// returns the sum as an integer type    
  37.         /// </summary>    
  38.         /// <param name="firstNumber">Takes the parameter of integer type</param>    
  39.         /// <param name="secondNumber">Takes the parameter of integer type</param>    
  40.         /// <returns>Returns the result of integer type</returns>    
  41.         public int AddTwoNumber(int firstNumber, int secondNumber)    
  42.         {    
  43.             return firstNumber + secondNumber;    
  44.         }    
  45.     }    
  46. }  
Let’s assume the user has entered the wrong inputs and your application crashes displaying the whole “YSOD” exception to the user. How will you handle the situation?

The best approach is to enable tracing and tons of other functionalities but what I have learned over the years to use the “mix-shake” of try-catch with logging as the first level of solution in fail-case scenarios. This can be a debatable statement but I have used it in multiple projects and have been beneficial each time.

The modified code will look like this,

  1. namespace EffectiveCode    
  2. {    
  3.     /// <summary>    
  4.     /// Program - The startup class of the application    
  5.     /// </summary>    
  6.     class Program    
  7.     {    
  8.         private static void Main(string[] args)    
  9.         {    
  10.             try    
  11.             {    
  12.                 int a = 10;    
  13.                 int b = 10;    
  14.                 int c = 0;    
  15.     
  16.                 IAdd add = new Add();    
  17.                 c = add.AddTwoNumber(10, 0);    
  18.                 Console.WriteLine(c.ToString());    
  19.                 Console.ReadKey();    
  20.             }    
  21.             catch (Exception ex)    
  22.             {    
  23.                 Logger.Log(ex.InnerException.ToString());    
  24. #if DEBUG    
  25.                 throw;    
  26. #endif    
  27.   
  28. #if RELEASE    
  29.                 //Don't throw, log it    
  30.                 //throw;    
  31. #endif    
  32.             }    
  33.     
  34.         }    
  35.     }    
  36.     
  37.     /// <summary>    
  38.     /// IAdd Interface - A skeleton of AddNumber functionality    
  39.     /// </summary>    
  40.     interface IAdd    
  41.     {    
  42.         int AddTwoNumber(int firstNumber, int secondNumber);    
  43.     }    
  44.     
  45.     /// <summary>    
  46.     /// AddNumber Class - Implementation of the IAdd Interface     
  47.     /// </summary>    
  48.     public class Add: IAdd    
  49.     {    
  50.     
  51.         /// <summary>    
  52.         /// Method Implementation of AddTwoNumber – This method will take two integer type parameters and     
  53.         /// returns the sum as an integer type    
  54.         /// </summary>    
  55.         /// <param name="firstNumber">Takes the parameter of integer type</param>    
  56.         /// <param name="secondNumber">Takes the parameter of integer type</param>    
  57.         /// <returns>Returns the result of integer type</returns>    
  58.         public int AddTwoNumber(int firstNumber, int secondNumber)    
  59.         {    
  60.             try    
  61.             {    
  62.                 return firstNumber + secondNumber;    
  63.             }    
  64.             catch (Exception ex)    
  65.             {    
  66.     
  67.                 Logger.Log(ex.InnerException.ToString());    
  68. #if DEBUG    
  69.                 throw;    
  70. #endif    
  71.   
  72. #if RELEASE    
  73.                 return 0;    
  74.                 //Don't throw, log it    
  75.                 //throw;    
  76. #endif    
  77.             }    
  78.         }    
  79.     }    
  80.     
  81.     /// <summary>    
  82.     /// Logger Class -This is used to log messages    
  83.     /// </summary>    
  84.     public static class Logger    
  85.     {    
  86.         /// <summary>    
  87.         /// Log - This method will log the messages    
  88.         /// </summary>    
  89.         /// <param name="messageram>    
  90.        public static void Log(string message)    
  91.         {    
  92.             //Log Message    
  93.         }    
  94.     }    
  95.     
  96. }   
You can see if there is any exception in the code, it will get logged and we can troubleshoot the cause of it. I have also used the #if directives and I’m throwing the exceptions only in Debug Mode while testing the code locally but when it is in Release Mode, it will swallow the exception and log it.

7. Unit Testing

The last and most important guideline in the whole article is Unit Testing. Unit Testing, in layman's terms means to be the critique of your own code.

Unit testing is considered the best practice to identify unknown scenarios that can fail your code. If you have to develop something, you first think about its usability and where it will reside in future. Based on the assumptions, you start writing code but if you involve unit testing during the analysis phase, it will help to identify more scenarios that can be of importance.

To make it simple, consider the same above example one last time and see how unit testing had saved the code from crash if used. During the analysis of the problem – “Write a program to add two numbers”, if I have written only 3-4 test cases, I would have identified the problem.

Unit Test Case 1

Check the program for null check scenarios. (If I pass Add(0, null))

Unit Test Case 2

Check the program for negative values. (If I pass Add(-3, -5))

Unit Test Case 3

Check the program for invalid inputs. (If I pass Add(??, ??)) What are the invalid inputs? I don’t have the exact requirement what data types do I consider? Let’s ask the product owner! Just in three test cases, I have identified that I don’t have the clear requirement and need to ask more questions related to the requirement. This is what unit testing does to you.

Poi
nt of Interest

I’m sure until now you have seen the clear picture of what effective code looks like and why it is important to write it. It is very important for a developer to consider these points while writing code. It will always help in the long run and improve you as a developer. I hope this article helps you to look at the broader perspective of programming.

In future, if I met you and ask the same question, don’t be that 99.9 %. Until then, try to put a ding in the universe!


Similar Articles