Euruko 2018

Let's refactor some Ruby code

Let's refactor some Ruby code

by Ana María Martínez Gómez

In her talk at EuRuKo 2018, Ana María Martínez Gómez focuses on the importance of refactoring Ruby code to improve clarity and maintainability. The purpose of her presentation is not to critique poorly written code but to share her personal experiences and lessons learned while enhancing legacy code from an old Rails application called Preheat, developed as part of the Open Build Service (OBS).

Key points discussed in the video include:

  • The Nature of Comments in Code: Ana emphasizes the drawbacks of ambiguous comments, illustrated by a 'scary comment' in the OBS code that discouraged use but lacked clarity. She highlights the importance of refactoring confusing expressions into more explicit internal method definitions instead.

  • Improving Code Reusability: She contrasts outdated practices of defining methods using improper hash structures with a more readable approach that directly employs symbols, which streamlines and clarifies the code base.

  • Simplifying Logical Conditions: Ana provides an example where she refactored complex error-checking logic using exclusive OR (XOR) conditions, resulting in significantly clearer code that maintains its intended functionality without unnecessary complexity.

  • Utilizing Unique Methods for Lists: She discusses how to enhance list operations, proposing a new 'union' method that efficiently eliminates duplicates while concatenating lists, leading to better performance and greater clarity in code representation.

  • Engagement and Collaboration: The process of proposing code changes also highlighted essential interpersonal skills. Ana shares her experience when discussing the new method with peers, emphasizing the value of patience and collaboration within the Ruby community while handling differing viewpoints.

The key takeaways from Ana's talk include:
- The significant role of clear documentation and well-structured code in enhancing readability.
- Importance of community engagement in Ruby development and collaboration in the open-source setting.
- Acknowledgment that refactoring is a journey that builds not just better code but also stronger relationships and understanding within the development community.

In conclusion, Ana encourages participants to contribute to Ruby's evolution actively, stressing that the collaborative efforts will lead to personal growth and improved code quality. The session wraps up with the reminder of the richness of the Ruby community and the benefits of shared experiences.

