Tag Archives: refactoring

How we use data to improve our product development

A few years back at Guestline, we decided to build our own payment integration, that we can offer to our clients with benefits to us and savings for the hotels.

The need for a new design

With the new integrations, we initially worked on the back-end part of the puzzle, but later on, we tackled front-end as well. Our current design had its glory days behind and we know we need to refresh it deeply. Let me show you a small part of the process, which is filling in card details in order to conduct a transaction. It happens when a client is making a reservation over the phone, and a receptionist is making an initial payment with given card details.

This design is far away from perfect and when it comes to the user experience it’s nothing that we came across in modern payment integrations. We knew that the first two steps could be merged together but what about the other two? Are they needed?

We need more data

We asked those questions to various people, starting from developers, through product owners and support, and ending with clients. What was the answer?

We probably don’t need this, but it has always been like that.

We knew we have to measure it ourselves and here is the part where we used Azure Application Insights. We added logging on every step to figure out how users use our application. This stuff can take time if you don’t have the data you need and you need to add measurements yourself. Nevertheless, after a few weeks, we had our answer.

As you see, 97% of times deposit is added and 98% of times, the user doesn’t print the payment confirmation. At this point, we were almost certain that we can make those steps automatic, but we needed to ask those few percent a question.

Why do you use out payment integration like that?

We jumped into calls to our clients and found out, that mostly they just did it as a habit, without any specific purpose. Having that certainty we decided that now, our payment process could be vastly improved.

Now we can merge the first two steps and make the last two automatic. The next thing we did was improve on user experience when filling in card details. Here is how it looked before:

And now the GuestPay version:

This is a single window that the user is going to see and the billing details will be prefilled from the reservation. The card type will be shown based on the card number.

Once the payment is completed, the notification will be shown to inform the user, that the payment was successful and the deposit will be auto-added.

Rolling out the change

Obviously, this is a massive change for the user, so it has to be introduced gradually. We introduced a feature toggle, that we can turn ON or OFF for each hotel separately. We started with just a few sites for a beta rollout and then based on users’ feedback, we added tweaks to the product.

We also put stress on how intuitive the new payment integration is and if the users can complete the payment without confusion or any guidelines from our side. We wanted to minimize the need for training and a potential rise of support calls regarding payments. This is really important when having around 4000 hotels using our product. Any confusion can bring hundreds of support calls, which we really don’t want to generate.

Once we had that confidence that the process works, we could switch more hotels and monitor how users use it. That is also the part where Application Insights came in handy. One of the metrics that were important for us is pay process duration.

We were able to reduce the pay process by half! From 79 seconds to 35 seconds on average. If we multiply that by the number of payments, we get 44 seconds * 29331 = 1290564 seconds, that’s over 358 hours every 7 days.

We were able to save over 51 hours of guest time every day.

That’s 51 hours of receptionist time that can be used to serve guests and 51 hours of guest’s time not spent on a call to the hotel. With hotel difficulties in hiring staff, every process that can be sped up or made automatic is essential.

Summary

When introducing a change in the legacy system, data collection is crucial. You can ask your clients for feedback but that would always be subjective. Statistics don’t lie, but you need to ask the right questions and interpret them correctly. Remember that a man with a dog has on average 3 legs each.

Gathering data to guide development change will improve your product utility. Getting feedback from the client is one of the hardest things to achieve, but with the proper measurements, you can generate that feedback yourself. This could answer the question of whether a change is progressing in the right direction or give you a hint of what needs to be closely looked at.

Refactoring with reflection

There are often times when you need to do a refactoring, that Resharper cannot help you with. In my last post I described how regular expressions can be useful: Static refactoring with Visual Studio regular expressions

This time the case is different and simple replacement would not work in this case.

The default value for a property

I came across the code, where the DefaultValue attribute was heavily used, but actually, not like it should be. The DefaultValue attribute provided by the .Net Framework is useful for code generators to see if the property default value is the same as the current value and if the code for that property should be generated. I don’t want to get into details, but you can have a look at this article for more information.

