Skip to main content

· 6 min read
Adnan Rafiq

Refactorings

Any code written in the past is legacy code as soon as adding a new feature or fixing a bug makes it difficult to change it. There is only one cure to this problem: Refactor it, but only one refactoring at a time. Otherwise, you will be frustrated to the least.

Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. Its heart is a series of small behavior preserving transformations. Each transformation (called a 'refactoring') does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it's less likely to go wrong. The system is kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring. Martin Fowler

There is no better refactoring than making the code more readable. After all, to change code, you first have to understand it: by reading it.

Simplify Expressions with Variables

Expressions appear in many places in code like conditions, loops, and mathematical expressions. Complex expressions are hard to read and understand, especially when their parts can be named naturally.

Extracting parts of the expression into variables and giving them natural meaningful names gives you:

  • Better reading experience.
  • Improve debugging experience.

But be careful: Don't get obsessed with extracting variables and start naming it a,b,c and make the code messy.

What is wrong with the below code, and how will you improve it? Will the test pass because you will find it hard to calculate the expected value, why? Because complex expression.

A complex expression requires intense focus - Bad Code
public class Order
{
public int Quantity { get; init; }
public decimal ItemPrice { get; init; }

public decimal Total()
{
// 1. Calculate base price
// 2. Apply 5% discount if quantity is more than 500
// 3. Add 10% shipping cost but not more than 100
return Quantity * ItemPrice -
Math.Max(0, Quantity - 500) * ItemPrice * 0.05m +
Math.Min(Quantity * ItemPrice * 0.1m, 100);
}
}

public class OrderTests
{
[Fact]
public void Total()
{
var order = new Order { Quantity = 100, ItemPrice = 10 };
Assert.Equal(1100, order.Total());
}
}

I spotted a good comment but a bad code. But why rely on the comment when you can make the code self-explanatory. Take a look at the below code and tell me how you feel about it?

A complex expression requires intense focus - Good Code
public class Order
{
public int Quantity { get; init; }
public decimal ItemPrice { get; init; }

public decimal Total()
{
var basePrice = Quantity * ItemPrice;
var discountBasedOnQuantity = Math.Max(0, Quantity - 500) * ItemPrice * 0.05m;
var shippingCost = Math.Min(basePrice * 0.1m, 100);

return basePrice - discountBasedOnQuantity + shippingCost;
}

//Total with more descriptive names
public decimal TotalV2()
{
var basePrice = Quantity * ItemPrice;
var applyFivePercentDiscountWhenMoreThan500 = Math.Max(0, Quantity - 500) * ItemPrice * 0.05m;
var add10PercentShippingButNotMoreThan100 = Math.Min(basePrice * 0.1m, 100);

return basePrice - applyFivePercentDiscountWhenMoreThan500 + add10PercentShippingButNotMoreThan100;
}
}

public class OrderTests
{
[Fact]
public void Total()
{
var order = new Order { Quantity = 600, ItemPrice = 10 };
Assert.Equal(6050, order.Total());
}
}

So far, we have done the following:

  • We avoided variable obsession by not extracting the parts of discountBasedOnQuantity and shippingCost into variables.
  • We gave the variables meaningful names and directly returned the total.
  • The test is still passing.

But it would make more sense to extract the expression if the usage is not local to the method.

Simplify Expressions with Methods

If the Discount is used in other methods or classes, then it would make sense to extract it into a method or property. For example, you would like to display the discount to the user while displaying the order.

Extract Methods when usage is not local - Good Code
public class Order
{
public int Quantity { get; init; }
public decimal ItemPrice { get; init; }

public decimal Total()
{
var basePrice = Quantity * ItemPrice;

var shippingCost = ShippingCost(basePrice);

return basePrice - Discount + shippingCost;
}

//You can totally do same thing with Base Price
private decimal Discount
{
get
{
if (Quantity <= 500) return 0m;

var quantityEligibleForDiscount = Quantity - 500;
var discount = quantityEligibleForDiscount * ItemPrice * 0.05m;
return discount;
}
}

private decimal ShippingCost(decimal basePrice) => Math.Min(basePrice * 0.1m, 100);

}
public class OrderTests
{
[Fact]
public void Total()
{
var order = new Order { Quantity = 500, ItemPrice = 10 };
Assert.Equal(5100, order.Total());
}
}

Don't Mutate Variables

What is wrong with the below code, and how will you improve it?

Mutating variables is a bad idea - Bad Code
public class Order
{
public int Quantity { get; init; }
public decimal ItemPrice { get; init; }

public decimal Total()
{
var total = Quantity * ItemPrice;
total -= Math.Max(0, Quantity - 500) * ItemPrice * 0.05m;
total += Math.Min(total * 0.1m, 100);

return total;
}
}

public class OrderTests
{
[Fact]
public void Total()
{
var order = new Order { Quantity = 100, ItemPrice = 10 };
Assert.Equal(1100, order.Total());
}
}

The variable total is mutated over and over again. It makes it hard to read and understand the code. Do not do that and instead introduce a variable with meaningful name for each step, as we did in the previous examples.

Variable Location Matters

What is wrong with the below code, and how will you improve it?

Variable location matters - Bad Code
public class Order
{
private const decimal DiscountRate = 0.05m;
private const decimal ShippingRate = 0.1m;
private const int DiscountQualifiedQuantity = 500;

public int Quantity { get; private set; }
public decimal ItemPrice { get; private set; }

public decimal Total()
{
var total = Quantity * ItemPrice;
if (total > 1000)
{
total = total - Math.Max(0, Quantity - DiscountQualifiedQuantity) * ItemPrice * DiscountRate;
}
total = total + Math.Min(basePrice * ShippingRate, 100);

return total;
}
}