00:00:21.579 Welcome back! Lunch is officially over. I hope you all cast your votes for Rotom or another city. Of course, it doesn't really matter. I have a couple of announcements to make before we get started, so please listen carefully. There have been care packages taken from the men's room — perhaps you thought they would be better utilized in the ladies' room. They were placed there for a reason, so please leave them where they are. Also, some of the care package baskets were taken away entirely, so we kindly ask you to return them.
00:01:07.610 A couple of things for tonight: if you don't know where to have dinner yet, the hotel provides a 20% discount in their restaurant, and you get a discount at the hotel registration tonight.
00:01:21.440 Ramon, one of the organizers, has reserved 20 places at a karaoke place if that's something you're interested in. Just come to the front later or find Ramon to sign up; it's first come, first served. Danielle has also reserved 20 places at Back to Ha, which is a nice, hip place in the 5th district. I highly recommend it.
00:01:47.590 Aaron is not in the room, so I will make this announcement for him. He is doing a tour — more details will be mentioned later. Finally, please do not put glass bottles in the bins; there are crates next to the fridges for recycling purposes. Now, we will have two talks, and after those, there will be a brief break. I would love for everyone who has ever coached at a Rails Girls event or organized a Rails Girls event to come to the front so we can take a nice picture.
00:02:33.469 So that's everything for the announcements. Now, Anna will take the stage. She started with Rails in 2015, contributed to an open government project, fell in love with Rails, and has never looked back. She will take us on a refactoring journey.
00:03:04.369 Hi everybody, I'm Anna, and I'm going to speak about refactoring, improving Ruby. This year, Ruby is turning 25, and I think I've changed more than Ruby itself. A lot of great Ruby code has been written in these 25 years, but also Ruby code that was not that great. The purpose of my talk is not to criticize bad code or to show you the most impressive refactoring ever, but to share my experiences and what I learned while refactoring Ruby code.
00:03:21.439 Before going into concrete examples, allow me to introduce Preheat, which is what I’m working on and from which I took most of the examples. Preheat is a really old Rails application that started in 2005, before the first Ruby release, just to give you an idea of how old it is.
00:04:03.000 The Preheating project I'm working on is called Open Build Service, commonly abbreviated as OBS. From now on, I will speak a lot about OBS. OBS is a system that builds and distributes packages from sources. It is an open-source project, and the packages can be interacted with freely. The URL to the GitHub repository and the URL to the external instance are available on the slide, so feel free to check them out.
00:04:14.669 Now that everyone is up to date, let’s refactor some Ruby code. The first example I want to share is what I like to call 'the scary comment.' This is real code from an OBS class introduced in 2011: the Note class. As you are laughing, you may have already discovered the scary comment: 'stay away from this!' That was my reaction when I read the comment!
00:04:58.500 Now, seriously, what does 'stay away from this' mean? It's a comment that raises questions. I had no idea what it meant, but according to the documentation, it is used for public methods that should not be publicly consumed. It's not just a comment; it has a side effect because it hides the method from the documentation. Given that 'stay away from this' accompanies a no-doc comment, I wondered what it meant.
00:05:53.990 The specific method was used internally. It was marked for the equal method. I discovered through my digging that it wasn't private. Instead, it was used in an internal context, guided by the definition of 'protected.' The protected keyword allows methods to be called only within the defining class and its subclasses. So I made the comment an internal method to clarify that it was indeed an intentional inclusion.
00:06:41.629 From this experience, I learned that comments can be a bad idea. And of course, sometimes comments are necessary, but there are many instances where we could describe the code better. I also learned what a no-doc comment is, having seen it primarily in the Rails code for methods that are public but not intended for user reliance. And of course, I learned about the usefulness of 'protected.'
00:07:45.100 Now, let's talk about reusing code in OBS. Let’s say we have some code to be reused that uses two parameters, and we need it twice. The reasonable way to handle this would be to define a method and call it twice.
00:08:00.000 However, in OBS, what we had to do before followed an outdated pattern. We used an improperly defined hash for the parameters — with one parameter as a key and the second as the value. We iterated over this hash and placed the reusable code inside, but that gets messy pretty quickly.
00:08:19.650 The new approach is changing the strings in the keys to symbols and subsequently transforming it during the iteration, leading to more readable and maintainable code. A better option is to use symbols directly to minimize conversion processes and improve clarity, ultimately leading to a more efficient code snippet.
00:09:03.090 Next, let’s review logical conditions in code as another example of necessary refactoring. Here’s a concrete example from OBS where we add errors based on checking multiple conditions. The first condition adds an error if both one value and another are true, while the second specifies that if at least one of those values is true, we should add an error.
00:09:40.010 What we need is an exclusive OR condition, sometimes referred to as XOR, which will return true only if one of the conditions is true and false otherwise. Refactoring this section simplified the code to be much more concise and readable while maintaining its intended logic. Now, if we implement an error-check, it only raises if either of the stated conditions is true and helps ensure clarity in intent.
00:12:00.700 Now, I have another example: the concat plus unique logic in OBS. Here we work with the 'if list packets' situation. This is essential to allow for checking against nil values and ensuring things are handled properly in the code.
00:12:38.500 Before, these operations were more cumbersome, and we processed data using undesired methods. My goal was to simplify this into clear, maintainable code. As we want to remove duplicates while concatenating lists, I proposed a new 'union' method to streamline the process. It turns out that when we used a pipe operator in Ruby, we found it could simplify this method greatly.
00:13:24.240 This method improves efficiency over the existing solutions, especially if we utilize it for three or more elements. My suggestion to replace concatenate with a clear union method still stands as it provides better efficiency overall while keeping code simple and understandable.
00:15:25.740 Now, I went ahead and asked the Ruby core to explore this option. I presented the idea but faced some resistance initially. Christian, a colleague, had thoughts on why the original concat might remain preferred due to readability concerns. This led to some discussions, and I realized that it's essential to approach these scenarios constructively and collaborate effectively even when opinions differ.
00:16:43.970 Eventually, the nuances led to the idea that different environments have different expectations. I found that I needed to take patience into account when working on code with others. Not everything can happen at once, and persistence matters.
00:18:17.090 What have I learned throughout this process? It taught me the importance of engagement in the larger community, with real-world implications on Ruby development. The Ruby open-source community is thriving and contributes significantly to my understanding and ability to work within it. Learning from one another is invaluable, and the importance of patience also strikes home.
00:20:48.839 I encourage you all to get involved in Ruby, contributing upstream where possible. We grow together in those experiences, and while some processes may be challenging, the results and friendships that emerge simply reinforce the value of this community. Thank you!