In my case, I needed to change all the default values to assignments of properties when the class was created. The problem was that this class had more than 1000 properties like this and doing it manually would not only take a lot of time but could potentially introduce bugs.

Let’s have a look at the sample code:

    public class DefaultSettings
    {
        public DefaultSettings()
        {
            // assignments should be here
        }

        [DefaultValue(3)]
        public int LoginNumber { get; set; }

        [DefaultValue("PrimeHotel")]
        public string HotelName { get; set; }

        [DefaultValue("London")]
        public string Town { get; set; }

        [DefaultValue("Greenwod")]
        public string Street { get; set; }

        [DefaultValue("3")]
        public string HouseNumber { get; set; }

        [DefaultValue(RoomType.Standard)]
        public RoomType Type { get; set; }
    }

This is where reflection comes in handy. I could easily identify the attributes on properties for a given type, but how to change that into code?

Let’s write a unit test! A unit test is a small piece of code that can use almost any class from the tested project and what’s the most important – can be easily run. 💪

    [TestFixture]
    public class PropertyTest
    {
        [Test]
        public void Test1()
        {
            var prop = GetProperties();

            Assert.True(true);
        }

        public string GetProperties()
        {
            var sb = new StringBuilder();

            PropertyDescriptorCollection sourceObjectProperties = TypeDescriptor.GetProperties(typeof(DefaultSettings));

            foreach (PropertyDescriptor sourceObjectProperty in sourceObjectProperties)
            {
                var attribute = (DefaultValueAttribute)sourceObjectProperty.Attributes[typeof(DefaultValueAttribute)];

                if (attribute != null)
                {
                    // produce a string
                }
            }

            return sb.ToString();
        }
    }

It’s a simple test that just runs GetProperties method. This method fetches PropertyDescriptorCollection that represents all properties in DefaultSettings class. Then for each of those, we check if they contain DefaultValueAttribute. Now we just need to somehow generate code for every property and write it to StringBuilder. It’s actually simpler than it sounds.

Let’s check how code will behave differently for enums, strings, and other types:

public string GetProperties()
{
    var sb = new StringBuilder();

    PropertyDescriptorCollection sourceObjectProperties = TypeDescriptor.GetProperties(typeof(DefaultSettings));

    foreach (PropertyDescriptor sourceObjectProperty in sourceObjectProperties)
    {
        var attribute = (DefaultValueAttribute)sourceObjectProperty.Attributes[typeof(DefaultValueAttribute)];

        if (attribute != null)
        {
            if (sourceObjectProperty.PropertyType.IsEnum)
            {
                sb.AppendLine($"{sourceObjectProperty.Name} = {sourceObjectProperty.PropertyType.FullName.Replace("+", ".")}.{attribute.Value};");
            }
            else if (sourceObjectProperty.PropertyType.Name.Equals("string", StringComparison.OrdinalIgnoreCase))
            {
                sb.AppendLine($"{sourceObjectProperty.Name} = \"{attribute.Value}\";");
            }
            else
            {
                var value = attribute.Value == null ? "null" : attribute.Value;
                sb.AppendLine($"{sourceObjectProperty.Name} = {value};");
            }
        }
    }

    return sb.ToString();
}

For an enum, we need to provide a name with namespace and an enum value after a dot. For strings, we need to add apostrophes and for nullable types, we need to provide null. For others – we just assign the attribute value.

Are you curious if that would work? 🤔

Not too bad, not too bad at all 🙂 

If I paste that into my constructor, it will look like this:

    public DefaultSettings()
    {
        LoginNumber = 3;
        HotelName = "PrimeHotel";
        Town = "London";
        Street = "Greenwod";
        HouseNumber = "3";
        Type = PrimeHotel.Web.Models.RoomType.Standard;
    }

Guess what? It works!

The summary

