RailsConf 2023

The End of Legacy Code

The End of Legacy Code

by Daniel Huss

In his talk, "The End of Legacy Code," Daniel Huss explores our often fraught relationship with legacy code, aiming to change how developers view and interact with it. He emphasizes the emotional responses of frustration and fear that often emerge when working with legacy systems, making them feel overwhelming and daunting. Huss believes that by adopting a mindset of 'Eternal Onboarding,' developers can refresh their approach to legacy code, integrating curiosity and collaboration into their workflows.

Key Points Discussed:

  • Definition and Perspective on Legacy Code:

    • Legacy code is often associated with feelings of dread, suggesting outdated or poorly maintained systems. However, Huss argues that it might be more about our emotional response to the code rather than the code itself.
  • Emotions Associated with Legacy Code:

    • Frustration arises from unclear compositions and convoluted dependencies, while fear develops from past experiences with breaking changes and complex requirements.
  • Impact of Tension on Team Dynamics:

    • The descent into negativity regarding legacy code can affect team morale, creating a cycle of blame and disengagement. Teams must lift each other up to counter these feelings.
  • The 'Eternal Onboarding' Mindset:

    • This concept encourages continuous learning and sharing within teams. By fostering curiosity about how legacy code came to be and promoting knowledge sharing, teams can alleviate the pressure that legacy code places on individuals.
  • Onboarding Documentation:

    • Effective documentation simplifies the onboarding process for new developers, making it easier to adapt to legacy systems without initial feelings of inadequacy.
  • Adopting Proactive Measures:

    • Investigating the origins and functionalities of legacy systems can empower teams to engage with the code rather than avoid it. Huss encourages teams to share experiences that ease frustrations, ultimately leading to better product development.

Significant Illustrations:

  • Huss uses the metaphor of a phonograph to discuss how legacy systems can still provide value when maintained correctly, contrasting a well-functioning tool with one that has fallen into disrepair.

Conclusions and Takeaways:

  • To combat the stigma of legacy code, developers should aim to approach it with curiosity rather than disdain. By fostering a culture of support and continuous learning, teams can transform how they interact with and improve existing code. The ultimate goal is to maintain compliant, functional systems that can evolve alongside changing technological landscapes without the weight of negative emotional associations. By forming strong team dynamics, developers can not only rebuild legacy systems but also redefine their overall approach to software development.