The problem with the above code is that it is using constants which are declared outside the method when they are only used inside the method. It is introducing unnecessary in-direction and hurting readability. Or simply said, scrolling.

The solution is to move the variables to closer to where they are used.

Variable location matters - Good Code
public class Order
{
public int Quantity { get; private set; }
public decimal ItemPrice { get; private set; }

public decimal Total()
{
const decimal discountRate = 0.05m;
const decimal shippingRate = 0.1m;
const int discountQualifiedQuantity = 500;

var total = Quantity * ItemPrice;
if (total > 1000)
{
total = total - Math.Max(0, Quantity - discountQualifiedQuantity) * ItemPrice * discountRate;
}
total = total + Math.Min(basePrice * shippingRate, 100);

return total;
}

· 3 min read
Adnan Rafiq
Timeout in ASP.NET Core 8

No built-in support for Request Timeout before .NET 8

There is no default way to control the request timeout in ASP.NET Core 6.0 or 7.0. But the ASP.NET Core 8.0 introduces a RequestTimeout Middleware. It supports policy convention/pattern to apply the timeout options on a specific endpoint or globally. You will find it familiar if you have configured CORS using the CORS middleware or any other middleware. But if you love extension methods, you are in luck, as it supports extension methods on the Builder.

· 3 min read
Adnan Rafiq
How to use Serilog with Application Builder

Logging

Logs are facts emitted by your application especially when it is running in production.

Structured logging is a way to give facts a shape so that you can query logs better.

Serilog is a structured logging library for .NET and it is one of the most popular logging libraries in .NET.

But how to use Serilog with the new .NET 8 Worker template?

· 5 min read
Adnan Rafiq

Middleware is a function in then ASP.NET 8, when many functions are invoked one after the other like a chain; it becomes a middleware pipeline. The middleware pipeline is responsible for handling the incoming HTTP request and outgoing HTTP response.

For in depth understanding of the middleware you can read my blog post here.

This is a series of blog posts in which I will cover all the builtin middlewares in ASP.NET 8.

· 5 min read
Adnan Rafiq
Validate Options on Startup

Validate Options<T> on Startup in .NET 8

The .NET 8 Host Builder allows you to bind configuration with C# objects by using AddOptions<T> and binding to the configuration.

It provides you an opportunity to validate the configuration values when the host (WebApplication or Hosted Server) is starting by using ValidateOnStart.

But there are two interesting aspects of it, which I will explain in this post.

· 16 min read
Adnan Rafiq
Diagnostic Middlewares

Middleware is a function in then ASP.NET 8, when many functions are invoked one after the other like a chain; it becomes a middleware pipeline. The middleware pipeline is responsible for handling the incoming HTTP request and outgoing HTTP response.

For in depth understanding of the middleware you can read my blog post here.

This is a series of blog posts in which I will cover all the builtin middlewares in ASP.NET 8.

· 8 min read
Adnan Rafiq
Validate Options on Startup

Validate Options<T> on Startup

The .NET 8 Host Builder allows you to bind configuration with C# objects by using AddOptions<T> and binding to the configuration.

It provides you an opportunity to validate the configuration values when the host (WebApplication or Hosted Server) is starting by using ValidateOnStart.

But there are two interesting aspects of it, which I will explain in this post.

· 15 min read
Adnan Rafiq
Title Image of the blog

Middleware is a function in then ASP.NET 8, when many functions are invoked one after the other like a chain; it becomes a middleware pipeline. The middleware pipeline is responsible for handling the incoming HTTP request and outgoing HTTP response.

For in depth understanding of the middleware you can read my blog post here.

This is a series of blog posts in which I will cover all the builtin middlewares in ASP.NET 8.

· 6 min read
Adnan Rafiq
Title Image of the blog

Middleware is a function in then ASP.NET 8, when many functions are invoked one after the other like a chain; it becomes a middleware pipeline. The middleware pipeline is responsible for handling the incoming HTTP request and outgoing HTTP response.

For in depth understanding of the middleware you can read my blog post here.

This is a series of blog posts in which I will cover all the builtin middlewares in ASP.NET 8.

· 11 min read
Adnan Rafiq
Title Image of the blog

The .NET migration from ASP.NET Framework 4.x to ASP.NET6 or upcoming ASP.NET8 is going to be on the rise in the next few years due to different reasons. Your reasons may vary from pure business value to upgrading the stack to use better tools & resources. Some teams may even do it to reduce the bill or for the heck of it. All are valid reasons.

One of the team with my current Employer is migrating a .NET Framework 4.x web application to .NET6 which serves up to 4K concurrent users. Before going to production, there is a process to certify the release by running a load test for at least 1X of the expected load which is 4K concurrent users in this application.

During the load test application started to experience a huge number of errors, mainly two types:

  • Sql Connection pool ran out of connections.
  • A key web service started throwing exceptions while communicating with the service. It is a SOAP service which is consumed using WCF Core Client Proxy with basicHttpBinding.

The ASP.NET Framework 4.x version of the application did not experience these problems. So what changed in .NET6 and how will you find out what is wrong? Because there are thousands of different requests at play.

Youtube Video

I have recorded a detailed YouTube video if you prefer the video content.

Detect ASP.NET Core Hangs