Sprouting in legacy code

Sprouting in legacy code

Sprout method

Sprout Method is a technique that one can use when one has to write a new feature in an existing code. The reason for Sprout method is that sometimes you cannot write unit test for the code that you want to change but you can use TDD to write unit test for the new code, so that at least the new code would be tested!

Example

public class TransactionGate{
    ...
    var orderItems = GetOrderItems();
    
    foreach(var orderItem in orderItems)
        transactionBundle.Add(orderItem);
    ...
}

Now a new change is requested on the legacy code

Now a new task demands to filter out order items that are not already in the TransactionBundle object, we do it by sprouting out our code (function) from the existing code!

public class TransactionGate{
  ...
  var orderItems = GetOrderItems();
  
  var uniqueOrderItems = GetUniqueOrderItems(orderItems);
  
  foreach(var orderItem in uniqueOrderItems)
    transactionBundle.Add(orderItem);     
}

public List<OrderItem> GetUniqueOrderItems(List<OrderItem> orderItems){
    ...
}

We created a method GetUniqueOrderItems with one parameter; a list of order items. Now we can initiate class and write tests for new code. It is recommended to use Test Driven Development to develop a Sprout method itself.

Sprout Class

When you must extend a large method with a sequence of instructions and the class is heavily dependent on different objects, it can be better to extract the sequence of instructions into a new class method.

This is more ideal when we want to make sure that Single Responsibility Principle is not violated anywhere

Example

Below we have an existing code/legacy code to create an order. Now we have a new requirement saying that when creating an order, We also need to add a VAT amount (a type of tax) if the order is from a non-individual, a company.

Existing code

public class OrdersFacade{
  private IExternalCustomerApiService customerApiService;
  private IOrderRepository orderRepository;
  ...
       
  public OrdersFacade(
    IExternalCustomerApiService customerApiService,
    IOrderRepository orderRepository,
    ...
    )
    {
    this.customerApiService = customerApiService;
    this.orderRepository = orderRepository;
    ...
    }
  
  public bool CreateOrder(){
    ...
     
    var customerInfoInJson = customerApiService.GetCustomerInfo(customerId);
    var customerInfo = JSonConvert.Serialize(customerInfoInJson);
    order.CustomerName = customerInfo.Name;
    
    orderRepository.Save(order);
    return true;
    }
}

Step 1: Let’s develop a completely separated class, preferably using TDD.

public class OrderVatCalculator{
  public Order AddVatIfNotCompany(Order order, bool isCompany)
  {
    decimal vat = 1.21;
    if(!isCompany){
      order.Price = order.Price * vat;
    }
  }
}

Step 2: Let’s initialize an object of Sprout class in existing code, pass parameters into its method, and let a method do the required expression.

public class OrdersFacade{
  private IExternalCustomerApiService customerApiService;
  private IOrderRepository orderRepository;
  ...
       
  public OrdersFacade(
    IExternalCustomerApiService customerApiService,
    IOrderRepository orderRepository,
    ...
    )
    {
    this.customerApiService = customerApiService;
    this.orderRepository = orderRepository;
    ...
    }
  
  public bool CreateOrder(){
    ...
     
    var customerInfoInJson = customerApiService.GetCustomerInfo(customerId);
    var customerInfo = JSonConvert.Serialize(customerInfoInJson);
    order.CustomerName = customerInfo.Name;
    
    var orderVatCalculator = new OrderVatCalculator();
    
    orderVatCalculator.AddVatIfCompany(order, customerInfo.IsCompany);
    
    orderRepository.Save(order);
    return true;
    }
}

The new piece of instructions is covered by tests and has only one responsibility. It is very easy to read and maintain!

A good reading resource:

Working Effectively with Legacy Code, by Michael Feathers, EBook

Last updated