Code That Embraces Changes

Developers write code. They write code for specific functionalities, with requirements from clients (customers, bosses, leaders,…). When requirements come, they know what to do; know what code to write. In the world of business applications, the implementation is not a big deal. The story might be different if we write code for embedded systems using C/C++.

However, most of the problems come later, when the first draft of the function has done. The functions have deployed to the customers. They test and realize that they have to change here and there. If I have to pick one constant in the software development, I would pick CHANGE. The clients will change their mind. They know better when they actually see the product. You, as a developer, cannot avoid changes and cannot tell clients stop changing. No. It does not work that way.

Agile emerges with the spirit: Embrace changes. A wonderful mindset shift! We have to adapt that fact. However, the biggest question remains: How do you deal with changes?

How do you deal with changes in code?

I know that there are processes to deal with changes. But, ultimately, it all comes down to the code. You have to change the code, most of the time you have to.

I do not have a single answer or solution to that problem. However, I do have some experience in designing code that gives me the courage, the tool to embrace changes.

Here we go. But first, let’s define a simple feature. We have to work on a concrete example.

Requirement

As a business owner, I want to export orders (that are made today) into CVS file so that I can import into Excel and build some charts.

Assumption: There is a shopping system. They take orders from users. The orders are stored in the database. They are all basic stuff if you have ever worked with a business application system.

First Implementation

Without any hesitation, jump right into the VS2017 and implement the feature. Btw, how hard it is to implement such a simple feature, right?

    public class Order
    {
        public string Customer { get; set; }
        public DateTime OrderedAt { get; set; }
        public decimal TotalCost { get; set; }
        public int NumberOfProducts {get;set;}
    }
    class Program
    {
        static void Main(string[] args)
        {
            var ordersFromDatabase = new List<Order>
            {
                new Order{Customer = "Batman", TotalCost = 100000, OrderedAt = DateTime.Today.AddDays(-3)},
                new Order{Customer = "Superman", TotalCost = 1000, OrderedAt = DateTime.Today.AddDays(-2)},
                new Order{Customer = "Spiderman", TotalCost = 100, OrderedAt = DateTime.Today.AddDays(-1)}
            };
            const string headers = "Customer;Total Cost;Ordered At";
            var data = new List<string>{headers};
            foreach (var order in ordersFromDatabase)
            {
                data.Add(string.Join(";", order.Customer, order.TotalCost, order.OrderedAt));
            }
            var fullPathToCsvFile = @"d:\export\orders.csv";
            File.WriteAllLines(fullPathToCsvFile, data, Encoding.Unicode);
        }
    }

The actual implementation in a real project will be different. To demonstrate the point, I simplify the code.

What do we have above?

  1. An order has 3 information: Customer, Total cost, and Ordered At.
  2. There are 3 orders for Batman, Superman, and Spiderman. Assuming that they come from the database. Batman is a rich man so he ordered a lot 😛
  3. Export to CVS with “;” deliminator.
  4. The file is located at “d:\export\orders.csv” with Unicode encoding.

The code works. Mission accomplished.

Changes Come

Here are some changes that a customer might want to make

  1. Add NumberOfProducts into the export
  2. Change the location of exported file or even the encoding.
  3. Hey. I want to see reports of 2 days instead of just today.
  4. ….

Things go on in various ways. Finally, they all come to you, developers, to reflect the changes in code. If the application is just a simple stupid console app in this example, there is not any problem at all. Everything can be changed in a matter of minutes.

But we all know that it is not true in the real life.

Embrace Changes

I would prefer to do some logical analysis before actually writing code. If I start my code first, it will affect my thinking process. Sometimes it helps.

To embrace changes, we, first, must identify the possible changed areas. Isolate and Conquer approach

  1. Identify the possible changed areas
  2. Isolate them. Mean you can conquer them. Because you know where to look at, where to change the code.

Identify

Next and hardest question is

How to identify those areas?

Answer

Think in process and interaction. Capture concepts and patterns

Back to export order example. What does it mean? How do I apply in that example?

What is the process of exporting order?

  1. Get the orders
  2. Build data
  3. Write data into CVS file
Export orders process
Export orders process

Obviously, the process will not change. Because we control how we want to implement it. In other words, it does not depend on the customer. What changes? The 3 boxes: Get orders, Build data and Write to file.

Let’s go through each of them

Get Orders

  • Where to get them?
  • How to get them?
  • Criteria

Build data

  • How many columns do we need?
  • What is the format of currency, datetime, … ? the presentation of the data

Write to file

  • What deliminator to use? “;” or “,”? Maybe directly to Excel file?
  • Where to store the file? in which encoding?
  • What if the file fails to save due to permission?

Let’s say we have identified them. Now, let’s isolate them.

Isolate

I will modify the code as below

    class Program
    {
        static void Main(string[] args)
        {
            var orders = GetOrders();
            var data = BuildData(orders);
            WriteToFile(data);
        }

        private static IList<Order> GetOrders()
        {
            return new List<Order>
            {
                new Order{Customer = "Batman", TotalCost = 100000, OrderedAt = DateTime.Today.AddDays(-3)},
                new Order{Customer = "Superman", TotalCost = 1000, OrderedAt = DateTime.Today.AddDays(-2)},
                new Order{Customer = "Spiderman", TotalCost = 100, OrderedAt = DateTime.Today.AddDays(-1)}
            };
        }

        private static IList<IList<string>> BuildData(IList<Order> orders)
        {
            // Initialize rows with header
            var rows = new List<IList<string>>
            {
                new List<string> {"Customer", "Total Cost", "Ordered At"}
            };
            // Add rows data for each order
            foreach (var order in orders)
            {
                rows.Add(new List<string>
                {
                    order.Customer,
                    order.TotalCost.ToString(CultureInfo.CurrentCulture),
                    order.OrderedAt.ToString(CultureInfo.CurrentCulture)
                });
            }
            return rows;
        }

        private static void WriteToFile(IList<IList<string>> data)
        {
            const string deliminator = ";";
            var fullPathToCsvFile = @"d:\export\orders.csv";
            var csvData = data.Select(a => string.Join(deliminator, a))
                .ToList();
            File.WriteAllLines(fullPathToCsvFile, csvData, Encoding.Unicode);
        }
    }

Each method in the Main class encapsulates a concept, area that we have mentioned. Whether we should move them into separated classes is not that important. But yes, you should move them when in production code.

With that in place, when clients want to change something, you are more confident to embrace them. Or when there are bugs. In the beginning of the post, I mentioned that clients want to add NumberOfProducts into the report. Do you know where to change? Yeah! You got it – change in the BuildData method.

The above refactoring is good to embrace changes. However, we can make it better. I leave it to you if you wish to challenge yourself.

Takeaway

Take the “Embrace changes” seriously with you. Train your mindset into the concept. As a developer, we must be aware of that simple fact. Complaining about them will not solve the problem. You will have to deal with it anyway. Better, you should be in proactive position. Changes come! Not a problem!

When implementing a new feature, write new code, I would suggest that you stop for a moment, draw the process, the interaction in your head (better if you do it on paper) before you actually write code. Once you have done that, the possible changes areas emerge. Capture them immediately.

I have a simple process for you

  1. Think about the process, the interaction in normal language. Not the language of the code.
  2. Identify the possible changes areas.
  3. Capture them immediately. You should write them down in the paper.
  4. Write code.
  5. Design patterns, best practices come last.

I hope you enjoy the reading and use them for your next piece of code.

Have a nice day!

Write a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.