Inline-Method refactoring considered under-valued

If you are like me and fell into programmer without proper introductions to the tools of the trade, you may have always wondered what those fancy options in the refactoring menu of your IDE are. One of the refactorings I often-times under-value and under-appreciate is the Inline refactoring. In a recent video from Arjan Egges on his YouTube channel I was reminded about the power that this refactoring sometimes has. Let’s explore.

In his video, Arjan shows a series of refactoring steps to simplify a simple code base. Watch his video to get an introduction and some good tips about how to tackle problems in your code base.

At a certain point, Arjan decides to combine several methods in the Company class of that Python code base. There is a find_vice_presidents() method, a find_support_staff() method, which he combines to a more general find_by_role(role) method Here is the relevant code before his steps:

class Company:
    """Represents a company with employees."""

    def __init__(self) -> None:
        self.employees: list[Employee] = []

...

    def find_managers(self) -> list[Employee]:
        return [e for e in self.employees if e.role == "manager"]

    def find_vice_presidents(self) -> list[Employee]:
        return [e for e in self.employees if e.role == "vice-president"]

    def find_support_staff(self) -> list[Employee]:
        return [e for e in self.employees if e.role == "support"]

    ...

Those look quite similar. Arjan introduces the new method:

    ...

    def find_by_role(self, role: str) -> list[Employee]:
        return [e for e in self.employees if e.role == role]

    ...

Then he deletes the old methods, but has to manually adapt the callers in his main() function in the script. When I first saw that, I started to wonder why he’s not making use of the inline refactoring instead. Here’s how I ended up doing the same without the need to manually adapt the calls in the main() function.

First of all, I replaced the original methods with calls to the new find_by_role() method. Here is the result:

    ...

    def find_managers(self) -> list[Employee]:
        return self.find_by_role("manager")

    def find_vice_presidents(self) -> list[Employee]:
        return self.find_by_role("vice-president")

    def find_support_staff(self) -> list[Employee]:
        return self.find_by_role("support")

    ...

After that I went to each of the methods I wanted to get rid of and chose the inline refactoring from my IDE (I use PyCharm in my instance while Arjan relies on Visual Studio Code. I’m not sure whether VSCode has the automated refactoring available to him.)

After inlining I ended up with basically the same code as Arjan, but felt a little less anxious if I had tried the manual approach – especially if this was a large code base with lots of uses of the original functions.

I think I learned this trick from Joshua Kerievsky in Refactoring to Patterns. I certainly used similar steps when I attended a Coding Dojo several years back in Düsseldorf, Germany, and had the self-identified architect in that dojo surprised by my refactoring steps when getting rid of a circular dependency between two classes.

Does it always work? Frankly, no. Later in the video, Arjan refactors the notification functionality. I tried to apply similar thinking there, but couldn’t pull this off with just relying on the inline refactoring.

Sometimes IDEs decide to do weird stuff with the inline as well. I recall instances during my Rust journey where I tried the same trick with inline, but for all the parameters the inline refactoring decided to create their own variables, even though they were already residing in another variable. I’m not sure whether that’s a Rust problem, or how the refactoring is implemented, but I found it annoying up to the point where I avoided inline refactorings in that language alltogether.

Why do I find the automated refactorings more safe than the manual approach? IDEs with their automated refactorings go the extra mile to make sure what you are trying to refactor is safe. If that’s not the case, the IDE will you right out so. I know, I’m human, and I can make mistakes. Sure, automated unit tests help me to identify where I screw up. But a carpenter also knows his toolbelt well, and knows how to use the tools they carry with them. I think we should do the same as software crafters.

Leave a Reply

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