RailsConf 2018

Don't Settle for Poor Names (or for Poor Design)

Don't Settle for Poor Names (or for Poor Design)

by Alistair McKinnell

In his talk at RailsConf 2018, Alistair McKinnell emphasizes the importance of naming in software development, linking it directly to design. He explains that as codebases grow, particularly in large projects, good naming becomes essential for maintainability and clarity. Throughout the presentation, McKinnell shares insights from his extensive experience in programming, beginning from his early days writing code for mainframes to his current work with Ruby on Rails.

Key points discussed in the talk include:
- The Connection Between Naming and Design: McKinnell posits that effective naming and thoughtful design are interdependent processes that help improve code quality.
- Real-world Example (Q Cloud): The talk centers on a project called Q Cloud, a quality control application. McKinnell illustrates how the naming for certain structures (like 'attribution event') evolved through practical problem-solving and code refactoring, resulting in improved clarity and functionality.
- Recognizing Patterns in Code: He discusses identifying duplication in code as a trigger for abstraction, demonstrating through the creation of the 'attribution event' value object that encapsulates related fields in a meaningful way.
- Improvements to Code Abstraction: He showcases how the refactoring process leads to a clearer structure, as well as how the introduction of class macros and concerns in Rails can streamline code and reduce duplication, leading to more maintainable applications.
- Dynamic Design Approaches: The talk includes a focus on iterative improvements, suggesting that ongoing refinement of names and structures is a normal part of the software development lifecycle.
- Conclusion and Resources: McKinnell concludes by recommending approaches from domain-driven design and simple design principles to foster better naming practices. He encourages developers to continuously improve their naming conventions to enhance design and maintainability.

Overall, the presentation underscores that investing time in naming is not just a surface-level task; it significantly contributes to the overall design quality and lifecycle of a software project.

