Refactoring

Summarized using AI

Zen and the Art of the Controller

Michael Kelly • May 25, 2016 • Kansas City, MO

Zen and the Art of the Controller

In this presentation, Michael Kelly, a senior Rails developer, delves into the complexities surrounding Rails controllers, particularly focusing on how to manage and refactor them effectively in professional environments. Despite controllers appearing straightforward, they often become bloated and unwieldy in real-world applications due to rapidly changing requirements and feature creep.

Key Points Discussed:

  • Understanding Controller Complexity: Kelly emphasizes that junior developers often encounter a simplified view of controllers? focusing mostly on standard CRUD operations like index and show. However, real-world professional controllers can be complex, leading to difficulty in maintenance and development.
  • Identifying Bloat Causes: He points out that controller bloat typically follows actions such as adding AJAX functionality, which can contribute significantly to a rising action count. For instance, imagining a scenario with an Ads Controller that starts with seven actions but balloons to fourteen as more features and requirements continually add complexity.
  • Implementing Solutions: To combat excessive complexity, Kelly recommends breaking down controllers into smaller, more focused components. For example, creating separate controllers for distinct functionalities (like an Ads Preview Controller) can simplify the code and assist with easier debugging.
  • Best Practices: Kelly encourages developers to keep to a RESTful design, clearly defining resources and maintaining healthy boundaries regarding controller responsibilities. Each action should be defined distinctly to avoid clutter and confusion.
  • Long-Term Maintainability: He highlights the importance of making strategic decisions during development, advocating for creating smaller actions and refactoring code sooner rather than later to reduce long-term difficulties.

Conclusions and Takeaways:

  • Maintain focus within controllers by ensuring each action corresponds to a specific concept.
  • Strive for modular controllers to improve clarity and navigability, allowing new developers to onboard easily.
  • Understand that while it may seem easier to add functions to existing controllers, creating new ones fosters long-term maintainability.
  • Commit to a rigorous evaluation of project structure as applications evolve, regularly assessing the placement of controllers to adapt to burgeoning requirements.

In conclusion, Michael Kelly's talk illustrates the need for thoughtful structuring of Rails controllers to balance ease of use and long-term maintenance, encouraging developers to prioritize clarity and responsibility in their coding practices.

Zen and the Art of the Controller
Michael Kelly • May 25, 2016 • Kansas City, MO

Zen and the Art of the Controller by Michael Kelly

So you’re fresh out of boot camp or just off a month long binge on RoR tutorials/examples and you’re feeling pretty good about MVC and how controllers fit into the whole framework. But projects in the wild are often far more complicated than you’ve been exposed to. In this talk, we’re going to discuss several techniques used by seasoned engineers to build and refactor controllers for features you’ll actually be working on.

Help us caption & translate this video!

http://amara.org/v/JUpJ/

RailsConf 2016

