Software Development

Keynote: The Case Of The Vanished Variable - A Ruby Mystery Story

Keynote: The Case Of The Vanished Variable - A Ruby Mystery Story

by Nadia Odunayo

In the keynote titled "The Case Of The Vanished Variable - A Ruby Mystery Story", Nadia Odunayo, CEO of The StoryGraph, dives into a fictional mystery intertwined with Ruby programming challenges. The narrative follows Deirdre Buck, a private investigator for Ruby-related mysteries, as she is pulled into a high-stakes scenario involving the Ruby Institute of Professionals (RIP) and a coding error that jeopardizes a significant competition. Deirdre receives a call from a Ruby developer, Jenny, who explains that her team's automated grant assignment application malfunctioned, resulting in an incorrect number of grants awarded. With only hours until the competition's decisive moment, Deirdre reluctantly agrees to help. Throughout the case, key points include:

  • The Setup: Deirdre's background as a solo developer and investigator of Ruby crimes showcases her dual roles in the tech community.
  • The Conflict: Jenny's team is on the brink of disqualification due to a coding error that awarded 300 grants instead of the maximum capped at 100.
  • Investigation Process: Deirdre unravels the issue by working alongside the lead developer, Alex. They explore the code which reveals critical mismanagement of class instance variables that caused the bug.
  • Resolution Discovery: Deirdre's strategic thinking leads to a simplified conclusion that the persistent class instance variable was incorrectly overwritten due to careless code editing, ultimately marking the origin of the bug.
  • Team Dynamics: The story also highlights interpersonal conflicts as Deirdre navigates Alex's reluctance to accept help, reminiscent of common challenges in tech teams.

Through this engaging narrative, the audience gains insights into debugging practices, teamwork in coding projects, and the importance of clear communication in development environments. Nadia's story emphasizes that coding obstacles are often less about complex issues and more about understanding code interactions. The main takeaway is that with collaboration, clarity, and communication, even the trickiest problems can be solved.

