00:00:08.830
I just got back from Gusto. Today, I want to talk about a study that I've begun into software design. After 40 years of programming, I figured it was time for me to understand a little bit more about what was going on.
00:00:12.910
As often happens at the beginning of a study, I realize there's a lot that I know in my gut that I can't articulate. So, I started writing things down. The first thing that came out of my pen was a shock to me: I wrote, 'Software design is an exercise in human relationships.'
00:00:22.960
And I thought, 'No, no, no, I'm going to write about coupling and cohesion, power law distributions, and things like that—not about human relationships.' But the more I thought about it, the more I realized that the essence of software design, the things that make it really hard, are human factors, not necessarily the technical things that I'm much more comfortable dealing with.
00:01:11.950
In today's presentation, I'll tell you about the two most important human relationships in software design. Then, I'll give you an experiment that you can run to help you improve the relationships within your teams regarding software design.
00:01:24.070
I'm going to divide the world into two kinds of people. The first kind of people use software; they might want to see its behavior change, but they can't actually enact those changes themselves. I'll refer to those people as 'waiters.' In contrast, there are people who can make the necessary changes to the software's behavior; I'll call them 'changers.'
00:01:39.580
It's the relationship between waiters and changers that makes software design such a challenge. In the long run, everyone wants to see the system change and evolve, and they would like it to happen as quickly as possible.
00:01:58.120
However, in the short run, the incentives of waiters and changers can diverge significantly, primarily because the perceptions they have of software development are so different. At one level, software development is a sequence of ideas that turn into behavior changes in the software, sparking more ideas and more behavior changes. This cycle goes on and on.
00:02:19.030
Both parties understand this cycle, recognizing the flow of behavior changes that reflect how the software evolves. However, beneath the surface, there's another set of changes occurring: changers know that the structure of the system dramatically affects the cost of making behavior changes.
00:02:33.530
Some of the investment in development goes into changes in structure, but these changes aren't visible to the waiters. This leads to conflict in the relationship. A common dysfunction occurs when behavior changes take all the precedence, causing the structure to worsen over time.
00:02:44.950
Eventually, it becomes impossible to make changes to the behavior, leading to frustration. There are issues where too much investment in structure, or making those investments at the wrong time, also damages the relationship. For example, if we focus solely on building the perfect structure, the behavior of the system may not change as waiters grow impatient.
00:03:00.140
This situation can lead to an endless cycle of investment in structure, where if you don't get it right now, you'll never get it right. Hence, we experience a divergence in incentives between waiters and changers. Maintaining this relationship is critical to software development.
00:03:13.450
Our goal should be to find a balance between the needs of both parties, ensuring that the investment in structure and behavior is sequenced so that everyone's needs are met as much as possible.
00:03:27.319
The second set of relationships exists between changers themselves. For instance, if I want to change an API that you call within our system, we are both changers, but the work I do affects your work. Thus, my relationship with you is crucial.
00:03:40.640
If I make breaking changes to the API and then insist, 'Oh, it's your responsibility to update to the new version,' you won’t be very happy with me. You already have behavior changes to implement, and you don't have time for my shenanigans.
00:03:50.210
So, it's also important to maintain relationships among changers, which can be challenging. These two fundamental relationships—between waiters and changers, and between changers and changers—can create difficulties in software design.
00:04:05.950
Now, what can we do about it? I've observed four stages of software design enlightenment. The first stage occurs early in your programming journey, where you can get programs to work by making various changes without understanding the underlying process.
00:04:14.060
At this level, you're just making changes in an if-statement, changing variable names as you go. Then, when there's a large collection of these changes, you clean them up.
00:04:28.390
Stage two happens when someone introduces you to Martin Fowler's refactoring book, leading you to realize that there are structural and behavioral changes that are fundamentally different. You start to recognize that you should wear one hat at a time: either changing the structure or changing the behavior.
00:04:44.840
Level three is where you start to think a few moves ahead, like in chess. You make structure changes to make behavior changes easier to implement, and vice versa. This approach aligns with the phrase I coined: 'Make easy, make the change easy.'
00:05:03.830
At this level, the changes you make are intentional and sequenced to make your work easier. If you haven't started doing this, I recommend you give it a try. Sometimes, software development can feel really hard.
00:05:17.640
With more experience, I've learned that if it feels hard, that's often a sign that I'm doing something wrong. While some challenges are genuine, I often need to focus on making my work easier and feeling good about the simple tasks that result.
00:05:30.440
The experiment I'm suggesting is to learn to sequence behavior and structure changes. Start dividing the changes you make into small pull requests. Make a little change to the behavior, test it, and then deploy it. Next, make a few changes to the structure, deploy those, and continue this cycle.
00:05:51.700
Each pull request should focus on either behavior or structure changes. This separation is crucial because structure changes are generally reversible. If you extract a helper method, you can easily revert that change if necessary.
00:06:11.680
In contrast, behavior changes are typically irreversible. For example, if my team at Gusto inadvertently files incorrect tax reports, it's not something that can simply be undone. There are lasting consequences involved.
00:06:26.920
As a reviewer, when examining behavior changes, you need to be aware of the test cases and potential backup plans. On the other hand, structure changes don’t generally require the same level of scrutiny if they're progressing in a reasonable direction.
00:06:40.670
Thus, the experiment involves dividing your PRs into smaller, manageable segments that either change the behavior or the structure of the system. Sometimes this means changing the behavior in a different order than you might typically do.
00:06:54.380
For example, like the keystone in an arch, the user interface changes might happen at the very end of a project, whereas in a traditional workflow, one large PR could see UI changes made at any point.
00:07:07.560
Using this approach, you can lead up to the final user interface change with multiple smaller PRs, ensuring that each increment is manageable, allowing for a more seamless integration.
00:07:20.220
Now, how does this relate to the relationships we've been discussing? A significant cost in these relationships comes from delays and variances. For example, you might have been able to complete a task in a day before, but now it takes a month.
00:07:35.060
Adopting a style that separates behavior and structure changes provides many more options for changing behavior early in the sequence if there’s urgency or delaying it for structure changes that benefit the long-term health of the software.
00:07:49.400
By splitting your PRs into either behavior or structure changes, you can ensure that waiters consistently receive a steady flow of what's needed. This approach allows you to be a better partner to them while simultaneously helping manage the relationships among changers.
00:08:10.390
If I'm changing an API and care about our relationship as changers, I might implement the new API in terms of the old one, and then help you update to the new API. After all migrations, I can remove the old version, thus ensuring a smoother transition.
00:08:24.060
This strategy of breaking down a significant change into manageable parts helps maintain cooperative relationships, as does breaking down behavior changes for waiters.
00:08:36.500
In conclusion, there are plenty of opportunities for refactoring and adjustments in coupling and cohesion that clarify how all of this works and why it works. Yet, I want to leave you with this central insight: Software design is fundamentally about human relationships.
00:08:48.670
We, as technologists, have many tools available to do a better job of maintaining our part in these relationships. Thank you very much, and I hope you’re all staying safe.