Technical Debt

Summarized using AI

Digging Up Code Graves in Ruby

Noah Matisoff • November 18, 2019 • Earth

In the presentation titled 'Digging Up Code Graves in Ruby' at RubyConf 2019, Noah Matisoff discusses the common issue of dead code in growing codebases, particularly in consumer-facing products. He examines how dead code can accumulate as technical debt, especially in environments utilizing feature flags for A/B testing. The talk specifically highlights the importance of code hygiene—an overall approach to maintaining code quality and longevity.

Key points covered in the talk include:
- Definitions of Dead Code: Matisoff differentiates between unreachable code—code that cannot be executed due to no logical flow leading to it—and executed code that does not affect the program’s output.
- Impact of Dead Code: He emphasizes how dead code can slow applications, make navigation difficult for new engineers, and increase the complexity of updates and tests.

- Origin of Dead Code: The introduction of feature toggles allows for rapid development but can lead to significant accumulations of dead code if not carefully managed.
- Code Hygiene Practice: Matisoff mentions keeping dependencies updated, refactoring regularly, and maintaining accurate documentation as essential practices for good code hygiene.
- Managing Dead Code: Strategies for identifying and managing dead code include using the Ruby standard library's Coverage tool for tracking code usage and ensuring that the issue of dead code is addressed alongside feature releases.

In conclusion, while dead code may not directly break applications, over time it can lead to inefficiencies and increased costs. Matisoff advocates for proactive management of dead code as an integral facet of maintaining a healthy, efficient, and navigable codebase, stating that addressing dead code is vital to keeping code healthy. The talk engages the audience with various strategies and tools, encouraging them to reflect on their practices regarding code hygiene.

Digging Up Code Graves in Ruby
Noah Matisoff • November 18, 2019 • Earth

RubyConf2019 - Digging Up Code Graves in Ruby by Noah Matisoff

As codebases grow, having dead code is a common issue that teams need to tackle. Especially for consumer-facing products that frequently run A/B tests using feature flags, dead code paths can be a significant source of technical debt sneakily piling up. Luckily, the Ruby standard library exposes Coverage -- which is a simple, experimental code coverage measurement tool.

Let's dive into how Coverage and other tools can be used and expanded to track down dead code paths in Ruby.

#rubyconf2019 #confreaks

RubyConf 2019

