The Gilded Rose refactoring Kata is a famous exercise used to teach some principles of Object Oriented design. In addition to finding this Kata really fun, I think it illustrates perfectly 3 very important aspects of real world software development: the need for a robust test suite, when to refactor your code and how to refactor your code. Are you ready? Let's dive in!
The owner of the Gilded Rose inn, Allison, sells some of the finest goods (aged bries, backstage passes, sulfuras and normal goods). Those goods have 3 attributes:
- The name of the good
- A quality integer which denotes how valuable the good is
- A sell in integer which represents the number of days before the sell by date
Unfortunately, the goods are constantly degrading in quality as they approach their sell by date and of course, the rules of the quality changes per day are different for each type of good.
To keep track of her inventory, Allison uses a legacy software that is in charge of keeping track of the quality and number of days before the sell by date. She was really happy with it until now, because she needs a new feature. She hires you as a developer to add a new type of "conjured" goods to her inventory management tool.
If you want to read the full description of the Kata, you will find the complete set of requirements here.
Well there are 2 things you should know about Allison's inventory management tool:
- The application is almost only composed of a single 46 lines method full of conditionals
- There are no tests
Now I want you to have a look at this method and imagine having to add a new set of rules for a new good here. How does it make you feel? The answer is probably terrified of breaking everything without even knowing it and you are right! The first part if you want to solve this Kata is to write good tests.
In real applications, the complexity naturally grows just like in the Gilded Rose Kata. Without a robust test suite, you will inevitably break things, get a lot of errors in production and frustrate your users.
Before she needed this new feature, Allison was really happy with her inventory management tool. Refactoring was not needed, in fact, there was no need to change anything before the new requirement. At this point, you should not refactor your code because you only risk making things worse by breaking a tool Allison is happy working with. If you as the developer are not working on this part of the application, all you need to do is safely ignore this ugly code even if you could "improve" it.
The new requirement however changes everything. Should you start by a refactoring or should you add the new feature while keeping the old "architecture"? Here, you really have two ways to go. You could, as Kent Beck said "make the change easy then make the easy change" and refactor the code first or you could just add the new feature in a quick and dirty way.
There are no absolute rules here.
What is the cost of refactoring? Maybe it's not worth your time if the code almost never changes. Maybe the code isn't that clean but still manageable. What is the cost of not refactoring? Will I lose even more time trying to add my new feature in poorly designed code than refactoring and then adding the new feature? What is the long term impact of not refactoring? Am I likely to pay a small cost frequently that will compound and become much bigger than to refactor now?
In our Gilded Rose Kata however the answer is obvious. You will really struggle to add the new feature in this untangled mess of conditionals. It will be even longer than to add a reliable test suite, make the change easy by applying refactoring techniques to the legacy code and only then make the easy change.
The main ideas in Object Oriented design are always the same. You want to have small objects with a single responsibility that knows as little as possible about one another. The refactoring technique you should use here is called replace conditional with polymorphism. The main idea here is that instead of having conditionals everywhere, you will have one factory method centralizing the conditions and in charge of generating an instance of the right type of good, where each type of good has its own class implementing the same interface.
If you want to learn more about refactoring techniques, here is the link to the amazing talk of Sandi Metz during RailsConf 2014 that inspired this article. If you like this kind of refactoring Katas, you should definitely check her great book 99 bottles of OOP, it is almost the same exercise and it's really fun to read!
Thanks for reading! If you liked this article, you can subscribe to my newsletter, I try to write an article once a month!