00:00:11.330 Okay, let's get started. Welcome! My name is Alistair, and I work at a company in Toronto called Millage II. It's a great place to work. I've been working there for about five years on a codebase that's about 12 years old. It's a really large codebase, I think it's about, with tests, something like five hundred thousand lines of Ruby on Rails code with three hundred models. It's a very large codebase and when you work in such a large system, names are very important.
00:00:24.300 I think when you're programming on a small scale, you can get away with perhaps poor names, but as your program gets larger, obviously, you have more and more names, and you need some kind of system for generating those names. So we're going to talk a little bit about that today.
00:01:11.400 It's kind of unbelievable to me, but I actually wrote my first program in 1974. It was in high school, and my math teacher decided that we should start a computer science club or something. We started writing programs, mostly to solve mathematical problems. I was in grade 11, and the program that I was most happy with solved a system of linear equations represented as a matrix. We would sketch our solutions on the blackboard, carefully transfer our program to graph paper, and check it thoroughly before transferring it to cards, as we didn't have a keypunch. We would fill out the cards carefully, bundle them with an elastic band, and send them off to the office, where they would eventually reach a mainframe for processing.
00:01:46.990 The compile cycle was about three days back then, so it’s kind of unbelievable how much things have changed since then. In those days, you'd be very careful with even small typos. You'd read your code over many times before actually submitting it, because you couldn't produce much code in one go. These days, I can write much more code.
00:02:13.000 Eventually, I learned professional programming in C, then C++, and later switched to Java. Nowadays, I primarily use Ruby on Rails and PostgreSQL as my main tools, which I have been using for about five years. Additionally, I wanted to mention PostgreSQL because what's interesting is that I learned SQL around 30 years ago, which was developed in academia in the 60s and first commercialized in the 70s.
00:02:41.260 Larry Ellison owes his fortune to bringing the first relational database into the commercial world, so SQL has proven valuable and has lasted over three decades. In contrast, my knowledge of Fortran or C++ isn't applicable today. What I found in my career is that the half-life of my knowledge is roughly five years, meaning every five years about half of what you know is no longer valuable and you need to replace it.
00:03:09.630 However, along the way, there are nuggets of knowledge that have long-lasting value. If you can find those, you won't have to rebuild your knowledge as often. That's the essence of what I want to discuss today—a particular nugget I find super valuable, which is the idea that naming is deeply connected with design, and conversely, design is deeply connected with naming. The two concepts go hand-in-hand.
00:03:39.580 I want to take you through a project I worked on about a year ago, highlighting the different steps I took to improve both the design and the names used in the code. After walking through the example, we'll circle back and talk about some of the theory and practices you could apply in your own programs to achieve similar improvements.
00:04:08.980 The program we will discuss is called Q Cloud, used by people on an iPad who are often wandering around a factory doing quality control. That's where the 'Q' comes from—Quality Control. This application allows users to design a form, which then gets filled out. We call this a 'sheet,' and a sheet can have several inspections.
00:04:18.900 Today, we will focus on sheets. For instance, if you stop at a restaurant and go into the bathroom, you might see a piece of paper on the back of the door that has signatures and names on it—that's akin to a sheet with inspections. Here’s where one of our customer's inspections comes into play. An inspection checks the color of a tamper strip, and the inspector takes a sample of the product to determine if the tamper strip is red, indicating that the inspection passes. There was also a cheese processing company we worked with, which inspected whether the cheese smells, so that’s another inspection method.
00:05:05.830 We also need to capture the name of the person who submitted the inspection, and this is a problem we aim to solve. The database table for a sheet contains several columns, revealing some intriguing patterns. For instance, there are four columns that repeat certain values. This repetition indicates something about our design; it suggests we might be missing a concept.
00:05:37.790 When I joined the team, I quickly noticed that the repetitive naming was a sign of incompleteness. The column names were telling us something about their relationships, but this wasn’t strongly represented in the application. It’s essential to encapsulate these repetitive patterns as they highlight missing abstractions.
00:06:05.140 At this point, we wanted to give a name that conveyed the concept of each inspection. In Rails, there’s a macro called 'composed_of' that helps express the idea of one object being contained within another. This is particularly true when an object can either have zero or one instance, which often makes sense for our use case. Typically, an address would serve as a canonical example; it can be part of a bill and has multiple required fields.
00:06:40.730 Using the macro, you can represent small objects hiding inside another table clearly and effectively. For our specific instance, we had two 'attribution events'—one for the reviewed submission and another for the submitted submission. Each of the inspections we identified included a user ID, timestamp, and other useful data representing a user without needing to depend on just the user ID.
00:07:02.690 During the improvements, we realized that when capturing inspection data, it's essential to save not only the user ID but also a copy of the user's email and name in case those elements change. In essence, the timestamp plays a central role in our operations to record each attribution event, enhancing our understanding of the data.
00:07:40.560 As I worked with the code, I recognized that our reshaped concept of 'attribution event' enabled us to reference a user easily and monitor changes over time. This representation is vital as we predicted the potential need to capture additional information about a user in the future, such as storing contact information. The separation of data into these smaller objects made our implementation more maintainable.
00:08:07.220 The creation of the attribution events allowed us to abstract out common functionality. We achieved this by developing a small factory method to minimize the risk of common errors in capturing data. This encapsulation of functionality brought benefits beyond abstraction. Using these new methods allows us to enforce our design decisions and maintain high-quality standards in our coding practices, which reflects positively in our collaborative efforts.
00:08:29.420 As we improved our code, we became better at spotting duplicated patterns. For instance, if multiple inspectors submitted data, we were capturing various duplicated data from inspection records. This enabled us to reconsider how we wanted to represent inspection-related functionality while encouraging a more modular approach to our code.
00:08:55.120 Ultimately, our new representation through the composed_of pattern enabled simpler methods for maintaining user attributions. With these smaller objects effectively abstracted, we could start including methods via a concern, allowing us to streamline our approach when capturing user attributions for varying inspection needs. This modular approach aids readability and offers flexibility when it comes to deploying forward-facing features with clarity.
00:09:34.290 By leveraging these abstraction techniques, we not only minimized duplication, but we also optimized error checking within our system. Various mistakes became less prevalent due to emergent patterns in our design, which helped combat the occurrence of varied naming conventions. Incorporating these improvements significantly benefits project development by enhancing our communication regarding naming practices.
00:10:23.300 After implementing our design changes, the process of capturing attributions became much simpler and more meaningful. By invoking our framework built around the 'capture' method, we streamlined how user attributions are recorded within our database. This approach allowed for a more manageable way to capture data while ensuring future scalability, as changes to structure could be conducted within confines established by the framework without breaking existing functionality.
00:11:03.320 Going forward from our Q Cloud example, the interaction between naming and design became clearer. Naming conventions enable better understanding of our design and the encapsulation of complexity. Each time we would gather insights through attribute naming, we'd make more informed improvements while deploying new functionality that always prompts consideration for potential issues down the line.
00:11:35.790 We additionally explored resources that could be beneficial for these processes. Introduction books like Eric Evans’ 'Domain-Driven Design' and Kent Beck's 'Extreme Programming' serve as great starting points—detailing how language and simple design can be implemented effectively. Evans coined the term ‘ubiquitous language,’ which stresses the purpose of having a consistent language throughout your codebase and the application team.
00:11:56.620 Implementing these language concepts can reduce friction in development processes and bolster cohesion between technical and non-technical team members. Using the Q Cloud application as an example, terms like sheets and inspections resonate throughout the code and align well with stakeholder expectations.
00:12:15.300 Ultimately, my work identified the importance of simplicity in design methodologies. By establishing clear naming principles and adhering to domain-driven strategies, we not only minimized duplication but also revealed intentions behind data structures in our applications. Reflecting on this process showcases its significance in guiding teams toward greater understanding and efficiency.
00:12:35.830 I encourage you to not settle for poor names or poor design—take pride in crafting meaningful names that reflect your structure. This insight emphasizes the need for constant reevaluation while developing your system.
00:13:00.490 Naming plays a critical role in determining how we articulate ideas about our code. The insights we gain go beyond mere semantics; they deepen our understanding of structured data and expose potential gaps that need filling.
00:13:30.950 As we wrap up, I hope this discussion inspires you to think critically about naming conventions coupled with design enhancements. A mindful approach to semantics offers extensive rewards that can elevate your coding practices in meaningful ways.