00:00:21.480 I'm really excited to be in Atlanta! How about everybody else? Yeah, I've never been before and the food's been great. I really enjoyed it! I was one of the people who put my hand up for my first Rails Conference in person. That's awesome! I'm really excited. Thank you all for coming out to talk about the end of legacy code.
00:00:38.340 I'm Daniel, and you can find me on GitHub or LinkedIn. I have the same name there, but my Mastodon is really tiny because I don’t go on there very often.
00:00:52.260 I'm really excited to talk about legacy code. I know that's something that maybe not all of us feel good about. We know it when we see it, and it usually feels kind of bad. So why do I like to talk about it so much? During the day, you'll find that I have the job title of Senior Software Consultant at Test Double. On my LinkedIn, you would probably see something along the lines of aspiring technomancer and refactoring joyeur, which are the things that I spend most of my time appreciating in this job.
00:01:12.119 What I want you to care about today is that I am on a mission to end legacy code. This may sound like a bold statement, but what if we could have everything we worked on feel fresh, new, and exciting, just like the day we started our Rails app for the first time?
00:01:24.140 It's probably a little naive to think about these giant applications that are full of convoluted complexity. It might feel really impossible to recapture that fresh, tabula rasa, blank slate experience. So, then what are we talking about when we mention legacy code? I think I probably need to qualify it with a definition. But before I do, take a look at this phonograph for a second. What does it make you think of? What does it look like?
00:01:49.320 I don't have any descriptor words. Does anyone want to shout out at me? Antique? Yes? Untested? There we go! Scratching? Yeah, that's elegant. I like that one! That’s what it made me think of too. I hear you, though. We see something old and we think 'scratchy' or perhaps 'untested' or 'unvalidated,' but I like the way that this looks — shiny, fresh, maintained.
00:02:19.379 When we talk about legacy code, often it's in terms of code that was written some time ago, implying that we have some sort of older version of a language or framework that we're working with. This suggests an outdated paradigm, and sometimes we mean that it's in a disused language. These are kind of the descriptors we assume we are discussing when all of us stand around and think about those icky feelings about the legacy code we're working with.
00:02:54.420 I think there's an interesting problem with legacy code. All of us in the Rails community have this really exciting opportunity because we’re working in a framework with such longevity, coming up on its 20th year. There's a lot of legacy code out there, and many things built into Rails have gone through some of those descriptors we've already used.
00:03:09.300 We’re lucky to have that longevity! There are really exciting and smart people working on this framework, building applications together that we get to benefit from. But why then does so much of the legacy code we see feel more like a familiar bed in a dusty old room, with a shadow underneath? What kind of monsters are lurking in there? That’s the stuff that makes us feel icky.
00:03:45.540 So hopefully, after we're done talking, we can see that maybe what's under the bed isn't necessarily something super scary, but actually a little more friendly – something we can make friends with. I think there's another type of legacy code that sneaks its way in there. We're talking about it not in terms of ages ago but with other pejorative language.
00:04:44.100 I think what we're really talking about here is code that we don't like. We don't set out to write code we don’t like, but we've all experienced that it comes out of our fingers and ends up being something that's not up to our standard. So what brings us to that point? I think there are two emotions that drive this experience: frustration and fear.
00:05:17.580 When we start stumbling into legacy code, it’s because we begin experiencing the frustration of unclear compositions or inconsistent patterns. Things that feel bad violate our understanding of conventions – convoluted dependencies and situations that, as we're exploring, feel like walking into walls every time we turn a corner.
00:05:39.000 Maybe it's untested code, perhaps flaky when there are tests. All of that stuff piles on and the worse we feel about the code we're digging into.
00:05:50.280 Fear sets in when that frustration crystallizes. We've learned from our mistakes; we've been burned too many times. It hurts to start looking at this code. We hesitate to make changes because we fear new requirements from users or directives from product teams that add unnecessary complexity. We’re afraid of breaking things in production when we try to roll them out, and that fear only escalates when serious implications such as compliance issues or potential harm to users arise.
00:06:30.120 The more we get burned, the more afraid we are. I think there's a natural tendency for us to flip through the pages too quickly, getting into a salty mindset about the things we’re working on. I’ve seen this happen with various teams I’ve been a part of. Initially, we start with high morale, feeling good about our work. Then we see something that violates our expectations.
00:06:58.500 The more we trip over these issues, we become aware of the lumps in the codebase; we start sliding deeper into feelings of frustration. We start expressing our disdain, which leads to discussions about how convoluted code is and how we don’t understand it. We may raise our morale temporarily by avoiding this unpleasantry, but quickly find ourselves tumbling down the hill of frustration.
00:07:55.740 We slide further into feelings of blame towards those who came before us. Our emotions culminate into disgust for the code, leading us to a point where we declare, 'I hate this. I hate working on this.' It’s a spiral that leads us right into the black hole of burnout, and once you've hit that point, you don’t want to go back.
00:08:31.740 This isn’t just a unique experience for us individually; the mindset we’re in impacts those around us. As teams, we tend to fall down this precipice together more quickly. I have a bit of tough news for you: if you’re pushing your team further along this decline, we’re not doing our jobs properly. We’re not conducting ourselves professionally.
00:09:21.360 The good news is that anybody can help lift those around them. You might be surprised at how little effort it takes to spark support among your teammates to overcome those fears or frustrations with code. Healthy teams can uplift each other, allowing us to stay at the front end of a challenging curve. I’m from Canada, so I added a Canadian analogy here: has anyone seen the Canadian geese flying in a triangle?
00:10:04.500 They do that to help draft the wind; the rested geese lead the way, breaking the wind for everyone else behind them. So, it’s normal for us to experience these emotions; we’re human beings. We can’t be robots trying to deliver our best work, but we won’t always be at 100% all the time. Hence, we need outlets and support as we head into those experiences.
00:10:42.300 At Test Double, we have a couple of Slack channels: one’s called 'Salt' and the other, 'Yelling in All Caps.' These are spaces where we can bring our frustrations and share a laugh, which helps clear the way for understanding and digging into the problems.
00:11:01.380 There's an ebb and flow in our teams — when you're strong and rested, you can take the front point of the triangle, but when you're fatigued or overwhelmed, you can draft behind. The bond created during challenges can motivate and empower everyone on the team, enhancing morale and performance. So, how can we be better developers, work well with our teams, and take the front point of that triangle more often?
00:11:49.560 I believe this mindset of 'eternal onboarding' can help. Some people hear 'eternal onboarding,' and it might sound terrifying. But I see it as a desert oasis. We're wandering through a desert; we can find a moment in the shade, drink some water, pick ourselves up, and continue on our journey. When we work with legacy code, we might think that if we went back in time with everything we know now, we could engineer a better solution, a perfect fit that would work flawlessly from the start. But that context can trap us.
00:12:46.139 When we're stuck with all the complex knowledge we’ve built up, it’s hard to recall what that fresh blank slate experience was like. We underestimate how much context we’re juggling mentally while we work. If we’re too burdened by context during our engineering tasks, we often cannot imagine what is genuinely possible because we can’t uncouple ourselves from fears about the code.
00:14:04.380 How we answer the question, 'What does this code do?' is deeply impacted by how much context we are bringing. If we consider the most veteran person on a team, what would they think the code does? How does it function? What about someone who joined a week ago, or someone who left a year ago? Everyone has a different understanding based on their experiences with the code.
00:14:41.940 When you add in QA, logging, and monitoring, they tell very different stories about what our applications are doing. Often, there's little overlap in those views. If you factor in product managers, sales, and end users, it complicates the picture. Has anyone ever been on a team that has 100% overlap in understanding what the application does? I’m not naive; I don’t think this is entirely possible. It's similar to aiming for 100% test coverage — it’s arguably not our goal.
00:15:15.840 However, it can serve as a litmus test reflecting our processes, team cohesion, and cultural practices as we tackle frustrating or fear-inducing code. If we’re struggling to maintain a cohesive understanding of the system and how everything fits together, we end up with a heavy load of legacy code that shoulders responsibilities it shouldn’t.
00:15:41.760 Let’s get back to our phonograph for a moment. The descriptions you shared, such as 'antique' and 'scratchy,' showcase sound qualities that contribute to how the music industry employs old tools to create unique sounds in their work. This is part of how they mix music, creating a nostalgic experience. However, they no longer rely on phonographs to record complete albums. Well-maintained tools can still deliver cool sounds and historical flair.
00:16:21.360 When legacy code falls apart, it must express everything an app does, encapsulating the countless hours spent solving edge cases while displaying the limitations of the business goals and objectives without proper analysis. The code has an unreasonable burden and revelations about its boundaries often emerge concerning limited resources available for improvements. The code also has to capture everything we understand about the end user's world.
00:16:55.560 Ultimately, the code should not market the value it brings — while there are individuals dedicated to determining the value of software, this isn’t the place for code. The most critical aspect is that code should onboard developers into understanding the work context, not serve merely as instructions for machines.
00:17:32.700 How do we return to a human experience in this process? I think a few qualities can help with this mindset, starting with leading through curiosity. When we engage based on judgment, it’s easy to feel frustrations and fears. However, by being curious and asking how things came to be, or what the design limitations are, we can open up exploration avenues.
00:18:16.740 We act like explorers, taking a lay of the land and noticing interesting things. Sometimes the designers of that code may no longer be part of the company. In that case, we become archaeologists, digging through commit histories or pull requests to reconstruct origins from a field of rubble.
00:19:05.640 Leading with curiosity allows this exploration to feel enjoyable. The next step is to pay it forward, improving the process and enabling future developers. We need to invest in this work and get returns on our investments.
00:19:37.560 Finally, we should leave markers behind, trail markers that guide others toward the safer oasis we found along the journey. We can assist others as they climb this mountain, ensuring their bodies and minds remain intact as they discover exciting sights nearby.
00:20:08.760 Let's apply this to the basics. Everyone has to turn on their applications — today is a beautiful Monday. So, all of us will likely sit down to do something like bundle install on a Rails app. Let’s close our eyes for a second and visualize how it feels to begin. Now, imagine being told your laptop died over the weekend, and you have to start from scratch. Hold your hand up if that feels uncomfortable.
00:20:56.840 Setting up a brand-new environment isn’t a delightful experience. Maybe there's a lot of services to configure or complicated secrets management that complicate your journey. Uncovering the reasons why these setups feel cumbersome can reveal how much of that mental burden stems from the information stuck in someone’s brain.
00:21:38.500 Let’s extract foundational notes about the setup process and share that knowledge with others. This process significantly helps avoid that salty mindset that arises when starting a new role. If someone’s first day at a new company involves tedious configurations, they'll start to feel doubt about their abilities.
00:22:22.860 Investing in onboarding documentation is foundational. Many of you might know that I work at Test Double, and I enjoy test-driven development. Testing can sometimes be part of our legacy code that feels bad to work with.
00:22:56.940 If you're unaware of testing frameworks, that’s okay. I would love to chat more about it. Don't overlook the importance of writing good tests and utilizing tools such as Factory Bot.
00:23:08.880 When we begin with only a user's first name and last name, we may easily evolve our application over time to enhance capabilities. But as we integrate subscriptions, we push the complexity higher. Eventually, it starts seeming convoluted. Changes lead to excessive configuration options, adding more to the test setup that isn't even related to the actual application code.
00:23:31.320 This transition complicates the clarity users experience, quickly introducing traps that distract from effective bug fixing. To avoid this, we need to collaborate across shared experiences, digging into our organization's knowledge pool and soliciting insights before tackling complex issues.
00:24:29.460 We should understand whether certain features are core to our business model, thus directing our investigative focus and potentially letting us either streamline or even overhaul those systems from scratch. Our time spent investigating will help us understand what’s genuinely valuable and worth pursuing.
00:25:03.680 If we determine that a sub-feature isn’t significant, we should document our exploration and insights to leave markers for future developers that make their journeys smoother. Different teams have different processes for circulating information, and we can tap into that teamwork to reveal shortcomings in our collective process.
00:25:48.300 We need to recognize that scenarios exist where legacy code might control our decisions, guiding us down product cycles and consuming undue resources and timelines. We risk chasing trivial matters when we indulge in relentless tinkering.
00:26:12.840 When this, paired with perfection paralysis sets in, we can get bogged down attempting to design perfect implementations, halting any forward motion. We discover we must embrace community culture and knowledge sharing. We can aid each other when we cross boundaries to understand the nuances of our work better and encourage support for one another.
00:27:01.080 Those who have never attempted pairing should try it out, especially when encountering unfamiliar portions of the system. Encouraging others to reach out and bond across external teams readies our work environment for effective, consistent collaboration. Revisit the tools and communication you have available and challenge yourself to document contributions.
00:27:53.940 The rituals we engaged in on our projects can support us, especially retrospectives where everyone gets involved. Now is the time to remember how to properly support those social bonds, rise above frustrations together as a team, and bravely explore parts of unfamiliar code.
00:28:25.240 Our goal is to enable change that leads the team toward success, helping everyone as a larger community. It is within your power to ensure that legacy code remains a fruitful area reserved for potential breakthroughs instead of a lingering weight.
00:28:55.000 Just as you’d never want to see neglected systems gathering dust, we must maintain our Ruby applications as polished, functional phonographs of value. If you're struggling on this journey, reach out! Don't hesitate to connect with me on LinkedIn or Slack; I am excited to share resources.
00:29:29.880 I'll share my slides here, which influence my talks and this conference. Before I wrap up, I want to emphasize that not every issue revolves around technology; if you’re inclined to have crucial human-centric conversations, remember the team at Test Double is interested in that avenue as well.
00:30:03.000 Let’s continue building applications and maintaining our Rails systems in better ways, ensuring we rid ourselves of burdensome legacy code while enjoying the journey together. Thank you so much!
00:30:16.140 I think I have like a minute for questions if anyone has comments.
00:30:29.300 One of you asked what motivated me to give this talk. I don’t have a computer science degree; I come from a communications background. My journey started with boot camp, and I was enamored with the latest trendy JavaScript frameworks during that time.
00:30:48.280 After that, my first job revolved around helping maintain an arts organization system, a 14-year-old Rails app with a two-year-old React frontend that felt overwhelming. I lacked preparation, but these learnings helped me and those around me.
00:31:09.840 Working through frustrations with others not only improved my ability but fostered camaraderie in facing those challenges. With little effort, it became easier to think differently about the obstacles we faced and foster collaboration.
00:31:51.740 To better navigate code history, I rely on IDE tools to extract essential histories, often recommending the use of Git and examining changes that provide insight into legacy code.
00:32:19.680 In determining whether legacy code deserves your attention, evaluate whether it impacts significant parts of the app. If that is the case, it may be worth a deeper team discussion or a retrospective.
00:32:38.480 Thank you for the engaging dialogue! I look forward to further discussions later!