00:00:12.150 Thanks everyone for coming.
00:00:15.000 Cool, so I'm going to go ahead and get started. This talk is called 'Digging Up Code Graves in Ruby.' I'll talk a bit about managing dead code and the complexity of it.
00:00:20.050 I'll dive into some examples and discuss some tools for managing and tracking down dead code in various Ruby projects and Rails codebases.
00:00:35.710 Before I get into it, a quick introduction. I'm Noah, and I work on the engineering team at Credit Karma.
00:00:41.079 I love Ruby and have been working with it for a really long time. Most recently, I've been doing Scala, so I would be happy to talk about the differences between the two after the talk as well.
00:01:00.850 Now, let's discuss technical debt. This is a term many of you are probably familiar with and have nearly discussed with your teams on a regular basis.
00:01:10.260 There's also a concept called code hygiene, which some of you might have heard of. Code hygiene is an umbrella term for anything related to the health and longevity of a codebase.
00:01:28.899 This could encompass various factors. Here's a visualization for understanding technical debt and code hygiene. Technical debt can consist of many elements apart from code hygiene, but I will mostly be discussing code hygiene today.
00:01:44.560 A few examples of code hygiene include keeping dependencies up-to-date in your project. For instance, this could involve updating the Ruby version or the Postgres version you're using, as well as any gems or dependencies in your codebase.
00:01:58.719 Refactoring code is another common aspect of code hygiene. In fast-paced environments, many of us working with Rails applications find that refactoring is important for maintaining good code hygiene.
00:02:07.180 Ensuring that documentation is accurate and complete is also considered code hygiene, even though documentation itself is not actual code.
00:02:12.400 This includes keeping your REST API documentation or any internal project documentation updated.
00:02:24.950 Managing dead code is also a crucial aspect of code hygiene, which is what this talk is primarily about. There are many more examples that fit under the umbrella of code hygiene.
00:02:41.870 So, what about dead code? Dead code is another term we often hear. We likely have a general understanding of what it means, but I will discuss two distinct definitions of dead code today.
00:03:05.840 According to Wikipedia, in computer programming, unreachable code is part of the source code that can never be executed because no control flow path leads to it. Sometimes, unreachable code is referred to as dead code. However, dead code can also refer to executed code that has no effect on the output of the program.
00:03:40.610 This distinction is important. An example of dead code is an addition function that takes two arguments to perform an addition operation but includes a second line that performs an unnecessary action, which won't impact the result. Although this aspect is harmless, it can lead to inefficiencies in larger applications.
00:04:04.670 In a more complex system, if we have a piece of dead code causing an expensive operation or network call, it can cause the user to wait unnecessarily. The second aspect of dead code is unreachable code, which can stem from logical errors in conditional statements.
00:04:44.090 Another example could be code left intact for preservation purposes that is no longer needed. Tools like Git are excellent for explaining changes, and data operationally never triggering another condition may lead to redundancy.
00:05:13.240 Feature toggles, often used for canary testing, A/B testing, or operational testing, can also introduce dead code into your codebase. They provide a way to iterate quickly on a product while obtaining quantitative or qualitative metrics.
00:05:41.260 However, the downside of adopting feature toggles is that they can push costs onto development teams. The toggles can be implemented as simply as a table in your relational database along with a wrapper around querying various toggles' states.
00:06:09.200 Many companies also invest in internal tooling or use third-party services that provide great UIs for toggling features. The most common strategies I've seen involve using these toggles at the edge, perhaps in front-end applications, to toggle button color or copy.
00:06:45.980 On the other hand, using toggles across the stack to modify internal behaviors may lead to the risk of dead code.
00:07:06.090 There are both pros and cons to using feature toggles. A significant advantage is that you can essentially say goodbye to feature branches; you can merge behind feature toggles or flags. This can lead to deploying developmental work to production while iterating on it live.
00:07:44.180 One further benefit is that it enables agile methodologies, allowing the release of experimental features to a smaller user base without extensive upfront development. It also offers stakeholders control over product functionality through various panels managing different feature state flags.
00:08:08.290 Conversely, feature toggles represent a potential source of dead code and can introduce varying states across different environments if not managed properly.
00:08:55.780 Maintaining backward compatibility becomes necessary with any code changes to support different versions. When turning a flag on or off, fragments of code may become inconsistently managed across environments.
00:09:48.610 What if nothing is done? Ignoring dead code may not break the application, but there are costs associated with inaction. Over time, it can become increasingly challenging to work within a cluttered codebase.
00:10:35.080 In terms of costs, letting dead code persist can lead to wasted application cycles, making it slow for users unnecessarily. This can occur in database queries or APIs that aren't needed anymore.
00:11:18.600 For new engineers onboarding onto a project, a cluttered codebase will prove arduous to navigate. As well, changing portions of unusable code can become burdensome during updates or tests.
00:12:37.790 There's a fantastic quote by Martin Fowler. He compares feature toggles to inventory which comes with carrying costs, thus teams should work to keep that inventory low.
00:13:26.140 Finally, I want to summarize strategies for managing dead code effectively. The Ruby standard library provides a unique and experimental library called Coverage, which is an efficient tool for tracking code usage within larger codebases.
00:14:00.070 Some experimentation platforms offer the capacity to log or warn when code is unused for extended periods, indicating it is likely to be considered dead code.
00:14:50.220 Lastly, addressing existing dead code as part of any new feature release is vital to keeping your code healthy. Thank you for your attention. I'll be taking questions afterward off stage.
Explore all talks recorded at RubyConf 2019
+84