00:00:10.700 Okay, so let’s go ahead and get this started. My name is Michael Kelly, and I’m a senior Rails developer. I have been working with Rails for about five years, though I’ve been a developer on and off for around fifteen years total. As you can see from the slide in your program, we’re going to talk about Rails controllers today.
00:00:34.380 Before we get started, I know this is the junior track. Let me see a show of hands: who here is a junior developer, someone newer to the technologies? Okay, good. How many people here are senior developers who are here to judge me on my presentation? Oh, good! So, I only have to impress a few of you. Alright, good deal.
00:01:03.450 So, like I said, we’re talking about controllers. You’ve all seen them, and you’ve all dealt with them in some capacity. A lot of times, what you see, especially for newer developers coming out of boot camp or those who have run through several tutorials, is a simplified view of controllers. We often see index actions or show actions, the standard CRUD operations. However, what I want to address is what actually happens in real-world controllers once you enter the professional environment.
00:01:52.430 The first slide I have is an example of one such controller. Now, you’re not supposed to read that; we’re not going to go through it line by line because it’s atrocious. That’s over 350 lines of a single controller with more than 18 different actions and a hundred lines of just boilerplate code. If anyone is wondering, that’s a problem. What I want to discuss is how we can eliminate this complexity from what should essentially be a standard controller. It can actually be done a lot easier than you think.
00:02:41.560 What we typically have in a standard controller are actions like index, show, new, create, and the usual CRUD actions. However, often, you’ll hear people blame controller bloat on factors such as rapidly changing requirements, uncontrolled feature growth, or changes in your team. In the worst cases, issues from elsewhere in the application force you to add unnecessary complexity to your controller. This is incorrect. All of these factors are controllable. They exist in every business and every software product. There is a way to maintain a good controller.
00:03:26.240 So, what exactly causes this issue? How do we end up with three hundred lines of complexity? Well, the main reason is a misunderstanding of what your actions are actually doing and what resources they are working on. Let me walk you through a small example that is a bit closer to reality. I’ll gloss over a few things for time’s sake.
00:04:03.890 Let’s say you are working at a company that manages ads for its clients on platforms like Facebook and Twitter. A fairly standard requirement is a way to browse, edit, and manage these ads. You need to create and edit them, as well as control their activation status—basic on/off features. Lastly, users need to be able to see the performance metrics of their ads, such as views, clicks, and impressions.
00:04:31.700 In this example, I’m not going to build a whole app; I will focus on just one controller, which is the Ads Controller. The first thing we need to do is had a way to handle the standard CRUD operations. I actually prefer the acronym BREAD because it includes the index action, which stands for Browse, Read, Edit, Add, and Delete. It’s just a naming preference. You’ll see this concept implemented within the context of the Ads Controller.
00:05:33.060 It’s quite straightforward. I won’t even include features like paging or search—just basic actions. After we implement this and push it to production, everything seems fine at first. However, we now have a total of seven actions in this controller. Each controller typically has these actions, so already we’re discussing a setup that contains seven separate contexts that need to be managed in your mind. Plus, since we need to control the ads, we might want to add additional functionality.
00:06:19.240 In many cases, eager developers might think it’s reasonable to simply add AJAX actions to the Ads Controller because they relate to what we’re already working with. However, this can lead to significant issues. So let’s evaluate that idea. Our action count may balloon from seven to ten quite easily. Even though we might introduce more features that trigger background jobs to talk to platforms like Facebook or Twitter, it’s easy to see how the controller can become unwieldy.
00:07:04.390 Imagine one of your executives walks in with a request for a more responsive UI. They want the page to load independently and asynchronously without additional waits. Now you find yourself adding functionality that requires rendering multiple partials. This action count goes back up to twelve without much realization. Each of these actions is typically implemented minimally, often just a single line of code. However, any logic added to these actions complicates the situation significantly, leading to a messy controller.
00:07:55.820 Now let’s also consider the need to track ads' performance statistics. When we return to our Ads Controller, we might want to add more actions that deal with audience engagement—the users who have interacted with our ads. So again, we add actions that aggregate data like clicks and impressions, bringing our action count up to fourteen. This is very close indeed to that bloated example of a single controller that had many actions.
00:08:49.490 So, how do we stop this trend of increasing complexity? The answer is to break controllers into smaller, more manageable pieces. Many junior developers might struggle with this concept because each action feels relevant to the ads controller; however, you are not always dealing explicitly with ads. To illustrate this, consider categorizing your controllers based on the kind of information they handle.
00:10:34.490 For instance, you might have controllers that handle static or view-layer data, those that deal with composite concepts that are not directly represented by an Active Record model, or aggregate actions that combine data from multiple sources. In taking our previous example, we would separate out those actions into different controllers, which might include an Ads Preview Controller for rendering ad previews separately, allowing for easier management and a clearer sense of purpose for each controller.
00:11:36.540 This strategy simplifies debugging and navigation since if there is an issue with previews, for instance, you can address it quickly without needing to sift through the larger, more complex Ads Controller with its numerous actions. Furthermore, when new developers onboard a project, navigating through fewer lines of code is a lot more manageable than having to search through a large file just to find the bits they need.
00:12:36.120 Additionally, localizing where you make changes is essential—if changes are necessary for a specific feature, they must only occur within their related controller. This reduces the risk of introducing bugs elsewhere in unrelated sections. As each team member works on different pieces, having well-structured controllers allows for better coordination, as you won’t face merge conflicts as often.
00:13:25.080 To wrap up the main takeaways: keep your controllers focused on what they are actually doing. Each action should handle a specific concept rather than lumping multiple functionalities together. I encourage you to create smaller, modular controllers rather than overarching ones that tend to accumulate complexity. You’re not only improving your own development process but also making it easier for others who might need to work on the same codebase later.
00:14:53.940 You might encounter scenarios where it seems easier to add actions to existing controllers rather than creating new ones. This is often a balancing act, especially when approaching deadlines or critical production fixes. It’s advisable initially to add smaller actions when staff resources are limited, as long as it doesn’t compromise long-term maintainability. As your application evolves, ensure you revisit these decisions to keep things manageable.
00:16:34.780 Following a RESTful design is crucial. In creating a resource, you must clearly define what that resource is. Best practices dictate you should break apart actions as they present themselves, preventing the kind of sprawling, complex controllers we discussed earlier. If you feel overwhelmed by the sheer volume of code, take steps to refactor earlier than later—with proper planning, the burden of additional work can certainly weigh much lighter down the road.
00:18:00.160 Thank you all for your attention! I overestimated the amount of time I would take, and I appreciate you bearing with me. If anyone has any questions, I would be more than happy to expound further on any of these points or clarify any confusion.
00:19:53.170 In summary, it’s important to maintain healthy boundaries between your controllers and the responsibilities associated with them. Each administrative aspect and function must meet a clear separate definition. How you implement this is ultimately a reflection of your preferences as a developer, but don’t lose sight of the ongoing need for clarity and simplification in your codes.
Explore all talks recorded at RailsConf 2016
+106