RailsConf 2022

Leveling Up from Planning to Production

Leveling Up from Planning to Production

by Thomas Countz

In the video titled "Leveling Up from Planning to Production," Thomas Countz, a senior engineer at Zendesk, shares insights on the transition from executing tasks as a mid-level engineer to tackling larger responsibilities as a senior engineer. He explores practical techniques that individual contributors can use to navigate complex projects effectively.

Key Points Discussed:

  • Ask First, Solve Later: Before diving into solutions, Countz emphasizes the importance of formulating questions related to the task at hand. By doing this, engineers can have a structured roadmap to follow, reducing the feeling of being overwhelmed.
  • Share Your Findings as You Discover Them: Instead of waiting until the end of an investigation, Countz advocates for sharing discoveries in real-time. This not only facilitates feedback but helps team members learn together, reducing the chances of duplicating efforts.
  • Make Code Executable for Better Feedback: During coding, making work executable allows for quicker feedback loops rather than relying solely on code snippets or descriptions. Countz encourages the use of methods like unit tests and standalone scripts to communicate ideas more effectively.
  • Prepare for Post Deployment: After code deployment, it’s crucial to validate the deployment to ensure it functions correctly and meets project requirements. Countz stresses documenting findings and metrics during this process to aid in the evaluation of the deployed code's performance.

Significant Examples:

  • Countz shares his personal experience of feeling overwhelmed in new projects and how the questions he asks help him clarify tasks. He illustrates the idea of organizing questions to navigate complex tasks with examples from his work at Zendesk.

Conclusions and Takeaways:

  • Curiosity and Collaboration: Countz concludes that curiosity and the ability to collaborate are critical attributes for software engineers. He believes that embracing these traits can help overcome challenges and contribute to team success.
  • Documentation and Reflection: Keeping track of questions, sharing discoveries, performing executable tests, and documenting the deployment process are essential practices that promote clarity and efficiency in work.

Overall, Countz’s talk encourages engineers, especially those transitioning into more senior roles, to embrace their curiosity, actively share knowledge, and develop structured practices for managing projects effectively.