Reflection is not considered as the best practice but it is a very powerful tool. In this example, I showed how to use it and invoke that code in a very simple way – as a unit test. It’s not the code that you would commit, but for a try and error process, it’s great. If you came across a manual task like this in your code, try to hack it 😎

I hope that you learned something with me today. If so, happy days 🍺

Don’t forget to subscribe to the newsletter to get notifications about my next posts. 📣

 

 

The urge for refactoring

Recently in my team at work, we focus on maintaining older micro-services. While this might not be the most exciting job to do, it is an opportunity to work on a developer craftsmanship. A micro-service or any code that you write, can be old after a year or even a half, cause our developer habits changes. Not only technology goes forward, but we tend to use different nuget packages and in result write the same code in a different way.

Refactoring, which I’m referring to in this post, can be playful, can be fun, but it needs to be done with caution. And foremost, we cannot go too far with it, cause drawing a line here is not a child’s play.

Simplest application possible

Here is a very simple API that fetches a user from the database and fills in his description from a different REST service. Code is written in .Net Core.

[Route("api/[controller]")]
public class UsersController : Controller
{
    private readonly IConfiguration _configuration;

    public UsersController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet("{userId}")]
    public async Task<IActionResult> Get(int userId)
    {
        try
        {
            var conf = _configuration.GetSection("ConnectionStrings")["Blog"];
            using (var connection = new SqlConnection(conf))
            {
                var user = await connection.QueryFirstOrDefaultAsync<UserDto>(
                    "SELECT [Id], [Name], [LastUpdatedAt] FROM [Users] WHERE Id = @Id",
                    new { Id = userId }).ConfigureAwait(false);

                var userDesctiption = await GetUserDescription(userId);

                return Json(
                    new {
                        Id = user.Id,
                        Name = user.Name,
                        LastModified = user.LastModified,
                        Description = userDesctiption
                });
            }
        }
        catch (Exception)
        {
            return StatusCode(500);
        }
    }

    private async Task<string> GetUserDescription(int userId)
    {
        var client = new HttpClient();
        var response = await client.GetAsync($"users/{userId}/description");
        return await response.Content.ReadAsStringAsync();
    }
}

As you see it almost looks as a rookie developer might write it, but it’s not that bad – configuration is injected with an interface IConfiguration.

What is bad here?

  • There’s no abstractions – you cannot just swap parts of the code to different implementations. It might be useful for example to use abstraction over HttpClient
  • Everything is in one class – Single Responsibility rule is non-existent
  • One method does multiple things – hard to test
  • It’s not written in a modular way, as an experienced developer might expect it

Have a look at projects structure – it is really minimal:

Those are the most obvious things that should be fixed. Let’s go step by step.

Database and REST calls should have it’s own classes

So I moved that to separate classes and this is how controller looks like:

[Route("api/[controller]")]
public class UsersController : Controller
{
    private readonly IUsersRepository _usersRepository;
    private readonly IUserDescriptionClient _userDescriptionClient;

    public UsersController(IUsersRepository usersRepository, IUserDescriptionClient userDescriptionClient)
    {
        _usersRepository = usersRepository;
        _userDescriptionClient = userDescriptionClient;
    }

    [HttpGet("{userId}")]
    public async Task<IActionResult> Get(int userId)
    {
        try
        {
            var user = await _usersRepository.Get(userId);
            var userDesctiption = await _userDescriptionClient.GetUserDescription(userId);

            return Json(user);
        }
        catch (Exception)
        {
            return StatusCode(500);
        }
    }
}

UsersRepository now looks very decent:

public class UsersRepository : IUsersRepository
{
    private static class SqlQueries {
        internal static string GetUser = "SELECT [Id], [Name], [LastUpdatedAt] FROM [Users] WHERE Id = @Id";
    }

    private readonly IConfiguration _configuration;

