Talks

Understanding Coupling

wroc_love.rb 2018

00:00:17.380 Okay, so welcome everybody to my talk "Understanding Coupling." A couple of things about me: I'm, as Simon said before, a programmer, architect, trainer, and consultant. My main line of work involves helping other companies improve their software development process. In my experience, most companies create acceptable software, and that's where we come in. We help enhance what they are already doing. My specialties include domain-driven design, architecture, and continuous delivery—not just from the infrastructure perspective, but also from the architectural perspective. In my private life, I have a wife and five kids: three sons and two daughters. So, please forgive me if I am unable to stay until the end of the conference; I need to spend some time with them on Sundays. Hopefully, it will be okay.
00:01:45.060 Great! So let's start. Let's meet Guido. Guido is a recent graduate and a Ruby developer. Let's say he’s an object-oriented language programmer, or even functional. He recently became a team lead at his company after a few years of being a programmer. The company recognized his potential, and he transitioned into this role. His main priority now is to ensure good design within projects, and he understands that he will be responsible for the team's output. Guido's mission is to share design knowledge with everyone, guiding them on how to improve their work. He aims to foster an environment where team members can discuss designs openly without disputes, making sure everyone understands the rationale behind design choices.
00:02:35.080 However, when Guido begins this new project, he encounters a problem: how to evaluate the design. He finds himself in code reviews, evaluating software created by others, and he struggles to articulate whether the design is good or bad. His response is often vague, such as "It looks okay" or "My gut feeling tells me it’s too complicated and should be simpler." But what does simpler entail? Is it too many classes or too few? Is the elegance of the code sufficient? This is a common challenge many programmers, including Guido, face; it’s tough to discuss design abstractly. People even raise points about test coverage, but what part of the software should it cover? This begs the question: how do you evaluate design? How can you discern if a particular piece of your code demonstrates good or bad design? It’s quite difficult. Let’s focus on that for a moment. Evaluating design is not merely tricky; let me offer an analogy. If we had a mug, is it good or bad? Well, the answer depends on context.
00:05:50.260 For instance, this mug may be great for an adult but too large and easily breakable for a toddler. Therefore, what is deemed a good mug can vary based on the user's context. The same applies to our software; context is essential. First and foremost, we need to understand the context in which our product will be used. Moreover, we also need a precise description of the attributes of our design. This requires us to define the context and accurately describe what we are discussing, including temperature ranges for materials and the attributes of our software components. By having these detailed definitions, we can engage in structured discussions about design rather than vague and instinctual communications. Software development is relatively young, perhaps 60 or 70 years old, compared to other professions like architecture or medicine, which have extensive foundational knowledge. Consequently, when I ask anyone to define a module, a component, or architecture, I receive varied responses—each valid but different—because we lack a unified body of knowledge.
00:08:41.260 I will now share how I approach defining context and how to discuss precise attribute descriptions using coupling, a rather abstract concept. In any design, we can view the context from three perspectives: architecture drivers define our system's context, the business context reflects stakeholders’ requirements, and the project context outlines available resources and timelines. First is the business context, encompassing goals, visions, and problems we're trying to solve—often referred to as functional requirements. We need to understand the intended users and their specific needs. Next, the project context includes constraints like timelines, budgets, and skillsets available for the project. Lastly, Quality attributes like availability, security, and reliability are essential. The definition of these attributes is vital in determining whether our design is successful or not.
00:12:00.600 Consider an architect: the first thing they need to know is their surroundings—the available space, existing structures, governmental regulations on height, and so forth. Evaluating design necessitates context; without it, we can't determine whether something is good or bad, as the effectiveness depends on the surrounding circumstances. The next part of this discussion involves articulating design more precisely. Guido still struggles with how to communicate abstractly, so how do we discuss the concept of time, which is inherently abstract? Time has many interpretations: some see it as equivalent to money, while others perceive it as a constraint. Essentially, we need metaphors for abstraction to build intuitive understanding. Now, what metaphor can we leverage for coupling in programming? In object-oriented programming, we can view objects as individuals communicating through messages, which provides a tangible basis for understanding the abstract nature of coupling.
00:15:21.300 As we delve deeper, let’s specify this metaphor. Each object represents a person with distinct roles, similar to how they would interact in a workplace. In a company, there’s a hierarchy; the CTO issues directives, which flow down to team leads and programmers, resulting in a final product. This framework can create a natural understanding of ‘objects’ in code as they send messages through functions. Programming can be broken down into three basic interactions: questions, commands, and notifications—analogous to how we communicate. This metaphor links to coupling; observing how objects relate to one another can inform how we structure our code. The highest degree of coupling occurs within tightly integrated systems, where one object fully understands another. This often correlates with functions where different roles are perhaps consolidated into one. As companies grow, such coupling can be reduced by distributing responsibilities and enabling greater modularity.
00:18:41.250 Suppose our company expands, and as IT demands rise, we need to hire IT professionals. We start by creating an instance of John, the IT guy, to manage computer repairs, reducing the coupling between the role of the director and the IT tasks by employing someone dedicated to handling those issues. With John as a separate instance, the director communicates to him when repairs are needed, fostering a looser coupling where the director no longer needs to be involved in the specifics of the repair process. As the team grows further, we think about allowing remote work for personnel, which leads us to establish additional boundaries of interaction and knowledge separation. Eventually, we could create an IT department—as an interface—not directly engaging with John or any specific individual. This approach allows for interaction with the department broadly without direct knowledge about the capabilities or whereabouts of assigned IT personnel, further decoupling responsibilities.
00:21:54.180 When escalating further, we reach a stage where requests for services are made through an assistant or similar role, allowing the director to handle issues more declaratively. Instead of specifying what ought to be done, the director can simply report an issue, granting the assistant the autonomy to troubleshoot. This highlights a further abstraction where we have notifications of problems without needing details about resolution strategies. We can describe five unique levels of coupling within our systems ranging from tightly coupled methods to loosely defined notifications. Each level serves a purpose and can be applied in different contexts based on the operational scales we observe. Reflect on these levels of coupling. In some instances, high coupling offers advantages—in tightly controlled systems like aggregates within domain-driven design. Conversely, within microservice architectures, the preference typically lies in minimizing coupling to encourage independent yet coherent services.
00:27:10.360 To conclude, my main takeaway from this talk is the necessity to articulate design effectively. It’s essential to develop a vocabulary for discussing coupling and related design principles. The design must precede code execution to foster a healthy development culture. The sharing of knowledge and abstractions enhances communication and encourages teamwork in creating robust systems. Therefore, refine how you talk about design so it conveys your thoughts clearly while fostering collaboration. Ensure you avoid taking an instinctive approach to decision-making as Guido has occasionally done throughout this journey.
00:30:39.180 Thank you very much for your attention! I aimed for a perfect timing of around 45 minutes. While we might not have time for many questions right now, I'll remain available for discussions. If there’s interest in exploring coupling metrics in large projects, remember that it’s always context-sensitive. Though theoretical answers exist, experience demonstrates that metrics often underperform in chaotic systems where every aspect is intertwined. Instead, focus on strategies for improved communication centered on design and coupling. I appreciate your participation today, particularly given the cold weather. I hope you've all gleaned some insights from our discussion!