00:00:00 Hello, everyone.
00:00:12 Hi everyone.
00:00:14 My name is Thomas. What’s your name? Oh, wow, that worked way better than when I practiced it alone in my hotel room, so thank you for that.
00:00:22 Let’s give a round of applause for the organizers, of course. Round of applause for our tech crew and our camera crew.
00:00:27 What an amazing keynote! We’re at RailsConf, and we’re here. Thank you for coming to this talk called "Leveling Up from Planning to Production."
00:00:34 As I mentioned, my name is Thomas Countz. I'm a senior engineer at a company called Zendesk on the core engineering team based in Copenhagen.
00:00:43 You can find me around the internet with the username Thomas Countz. Just a little bit about my day-to-day work before we jump in.
00:00:52 Zendesk is a customer service software company. They build support and sales products designed to improve customer relationships.
00:00:59 Zendesk is one of the largest and oldest users of Rails. Our founders typed "rails new" into their terminal way back in 2005, and now we see upwards of 250,000 requests per second.
00:01:13 If you're interested in learning more about Zendesk or joining our over 1400 remote first engineers from around the world working on Rails, then make sure to check out our booth tomorrow and Thursday.
00:01:26 Also, check out my colleague Christian's talk tomorrow, which is called "A Rails Performance Guidebook: From Zero to One Billion Requests Per Day."
00:01:39 I subtitled this talk "I'm an Individual Contributor — Now What?" Because when I first got started in this industry, I felt like I was thrown into the deep end.
00:01:46 I spent a lot of time learning how to code, but coding was only about 10% of my day. I felt like an imposter, not because I didn’t know how to write Ruby, but because I didn’t know how to approach even the simplest of tasks in the large systems I was being thrown into.
00:02:00 So, this talk is about some of the practical techniques that I use and that my colleagues use to help give structure to our curiosity and collaboration in order to get things done.
00:02:13 The hope is that whether you're new to this industry, leveling up to a new role, or if you sometimes feel overwhelmed, some of these techniques might help you as well.
00:02:24 There are four techniques that I want to talk about today, and they're in order from planning to production, hence the title.
00:02:39 The first one is: Ask first, solve later. Then there's: Share your findings as you discover them. Make code executable for better feedback, and prepare for post-deployment.
00:02:45 For each one, I’ll start by describing the problem that I was facing and then I’ll describe how the technique helped me solve them.
00:02:56 So let’s begin with 'Ask first, solve later.'
00:03:01 We’re going to take a look at one of the first tasks I remember being assigned in a Rails codebase that I was unfamiliar with. We’ll use this task for the rest of the conversation.
00:03:10 The task is to update the app to allow users to register with their first and last names separately rather than a single field for full name.
00:03:21 Now, this is a Rails app, and since I was familiar with Rails, I assumed I needed to create some sort of database schema migration for a users table and update any views to include fields for both first name and last name.
00:03:40 So let’s work with that assumption and jump right in, just like I did.
00:03:46 We can see the registration form has only one input field for full name. Let’s take a look at the schema.rb file, which describes the shape of the database. There appear to be two fields already—one for first name and one for last name.
00:04:01 So maybe we are storing them separately already. Opening up a production console, it looks like we are storing full names, but in the first name column, and the last name is nil.
00:04:09 Oh, but only sometimes. And what about that form? It looks like we're taking the full name input and storing it in the first name parameter. But then there’s another form partial with separate inputs for first name and last name. What’s going on?
00:04:25 If you're like me, or this purple blob, you have a lot of follow-up questions about how this app is wired together. The data is inconsistent, and there are multiple user forms. That was the problem for me. I took a look at a task I thought was simple, and when I finally started investigating, it was nothing like I imagined.
00:04:47 In a small application, I can probably pick this apart while keeping everything in my head, but for larger codebases or a mesh of microservices, I need some structure to organize my investigations.
00:05:02 In order to stay organized and not get overwhelmed, instead of starting by tackling the solution right away, let’s try again, but this time we will ask ourselves some questions before looking at the code.
00:05:16 For example, before reading any code, I had no idea how users registered for this app, and I had no idea how we store their information in the database. It’s a Rails app, so I can make some assumptions, but those are only assumptions.
00:05:39 So let’s start by writing down these questions. For each question, I like to record how important I think it is to find the answer. I ask myself, am I blocked by not knowing the answer to this, or is not getting an answer going to stop me from moving to the next step?
00:05:56 In our case, these are pretty important to know if we want to update our user field.
00:06:01 Another question I might have is around the requirements themselves. What about multiple given names like middle names? Let’s write that question down, but note that maybe finding the answer to that can wait.
00:06:19 Oh, and didn't Sarah work on something like this last quarter? I'm not sure; maybe not. Let’s write it down, but let's mention to ourselves that it could be a false lead.
00:06:26 Even if we stop here at these four questions, we can start to see that instead of beginning by chasing down a potential solution, we can first look into finding answers to these prerequisite questions.
00:06:39 For example, starting with answering how we store users' information, we can still begin by looking at the schema.rb file. But now we can address the inconsistencies without worrying about how we’re going to update the view.
00:06:55 We’re looking for an answer to how we store users' information, not how do we alter the database in order to update the UI. It’s subtle, but it’s an important difference.
00:07:19 We can follow our curiosity without needing to tackle the larger scope of work because we’ve created a sort of roadmap of questions for ourselves.
00:07:36 Oh, and when it comes to estimating, instead of using a hypothetical engineer's time and effort, we can instead get a realistic sense of what we need to do to get the work done based on how many blocking questions we've written down.
00:07:52 So, that’s step one. By starting with questions instead of the solutions, we give ourselves a little bit of structure for approaching complex tasks without feeling overwhelmed, as if we need to know everything before we can even begin.
00:08:05 We are also acknowledging the investigatory work that we all find ourselves doing, but it often stays hidden or gets done by accident. This has been really helpful for me because I found it difficult admitting to things that I didn’t know.
00:08:27 I soon discovered, however, that the thing I admired most about my colleagues is that they see their questions and unknowns as a valuable part of their jobs. They encourage me to ask questions, and so I want to encourage you all to do it too.
00:08:47 Now that we've broken down our questions instead of the solution, we're ready to find our answers. However, finding answers is work that shouldn’t be and often can’t be done in isolation.
00:09:00 Share your findings as you discover them. Sharing discoveries allows us to get feedback on how our investigation is going and it helps others learn alongside us.
00:09:14 It’s important to note that we should share our work as we're investigating and not wait until we've reached some sort of conclusion to share out our findings.
00:09:36 Let me tell you about a bad habit that I had fallen into and why sharing my findings as I discover them has helped me to level up.
00:09:55 Let's go back to our example task. We already did a little bit of investigating and found out that sometimes we're storing the user's full name in the first name column of the users table, and this behavior is inconsistent.
00:10:06 We can continue our investigation by querying the database to see just how inconsistent this data is.
00:10:14 We can search for usages of the two forms in the codebase to try to understand where the inconsistencies come from.
00:10:25 We can also use tools like Git to find the motivation behind the second form and when it might have been introduced.
00:10:37 After two weeks of investigating, we come into stand-up, bright-eyed and ready to share. So we say, "I'm still investigating inconsistencies in the users table, but after two weeks, I figured out why we have two forms."
00:10:50 And then, our ever-so-helpful colleague, who has been sitting right next to us these entire two weeks, says, "Oh, Sophia and I added that form last year. It was for the test team only; let's delete the old form since they use an API now."
00:11:06 Why didn’t they say that two weeks ago? Why hadn’t we asked two weeks ago? This is the problem.
00:11:20 I would sometimes spend days or even weeks investigating something in silence, feeling like I needed to come away with something concrete to show for all that time and effort spent. My imposter syndrome made me feel like I couldn't bother my team or ask for help.
00:11:43 If I did, everyone would know how lost and clueless I was. But correct me if I'm wrong: We're all a little bit lost and clueless sometimes. It comes with the territory.
00:12:06 This is why I now investigate in public. It’s a way to crowdsource knowledge in real time rather than waiting until we’ve exhausted ourselves following false leads.
00:12:29 So here are some ways that my team and I like to share our investigations in real time. We create subtasks in our task tracker to allow others to get a sense of what's ahead and to see the status of what we're working on.
00:12:56 We share standalone documents like gists and GitHub links, which allows us to share our notes and have other people comment on them.
00:13:13 Sometimes, we create threads in our team channel, like in Slack, which invites people to give fast feedback and hot takes.
00:13:32 For example, one time when I was sharing my investigation about a vulnerability in Bundler, there were over 30 replies to this thread. Most of them were me, but I occasionally got some really good emoji feedback.
00:13:42 Sharing while we're investigating allows us to tap into our team’s experience and expertise and record answers to questions that others probably have as well.
00:14:02 It has been extremely helpful for my team and me to collect these public artifacts as a way to organically document the system.
00:14:25 In the same way that deploying often means turning code into value, sharing our knowledge in real-time means turning knowledge into value.
00:14:45 Keep in mind there’s no wrong way to do this, and not everyone on your team needs to participate fully. Sometimes all it takes is a little emoji to point us in the right direction.
00:15:02 Now that we are getting our investigation underway and sharing our findings as we discover them, it’s time to write some code.
00:15:05 Make code executable for better feedback. As Rubyists, our primary means of expression is code, and it’s a good idea to get really good at reading and writing it.
00:15:20 However, at a certain point, sharing diffs on GitHub or code snippets in markdown can only get us so far.
00:15:39 In order to leverage the power of code during our investigation and prototyping, we should aim to make our code executable. So what does that mean?
00:15:55 Let’s say we want to prototype an idea for a fix for our user records that have the full name in the first name column and a nil value in the last name column.
00:16:16 For those users, we want to take the value in the first name column and break it apart before storing it back into the database.
00:16:32 To get feedback on this idea, let's write a little code snippet, which could look like this:
00:16:50 We define a split names method in our User model. The first thing we do is check if there’s already a last name. If there is, we just return the user instance that we called the method on.
00:17:11 If they don’t have a last name, we split the value in the first name column on any repeating white space. We use tap to update the first name attribute with the first part of the name and the last name attribute with the last part of the name.
00:17:30 Then, we return the user instance that we called the method on so that we can call save, for example.
00:17:48 Let’s see if anyone has any feedback. We could pass this code around to our colleagues on Slack, or we could print it out and fax it to them.
00:18:01 But why not make it really easy for them to execute? Because sometimes the best way to get feedback on our code is to actually run it.
00:18:15 Although one of the best parts about Ruby is how readable it is, interpreting code is something that computers are just better at.
00:18:30 So for a method like this, we could probably hold everything in our heads, but if we write something a little bit more complex or want to iterate on it, it can be a lot to ask of our teammates to review this outside of a formal code review.
00:18:45 So let’s take a look at a few ways that we might execute this code in order to get feedback.
00:19:00 The first way is to define some duck types. These are Ruby objects that act like our User model, as long as the objects respond to the same applicable method calls as our User model.
00:19:21 This could work; we just paste it in our console and see what the output might be, and for our split names method, this is probably enough.
00:19:40 If, however, we wanted to leverage more of Rails or use the database, we might instead write a test. This is a really good idea. We like tests.
00:20:02 There’s a good chance that executing this is as easy as pulling down a draft PR and running some test command.
00:20:17 If we run tests in some sort of continuous integration pipeline, we can also have assurances that we’re not breaking anything else.
00:20:34 A third way that I enjoy making code executable is by writing a standalone Ruby script in a single file. We can use the Ruby shebang to tell the terminal to use the Ruby interpreter.
00:20:54 As long as we make the file executable, we can run it in our terminal like any other script. We can even include dependencies by using Bundler inline.
00:21:06 This allows us to configure a Gemfile within the same file as our source code. Going further, if we need to use Active Record but we don’t want to pull in all of Rails, we can configure it to work with SQLite in memory, for example.
00:21:25 This has a lot of benefits because we don’t have a separate database server running, and we get a fresh start after each execution.
00:21:55 The complete code is a little bit too large for a slide, so you can find the split names method implementation on my GitHub.
00:22:01 These slides are available on Twitter and on my website, so you can find this link later as well.
00:22:16 As a little bit of a spoiler, in case you haven’t noticed, this split name method does not work for names that are more than two parts, such as when there is a middle name.
00:22:30 I invite you to iterate on this code if you're curious.
00:22:48 As Rubyists, we are fierce practitioners of executing our code as we write it, by way of writing tests. I believe that iterating on code in this way makes us really good code authors and communicators.
00:23:02 Executable code makes things less ambiguous when asking for help. It helps us understand the specific context in which the code will be executed.
00:23:20 It allows us to make changes quickly without having to keep everything in our heads.
00:23:38 Okay, so we’ve broken down our questions, shared our findings, and executed code to get fantastic feedback.
00:23:50 Now we should prepare for post-deployment. What happens once we deploy?
00:24:09 Well, we move our ticket to done, or to finished, or to ‘Oh my God, that took way longer than I thought; I'm really glad I’m done with that.’ But our job isn’t over when the code gets deployed.
00:24:35 After the deploy, we want to validate that a) our code didn’t break something and b) it meets the requirements. Even a little bit of forethought goes a long way here.
00:24:54 Before I share with you how my team and I like to do this in practice, let’s take a look at what happens when we don’t prepare.
00:25:07 So, let’s switch over to the view and update the registration page so that new users can enter their first and last names separately.
00:25:20 The existing user registration form looks like this: a single input for full name, and the code looks like this.
00:25:33 There is a text field with a placeholder for full name, but it’s correlated to the first name parameter. We can confirm that when we use this form to register a new user, the first name column is populated with the value from the full name field, and the last name column is nil.
00:25:54 We’ll go ahead and add a second text field for the last name and update the placeholder for the first name. We run it in staging, and it looks fantastic!
00:26:15 We can confirm that now when we create a new user, the names are populated correctly. All the tests and PR checks are successful.
00:26:35 Now we can ship it! No time like the present. So how’s our code doing now that it’s in production?
00:26:47 Well, this pipeline is all green, that’s probably good. I’m sure the code is working just fine, but how do we know?
00:27:00 We want to be able to answer this question beforehand, and it’s probably a good idea that you’re not the only one that knows the answer.
00:27:11 Chances are you won’t be the one getting paged if things go wrong. So a great way to get started thinking about answering this question is to document it in a commit message or PR description.
00:27:24 For example, something like this: 'Check the browser.' It helps our team validate that our work is complete.
00:27:33 Also, any executable code we provide, like this SQL query, helps us just like executing code helped us before. It’s unambiguous.
00:27:48 This is something that might be able to integrate with the monitoring system or tracking system of some sort. Adding metrics or understanding how metrics might change helps increase awareness.
00:28:03 If systems that check for consistency start to notice that things are a bit different now, I see a lot of 'Hey, this graph changed; does anyone know what happened?' messages in Slack.
00:28:20 Let’s try to preempt those questions as best we can and document them here. Failure cases are important to document too.
00:28:36 There are likely systems checking for things like exceptions and status code changes. If there's a correlation to our deploy, we want to make sure we document it.
00:28:52 So let’s try this again. How is our code doing now that it’s in production?
00:29:02 While the input validations are working on the registration form, all new users have a non-nil value for their last name in the database.
00:29:18 The metrics show consistent counts of successful user registrations, and there are no increases in errors. Awesome!
00:29:35 Okay, so that’s what I wanted to share with you: four techniques for leveraging your curiosity and making it easier for your team and you to learn together.
00:29:46 Let’s quickly recap. As soon as we pick up a new piece of work, we can write a list of questions, even if we don’t know anything about the task.
00:30:05 We can start by writing down what we don’t know, and we can make discoveries and chip away at answering our questions as we do so.
00:30:21 As we’re doing that, we can share what we’re learning to get fast feedback and help others learn as well.
00:30:36 When we have an idea, we can use executable code to help us communicate with more clarity and give our colleagues more context for understanding our problem.
00:30:54 And when our code is finally deployed, we can capture feedback and respond to any regressions or unexpected side effects with confidence.
00:31:12 At the start of this talk, I shared that at the beginning of my career, I felt like an imposter—not because I didn’t know how to write code, but because everyone around me was so smart, passionate, and quick.
00:31:29 Not everything has changed since then. I still only write code about 10% of the time and my colleagues are still super smart, and I often don’t know what I’m doing.
00:31:44 But the difference is that now, instead of feeling worry and fear and doubt, I feel full of curiosity and like I’m part of a team.
00:32:05 In fact, I believe that curiosity and the ability to collaborate are our biggest assets as individual contributors.
00:32:24 Software engineers never have all the answers; they're just good at being curious. And I bet you all are being pretty good at being curious, too.
00:32:39 Thank you.
00:32:54 We have about six minutes. If anyone has any questions, feel free to ask them now, and if not, I can give you six minutes back.
00:33:00 Hmm, yes, so the question was if I have thoughts or advice about how to normalize asking questions in different engineering cultures.
00:33:03 And if you're the only one asking questions, it can feel a bit awkward. I don’t have any great advice, but I know there are great talks in this conference that touch on culture-specific things.
00:33:22 It’s powerful to be the one asking questions because that’s kind of the hardest part. One thing I mentioned is that your team doesn’t always need to participate.
00:33:37 Of course, if they do, it certainly helps, and if they foster a community of collaboration, it can make it really easy. I’m sorry I don’t have any specific things.
00:34:00 I’ve definitely been there and I know the feeling when you’re like, 'Oh, I’m the only one.' But hopefully, there is someone else you’re inspiring.
00:34:14 If you can be a bit conspiratorial and say, 'Hey, if someone DMs you with a question or taps you on the shoulder to say, I think it’d be really good if you asked that in a public room,' that can help.
00:34:26 Yeah, thank you for that question. So the question is how do you keep these kinds of artifacts up to date when you’re documenting your discoveries and findings?
00:34:38 I know there’s a huge way right now that it’s suggested we should be hiring librarians for this because it’s a full-time job to keep this information up to date.
00:35:06 I think something my team is experimenting with is just treating them as ephemeral pieces of documentation.
00:35:22 If you have a question about something, you might search in something like Slack and find an answer. If that answer isn’t up to date, then write a comment or update the information.
00:35:36 This way, the next person looking for it can see the latest information. But that’s a very challenging problem for sure.
00:35:48 The question was how do you deal with not getting any responses, not even an emoji saying 'Nope' to you?
00:36:03 Yeah, that’s really difficult. I mean, the first thing is to have empathy for our team members. When we ask for help, they’re also doing their own investigations and their own work.
00:36:17 It can be hard to try and get team members to tag along on your issue. That’s why some of the ideas of making it as easy for them as possible can help. But yeah, sometimes you just don’t hear anything.
00:36:34 I have colleagues who go to great lengths to try to corral people into getting answers. If there’s any pressure from your manager or the team, it’s good to raise what's blocking you early.
00:36:50 For example, you could say, 'I'm stuck getting an answer to this question. I think this person might have the answer, but I'm having trouble communicating.' Instead of asking for direct feedback on the problem, you could ask,
00:37:07 'How can I get past this blocker? Does anyone know if I can reach out to another team or individual, or am I Googling for the right thing?' That’s a difficult challenge.
00:37:23 Like I said, I see my teammates struggle with that quite a lot. We have time for any other questions? Yes, here.
00:37:38 Yes, the question was during this process of asking yourself questions: what’s the interface between needing to ask product or stakeholders versus taking a look specifically at the technology?
00:37:53 It’s about figuring out where you will get that feedback from. I love the idea of nested feedback loops in TDD and testing methodologies. You have the unit test, which is a tight feedback loop.
00:38:12 Maybe an integration test or whatever you want to call it; I think it’s the same when asking questions. Where is your feedback loop?
00:38:29 The immediate term might be if you have a question, look at the code to find the answer. If not, you might need to do a bit of research on how other people solve the problem or get requirements, which can be a bigger feedback loop.
00:38:45 But it’s worth doing that questioning up front because you can end up building the wrong thing and spending a lot of time—and that happens a lot.
00:39:00 So even though some examples were focused specifically on the technical questions, there’s absolutely similar breakdowns for product-level and requirement-level questions.
00:39:15 That’s all the time we have. Thank you all again so much for joining me in this talk.