    public UsersRepository(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public async Task<UserDto> Get(int userId)
    {
        var conf = _configuration.GetSection("ConnectionStrings")["Blog"];
        using (var connection = new SqlConnection(conf))
        {
            var user = await connection.QueryFirstOrDefaultAsync<UserDto>(
                SqlQueries.GetUser,
                new { Id = userId }).ConfigureAwait(false);

            return user;
        }
    }
}

UserDescriptionClient is still very minimal:

public class UserDescriptionClient : IUserDescriptionClient
{
    public async Task<string> GetUserDescription(int userId)
    {
        var client = new HttpClient();
        var response = await client.GetAsync($"users/{userId}/description");
        return await response.Content.ReadAsStringAsync();
    }
}

And project structure:

This is a level of refactoring that I feel comfortable with. The code is nicely decoupled, easy to test and read. However, as a project gets larger you can refactor more to have a more shared code. If you then jump to a small project, you might want to do things ‘the right way’, so the code is ready for future. You will use your best approaches from previous projects – but isn’t that going too far?

Let’s go further

First thing I did is create a base class for my UserDescriptionClient:

public abstract class BaseClient<T> where T : class
{
    public async Task<T> Get(string uri)
    {
        var client = new HttpClient();
        var response = await client.GetAsync(uri);

        if (response.IsSuccessStatusCode)
        {
            var contentAsString = await response.Content.ReadAsStringAsync();

            if (typeof(T) == typeof(string))
            {
                return contentAsString as T;
            }

            return JsonConvert.DeserializeObject<T>(contentAsString);
        }

        throw new System.Exception($"Could not fetch data from {uri}");
    }

    public async Task Post(string uri, T data)
    {
        var client = new HttpClient();
        var response = await client.PostAsync(
            uri,
            new StringContent(JsonConvert.SerializeObject(data), System.Text.Encoding.UTF8, "application/json"));

        if (!response.IsSuccessStatusCode)
        {
            throw new System.Exception($"Could not post data to {uri}");
        }
    }
}

And UserDescriptionClient now gets very simple:

public class UserDescriptionClient : BaseClient<string>, IUserDescriptionClient
{
    public async Task<string> GetUserDescription(int userId)
    {
        return await Get($"users/{userId}/description");
    }
}

We can do very similar thing with UsersRepository – create a base class

public abstract class BaseRepository
{
    private readonly IConfiguration _configuration;
        
    public BaseRepository(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    internal IDbConnection GetBlogConnection()
    {
        var conf = _configuration.GetSection("ConnectionStrings")["Blog"];
        return new SqlConnection(conf);
    }
}

And now users repository looks like this:

public class UsersRepository : BaseRepository, IUsersRepository
{
    private static class SqlQueries {
        internal static string GetUser = "SELECT [Id], [Name], [LastUpdatedAt] FROM [Users] WHERE Id = @Id";
    }
        
    public UsersRepository(IConfiguration configuration) : base(configuration) {}

    public async Task<UserDto> Get(int userId)
    {
        using (var connection = GetBlogConnection())
        {
            var user = await connection.QueryFirstOrDefaultAsync<UserDto>(
                SqlQueries.GetUser,
                new { Id = userId }).ConfigureAwait(false);

            return user;
        }
    }
}

We also can add more layers – there has to be a service between controller and repository.

Project tree looks like this:

And I just got started. There is actually much more that you can do:

  • introduce more folders so that interfaces are in a separate directory
  • create factories for everything:
    • preparing controller answer
    • preparing a request to REST service
    • creating url for REST service
    • creation of User instance
  • move folders to separate libraries
  • and much more…

It all depends on your imagination, but notice one thing – it didn’t actually add value to the project.

Is refactoring always good?

My refactored project is better designed and decoupled, but you never know which direction project might go. It is a threat when implementing completely new micro-service. You can implement whatever you want in the beginning, but you want to implement as much as possible so that the next developer will have an easier job to do. But would it be really easier? Trying to figure out why you wrote so much code for such a little value. In fact reading and understanding bigger project just takes more time than it should.

Did I get too far with refactoring? What do you think?

 

You can find code posted here on my GitHub: https://github.com/mikuam/MichalBialecki.com-refactorings