00:00:00.000 Ready for takeoff.
00:00:17.600 Hello! It's such an honor to be keynoting RubyConf. Thank you to the organizers for having me.
00:00:23.880 I’m Nadia Odunayo, and I'm the founder and CEO of a company called The StoryGraph, a reading tracker and book recommendations app. We help you choose your next book based on your mood and favorite topics and themes.
00:00:31.380 We've got a whole range of stats where you can learn about your reading habits and see how they develop over time.
00:00:38.579 We have machine learning-powered personal recommendations. It's like a trusted go-to friend who knows your preferences but whose brain is also a book encyclopedia.
00:00:45.719 You can find your next perfect read by filtering lists of books by mood, pace, fiction versus non-fiction, genre, book size, and more.
00:00:52.620 You can also read alongside friends, adding live reactions to specific parts of the book. Comments remain locked until your friends reach that specific part in their reading.
00:01:02.280 And there's a whole lot more on offer.
00:01:11.100 We have three million unique monthly visitors, 36 million page views per month, and each month we serve 110 million requests.
00:01:16.560 So, people often ask me, 'What is StoryGraph built in?' and I love that I can say, 'Ruby on Rails.' Thanks!
00:01:23.460 With all of this website and app activity, we get a lot of customer feedback, feature requests, and bug reports.
00:01:30.420 Given that I run the company's Instagram and Twitter accounts, I often receive messages that include something like, 'If you could pass this on to your dev team, that would be great.'
00:01:43.320 Now, while I have an absolutely awesome co-founder in Rob Freelo, the web and app dev team is made up of just me.
00:01:50.460 In fact, I recently gave a talk about this at the inaugural Rails SAS Conference run by Andrew Culver in LA last October. My talk was titled 'Getting to 1 Million Users as a One-Woman Dev Team.'
00:02:05.300 I spoke about the ups and downs in building StoryGraph, and there were some really stressful moments in this journey.
00:02:10.260 There were some very dark times when I felt stuck and questioned whether I could keep going.
00:02:20.520 As the talk blurb says, it was a personal, intimate story. But I saved the real intimate details for you, the RubyConf audience.
00:02:30.420 I have a secret. Don't tell anybody! You see, despite all of the focused hard work and stress that went into building StoryGraph into what it is today, I couldn’t help but keep up a little side gig.
00:02:41.460 I'm not just the founder, CEO, and solo developer of StoryGraph; I'm also a private investigator.
00:02:48.239 And what is it I investigate, you ask? Ruby crimes! Not jewel theft. I investigate crimes and mysteries relating to our favorite programming language.
00:02:56.640 Why did I go into private investigating? Well, there have been rumors circulating of a shady mastermind causing havoc and confusion amongst Ruby developers worldwide with his constant meddling in the source code.
00:03:09.980 I wanted to be the one to put a stop to him, and I'm sure all of us here know just how lucrative the world of Ruby private investigating is.
00:03:18.480 However, I decided that I couldn't do this gig under my normal name. No, I needed to protect my identity.
00:03:27.660 So, I decided to call my Ruby private investigating alter ego Deidre Buck. D for short. Why? Well, isn’t it rather obvious?
00:03:34.080 Now, I've got several stories from my years of doing this work, but for today's keynote, I thought I should tell you all about a case that happened within the very walls of one of our community's most revered institutions.
00:03:41.940 This is the case of the vanished variable.
00:03:53.519 Chapter One: It was June of last year. I'd had an incredibly stressful couple of days at StoryGraph. Rob and I had just finished dealing with our first experience of serious online controversy around our product.
00:04:04.680 Our content warnings feature had come under fire after a series of well-known authors brought to light that inaccurate warnings were being attached to their books. Things were going wild on Twitter.
00:04:14.700 Rob and I stayed calm and mollified the Twitter situation before coming up with a proposal for adjusting our system. We sought feedback from the community.
00:04:22.920 After our ideas were received positively, I coded all day and night to implement the changes. It was a wild and stressful time, but we pulled through.
00:04:30.679 Being the sole software developer, though, had really wiped me out. Despite knowing that wasn't the case, I felt personally attacked through all of this.
00:04:39.420 I needed a break from StoryGraph, and I was in the process of planning a relaxed evening when the phone rang.
00:04:46.020 Immediately upon answering, I heard a breathless, anxious voice say, 'I know I’m the last person you want to hear from, but I need your help!'
00:04:55.320 Oh, I recognized that voice—how could I forget it? Jenny!
00:05:00.720 Some of you may be wondering, who is Jenny? She was 29, a seasoned Ruby developer obsessed with all things Rails. She also betrayed her best friend once during one of my earlier cases.
00:05:06.779 I'd heard through the grapevine that she'd learned the error of her ways and was on a good path now, but I was hesitant to trust her.
00:05:16.560 Jenny worked for the famous, world-renowned Ruby Institute of Professionals, an organization that existed to keep the Ruby language and community alive.
00:05:23.820 It was a job-related issue that had Jenny calling me that day.
00:05:32.040 You see, the RIP had started up this new internal incubator—a competition of sorts—within the organization aimed at improving Ruby's image and usage worldwide.
00:05:40.679 People within the organization could form teams and submit various startup ideas. The winning team would secure a $500,000 investment and the opportunity to work on their startup full-time, fully supported by the RIP.
00:05:51.540 For the very first round of this competition, Jenny's team had made it into the final two.
00:06:02.280 They were building a platform that assigns grants from a pool of money that the RIP had available for this kind of thing to fund Ruby developers and organizations.
00:06:09.780 There were three different types of awards on offer that varied in grant size and could go towards different types of projects.
00:06:17.940 For our demo, we simulated an automated draw for the grants, including doing multiple redraws until the maximum number of grants had been assigned.
00:06:24.339 However, the number of awarded grants came out incorrectly in the semi-finals today, which was embarrassing.
00:06:31.560 We thought we'd be disqualified, but the judges put us through. It's so close between the final two teams that they will be looking for any reason to disqualify one of us.
00:06:39.420 But here's the problem,
00:06:49.560 Jenny sighed heavily. 'Our lead dev, Alex, is now very stressed, and he won't admit that he needs a second pair of eyes. With only two devs on the team—and not only do I have other jobs to get on with—I really hate working with him.'
00:06:56.100 The final is tomorrow, and there's only a few hours left to get to the bottom of this. 'We need you,' she finished.
00:07:04.260 I was quiet while I thought about it. Not only was I wary of helping Jenny due to her dishonest past, but after my incredibly stressful couple of days at StoryGraph, I was meant to be relaxing.
00:07:13.680 But, well, Debug can never resist a challenge, and it's never a good idea to pass up an opportunity to go into the RIP.
00:07:19.920 After all, I was sure they’d pay me handsomely for this work. I agreed to take on the case.
00:07:34.140 Chapter Two: 'I don't need help,' Alex said bluntly after listening to Jenny's explanation of why I was there.
00:07:41.520 He sat in his office chair, arms crossed—a mix between sulking and glaring.
00:07:48.919 Jenny started to protest, but I had an idea. I had dealt with this type several times before.
00:07:54.300 You know the self-proclaimed 10x hero developer who knew everything and declared that they could solve all of the company's problems in an afternoon if only people would leave them alone?
00:08:01.560 I knew what would loosen him up a little. 'I'll tell you what, Alex,' I said. 'I won’t distract you, I won’t say much, I won't touch the keyboard. I'll just sit next to you and be your rubber duck.'
00:08:09.420 'You can just talk me through the code and the bug, bounce ideas off of me, and of course, you’ll find the solution by yourself and get all the credit.'
00:08:16.300 'But this way, you'll find the bug quicker.'
00:08:23.100 We were all silent while Alex thought about it, frowning. Finally, he said, 'Fine.'
00:08:30.900 Alex had already turned back to his keyboard as Jenny gave me a subtle triumphant fist pump and said she’d leave us to it.
00:08:36.300 As I went to take my seat next to Alex, he briskly put an arm out to stop me. 'I've just realized I can't show you the code base as is.'
00:08:44.220 He said, 'There's confidential code in here from some of RIP's clients and partners. Let me get rid of it.'
00:08:50.760 I was wary that a couple of hours had already passed since Jenny’s call, but I couldn’t really argue with the issue of me seeing potentially sensitive client information.
00:08:59.520 So I pulled up a chair a couple of seats down from Alex and decided to make the best use of the time by opening up Duolingo.
00:09:08.640 After all, there was no way I was going to take down the shady mastermind if I didn’t stay on top of my Japanese, right?
00:09:15.240 Forty minutes later, and Alex was finally ready to show me the code. 'Now, I'm only showing you a simplified version of what I saw on that day.'
00:09:21.990 First, each award type had its own file that looked like this: Here, Alex had defined a class named Award A that inherited from another class called Grant.
00:09:27.240 Then, there was a class instance variable called award count which was set to zero. There was also a class method called award count, which returned the value of the class instance variable.
00:09:34.680 There was a class method called assign, which incremented the award count class instance variable by one.
00:09:40.260 Alex quickly showed me that the files for the other two award types were exactly the same except for a change in class name.
00:09:50.040 Then, there was the Grant class itself. There was a constant called Max grants which was set to 100.
00:09:56.640 The Grant class had its own award count class instance variable, which didn’t seem to be used anywhere.
00:10:02.760 The Grant class also had its own award count class method which added up the award count of all three award types.
00:10:09.240 Then, there was an awards remaining class method that was used in the redraw to figure out how many awards still needed to be assigned.
00:10:15.300 It took the maximum number of grants permitted and subtracted the number of awards that had already been given out.
00:10:22.740 Then there were scripts that assigned some awards—50 lots of Award A, 30 lots of Award B, and 20 lots of Award C.
00:10:30.060 Finally, there was a script that checked how many prizes were left and then performed a redraw if necessary.
00:10:37.920 So remember, in the code that Alex showed me, there were a maximum of 100 grants that could be awarded.
00:10:43.500 Fifty plus thirty plus twenty equals one hundred, which equals the maximum number of grants that were available.
00:10:50.520 'Are you following so far, or do you need me to go over it again?' Alex asked.
00:10:56.820 'No, I got it,' I told him. If anything, I've been holding back from suggesting places where his code could have used some improvements.
00:11:02.160 I mean, that wasn’t my style. Usually, I just focus on the problem at hand and avoid making quick judgments about the quality of unfamiliar codebases.
00:11:12.600 But Alex's behavior was frustrating. Anyway, on the whole, his code looked straightforward. I couldn't see what could go wrong.
00:11:22.020 Alex ran the scripts.
00:11:28.140 'What? Three hundred grants awarded?! Maximum number of grants exceeded?!' What a mystery.
00:11:31.980 Chapter Three: I spent a few moments feeling baffled before remembering that I was the great Debug, a private investigator.
00:11:39.960 I had to stay calm and focused, and I had to play my cards with Alex carefully.
00:11:46.140 He seemed to be opening up to me, so my strategy was to let him keep driving and to slowly nudge him into what would hopefully turn out to be the right direction.
00:11:53.640 Something had stuck out to me during the code demo: the class instance variables. Now, I thought class instance variables belonged to one specific class only.
00:12:06.120 But the four classes all had one with the same name. Given that the three award classes all inherited from Grant, and we had three times as many awards being assigned, I wondered if some overwriting was occurring.
00:12:11.760 'What if,' I said cautiously to Alex, 'each class were to have a differently named class instance variable? Perhaps they’re all conflicting with one another.'
00:12:18.420 Alex glared at his display as if he hadn’t heard me, but then he shrugged and said, 'Sure, we can try that.'
00:12:24.300 So he took each award class and adapted the name of their class instance variable to include some reference to its type.
00:12:31.320 He then ran the program again.
00:12:36.420 'Same result,' I noted. So, it wasn't the fact that the class instance variables with the same name in one inheritance chain were interfering with one another.
00:12:41.160 They did, in fact, as I had understood, stick to their own classes. I was wondering what to suggest next when we were interrupted by one of Alex's colleagues.
00:12:49.920 'Alex!' he said. 'You'll never guess who's in the office!'
00:12:56.880 We both turned to look at him. It was Max.
00:13:03.360 Everybody loved him, but I knew different. I knew he was not to be trusted.
00:13:10.680 But in that moment, his appearance worked in my favor, because the way Alex jumped up and sprinted away from the computer towards wherever Max was, you'd think he'd heard that somebody had dropped a billion dollars on the floor.
00:13:17.220 I didn't care, because finally I could get my hands on the keyboard.
00:13:24.360 Alex's fancy clacky keyboard with no labels on the keycaps didn’t faze me. Oh no, now I could finally do what I do best: solve this mystery.
00:13:30.600 While I was all over what to try, I immediately, out of habit due to my excellent git hygiene, typed 'git status' into the terminal.
00:13:36.480 There were changes in the repo, so I typed in 'git diff' and casually looked over the output before it hit me that I probably shouldn't be looking at this.
00:13:44.500 There might be client data in here that the RIP didn’t want me to see, so I quickly exited, but not before something caught my eye.
00:13:52.740 One hundred fifty-one lines added to this simple script that Alex had created to demonstrate the bug.
00:13:58.680 But the file we'd been looking at had been only a few lines long. I quickly tabbed over to the file in question.
00:14:04.200 Hit command and the down arrow to jump to the bottom of the file, and this is what I see:
00:14:10.560 The Grant class had been reopened. The class instance variable award count had been overwritten to be 300.
00:14:21.540 And the class method now read from the class instance variable directly. No wonder the maximum number of grants was exceeded!
00:14:29.520 'What are you looking at?' Alex had returned and wasn't too happy to see me sitting in his chair touching his keyboard.
00:14:36.300 'He looked over my shoulder at the screen and saw what I was looking at. He looked a little alarmed at first, but confusion took over.
00:14:45.840 'Uh, where did you get my old code from?' he asked suspiciously. 'That's from an old demo!'
00:14:53.640 'Well, it was right at the bottom of this file,' I said.
00:14:59.760 'All of a sudden, Alex looked incredibly embarrassed, like he wanted the ground to swallow him up.
00:15:07.260 He explained that when he was getting the repo ready for me, he'd been making quick edits, copying and pasting a bunch of code from one place to the next.
00:15:14.280 He had selected the whole file and deleted waves of it, and must not have noticed this bit of code being left behind in his haste.
00:15:20.640 'But this code must have been hanging around for a while,' I said.
00:15:26.160 'Surely you would have noticed it, unless you never do partial commits.'
00:15:31.680 Now copy-and-paste errors can happen to the best of us. I would certainly be called out by them before.
00:15:38.760 But I have to admit, though, after his behavior throughout the day, it was rather nice seeing Alex humbled a bit.
00:15:44.220 But however trivial the bug, a case isn't over until I see the code working.
00:15:52.680 So I deleted the code from the bottom of the script and ran the program again.
00:15:57.840 While it didn't end up being a meaty problem to sink my teeth into and distract me from my stressful StoryGraph situation, the case was closed. My work here was done.
00:16:06.840 Chapter Four: I was on my way out of the RIP building when I heard someone call my name.
00:16:12.960 It was Jenny. 'I've just heard from Alex that the two of you have figured out what the issue is,' she said.
00:16:19.020 'He's gone off to implement the changes now. I'm not the told-you-so type, but it was the class variables, wasn't it?'
00:16:25.380 'I knew that him using them was a bad idea.'
00:16:31.620 'You mean class instance variables,' I said.
00:16:37.020 'No, class variables!' This is Jenny.
00:16:45.180 'You don't mean class instance variables?' I asked, because there wasn't a single class variable in sight.
00:16:52.320 I didn't much like the expression that Jenny started to give me.
00:16:59.940 'Do you have time to come back upstairs, please?' she asked.
00:17:06.360 'I want to get to the bottom of this.'
00:17:12.300 We headed to her desk where she started to fetch the latest code from GitHub.
00:17:19.380 'Be careful so that I don’t see any confidential code,' I told her.
00:17:24.660 'What, there’s nothing confidential in here!'
00:17:31.260 'We started up a whole new code base just for this project.'
00:17:37.800 Now it's my turn to raise my eyebrows.
00:17:43.560 But it’s not long before the expression on my face turns into one of shock, because Jenny is now showing me the code.
00:17:51.180 It looks exactly the same as the code that Alex showed me, except for the fact that all of the class instance variables in the award classes are, in fact, class variables.
00:17:57.780 And the Grant method no longer has its class instance variable. Apart from that, it looked the same.
00:18:02.880 'Let’s run the code,' I said.
00:18:09.660 'Okay, so it still works! But hey, check this out,' I said to Jenny, pointing at something in the web browser tab visible behind the terminal.
00:18:18.720 Alex had made a commit 14 minutes ago. It was a one-line change; he'd removed a line from the Grant class.
00:18:27.420 'So before his last commit, the Grant class instead of looking like this, looked like that.'
00:18:36.060 'Let’s run this code,' I said. I had my suspicions.
00:18:43.740 And they were proven correct: this code fails in exactly the same way as the earlier bug that Alex had presented to me.
00:18:51.540 And now I'm really confused because there was, in fact, a bug, but Alex knew how to fix it all along but didn’t tell his teammates.
00:18:58.320 'Let's go and find Alex and get to the bottom of this,' Jenny suggested.
00:19:03.840 It was past 8 PM and hardly anybody was left in the office. Alex wasn't at his desk; he wasn't in the kitchen; he wasn't playing ping pong.
00:19:10.680 Max had gone, so we knew that he wasn’t somewhere fawning over him.
00:19:16.620 I spotted the quality control guy who had come over earlier and asked if he’d seen Alex anywhere.
00:19:22.920 'I just saw him! He was trying to find a quiet corner to take a personal call.'