A humble lesson in maintainability

A while back I needed to process some data that came from a webservice, a weather API. As you may know, these services usually have far more data then you generally need. In my case I just needed the temperature and humidity forecasts. The data needed to be processed so it could be plotted in a chart. Basically, you’d have one signal for temperature and one for humidity. (For simplicity, I just do an output to the console to simulate the processing of the data.)

So, here was my solution.

class Program
{
	static void Main(string[] args)
    {
		var data = new List<WeatherData>() {
			new WeatherData(){ Temperature = 15, Humidity = 65, UVindex = 6, WindDirection = "NNO", WindSpeed = 20 },
			new WeatherData(){ Temperature = 16, Humidity = 65, UVindex = 6, WindDirection = "O", WindSpeed = 35 },
			new WeatherData(){ Temperature = 19, Humidity = 45, UVindex = 8 , WindDirection = "W", WindSpeed = 45 },
			new WeatherData(){ Temperature = 13, Humidity = 75, UVindex = 3, WindDirection = "NO", WindSpeed = 65 } 
		};

        ProcessTemp(data);
        ProcessHumidity(data);
                                 
        Console.ReadLine();
    }

    static void ProcessTemp(List<WeatherData> data)
    {
        foreach (var item in data)
            Console.WriteLine($"{nameof(item.Temperature)}: {item.Temperature} °C");
    }

    static void ProcessHumidity(List<WeatherData> data)
    {
        foreach (var item in data)
            Console.WriteLine($"{nameof(item.Humidity)}: {item.Humidity} %");
    }
}

It worked, check-in, time to get a beer...

As you guessed, it got rejected.
What if the customer decides to track windspeed. Just create another method to process that parameter? And another and another and …. You see where this is going.

The tip was to use a selector, like they do with the Linq extensions.

The new code looked like this:

class Program
{
    static void Main(string[] args)
    {
        var data = new List<WeatherData>() {
            new WeatherData(){ Temperature = 15, Humidity = 65, UVindex = 6, WindDirection = "NNO", WindSpeed = 20 },
            new WeatherData(){ Temperature = 16, Humidity = 65, UVindex = 6, WindDirection = "O", WindSpeed = 35 },
            new WeatherData(){ Temperature = 19, Humidity = 45, UVindex = 8 , WindDirection = "W", WindSpeed = 45 },
            new WeatherData(){ Temperature = 13, Humidity = 75, UVindex = 3, WindDirection = "NO", WindSpeed = 65 } 
		};


        ProcessData(data, d => d.Temperature, d => nameof(d.Temperature), "°C");
        ProcessData(data, d => d.Humidity, d => nameof(d.Humidity), "%");
        ProcessData(data, d => d.UVindex, d => nameof(d.UVindex));
        ProcessData(data, d => d.WindSpeed, d => nameof(d.WindSpeed), "km/h");

        Console.ReadLine();
    }

    static void ProcessData(List<WeatherData> data, Func<WeatherData, int> selector, Func<WeatherData, string> propertyName, string unit = null)
    {
        foreach (var item in data)
            Console.WriteLine($"{propertyName(item)}: {selector(item)} {unit}");
    }
}

Ok, the signature of the method got a bit more complex, but if you’re used to working with Linq, this is not as daunting as it looks. But imagine the processing of the data happens in some other assembly, maybe even written by someone else. You would need to edit 2 files (or tell someone to update their API), adding a method to your business logic and wherever you’d implement that method (UI, service, whatever).
With the new method, I don’t need to edit the business side and just implement the method. Only 1 place to change a single line of code, also just 1 method to test and maintain.

This example gave me a better understanding of Linq and changed my view on what is a called “a simple implementation” of something. Many lightbulbs were gotten 😁