Coupling can take your system either towards complexity or towards modularity. Coupling as inherent part of system design. Not something that is necessarily good or necessarily evil, but something that is needed. We need coupling. It's like a glue that holds system together. When people are talking about coupling, or rather decoupling, they mean let's take something big and break it apart into those tiny parts. Let's take a monolith and break
it into micro services. However, if you're not following a proper decomposition criteria for drawing the boundaries of those resultant smaller components, you will end up with sharing lots of knowledge. As a result, you will end up with lots of cascading changes and as a result your system is
going to be full of complexity. A modular system is a set of, let's say, modular components, but working not only towards the goal of that system today, but it is also designed in such a way that we'll be able to support goals that will be defined in the future. Hello, guys, I'm very happy to bring back another repeat guest to the Technical General Podcast. Today we have Vladik Kononov. So flat, really looking forward for this conversation.
I still remember our first conversation, right? It was probably two years ago. Your episode is still within, you know, top ten. And I'm really glad to speak to you for your new upcoming book, Balancing Coupling and Software Design. So welcome to the show. Hey, Henry, thank you so much for having me again. And wow, time flies. Two years. Yeah. And I remember two years ago we were talking and I said that the coupling book is going to be finished soon. And here we are two years later.
Yeah. So that I think it's been quite a while, right. So maybe in the beginning you can share apart from this book, anything else that you did probably interesting for you to share with us? So I'm mainly known by my work in the domain of DDD Domain Driven Design, and that's primarily the Learning Domain Driven Design book and the Cardboard Domain Driven design and people who are on Twitter, maybe they will be able to find
out what that means. Right now, in last three years, I've been working very hard to finish the book on Coupling. Hands down, that's the hardest project I ever worked on. And I can't believe I'm saying this, but it looks like it's almost done. Right. Yeah. So in preparation of this conversation, I actually had a chance to read the book.
I think I would say almost, you know, end to end, cover to cover, although it hasn't completed right on O'Reilly in the beginning, I was actually quite interested to actually figure out what exactly can you talk about coupling right? So it seems like almost everyone in the UNI learned about this coupling, right? But probably only a little bit, right? Like OK, coupling, cohesion, object oriented encapsulation and things like that. But you actually covered it in one book.
And after I read it, it's actually very, very insightful. So let's try to engage a conversation about coupling today. So maybe in the first thing, right, we all know about coupling. Maybe we learn it in the university. What else that you are trying to explain actually by writing this
book? Yeah, so coupling, I remember when the idea for this book came up and I was talking to my friends about it and one of them said coupling, like, OK, so coupling is bad and do you need like really 300 pages to say that that is bad? But as they say, the devil is in the details. So it happens quite often that we're using certain terms without fully understanding them. Like we have that gut feeling about something. So we are using that in our
day-to-day language. However, we are not able to clearly describe what that something means. And that's something for me was coupling and cohesion for many, many years. I remember when I attended the first ever meet up, I attended, it was about coupling and cohesion. And yeah, I came out with this feeling, that gut feeling level. I understand what that means, but how can I explain what coupling is? Why is it bad? What is cohesion?
Why is that good? So that's why I wanted to write this book and share my findings, my research on this topics. And if you're going to read the book, you'll see that although it's called balancing coupling and software design, it's main topic is complexity and modularity. I want to show what complexity is exactly in software design because again, that's another thing that we understand on the gut feelings level, but it's challenging to describe and it's
even worse for a modularity. Like modularity, OK, they started talking about it in 60s, seventies, 80s, and today we all know that that's the goal we should strive towards. But what is modularity exactly? Like how can you put a number on a design? How can you evaluate whether it's modular or not? So that was my goal, to show how these two things are actually, or rather two sides of the same coin. And that coin is coupling. Coupling can take your system either towards complexity or
towards modularity. And yeah, for that I had to write almost 300 pages. Right. So I think maybe let's try first of all to define the terms what is coupling, right? Because I think people know about low coupling, high cohesion, right? It's always like the terms that you always use, right? Good code means like low coupling, high cohesion. Maybe people associate coupling with just, you know, maybe functions or classes, design and things like that.
But in your book, it is actually beyond just code level, right? It can even go up to organization level or different kind of services, right? Maybe let's start with the definition 1st. In your book what is the definition of coupling? Yeah. So let's say we're working on a system. A system is a number of components that are working together to achieve a goal. That goal is the reason why that system exists. Now, in order for those components to work together, they need to be connected,
right? Because they cannot be fully independent of each other. That will be just a collection of components. That's it. But to make the value of that system higher than the sum of its parts, we need to connect them. And those connections are basically coupling. If you go to the dictionary and look up that word coupling, well, you'll see that initially what it means is not manolith, not big bowl of, but it means connection. If two things are coupled, they're connected.
So I will ask you for the rest of our discussion, at least for the next 4051 hour. I don't know how long we're going to talk, but for the rest of this discussion, let's treat coupling as inherent part of system design. Not something that is necessarily good or necessarily evil, but something that is needed. We need coupling. It's like a glue that holds system together. We need it.
However, how we're designing those interactions, how we're designing those connections between the components, that's where it gets interesting. That's where that coupling can take us, either towards comlexity or towards modularity. Right. So I think it's interesting that you mentioned about connectedness, right? It doesn't mean that when you have coupling, it's actually
bad, right? Because coupling is necessary whenever you build software design because that's how components actually work in collaboration with each other, right? And you mentioned about connectedness in your book in terms of there are two things that probably are related when you talk about coupling. The 1st is about shared life cycle and the other one is about shared knowledge. I think having an understanding
of these two is really critical. Before we move on to the next section, maybe you can talk a little bit more about this shared life cycle and shared knowledge. Yeah, of course. So first of all, let's assume we have two components in the system. Now I'm purposefully using these abstract terms components in the system. I'm not telling what these components are. They can be services, they can be objects, classes, even methods within a class. And same is true regarding this system.
It can be a distributed system, it can be a module or an object, an object oriented codebase, doesn't matter. So let's say we have two coupled components. Now when we are saying that coupling is bad, what we usually imply is that we have to change those two components simultaneously. For example, you want to change one of them, but then you're forced to apply a corresponding change to the second component, right? And that connects their life cycles.
They have to be evolved simultaneously. Now, is that something we are interested in? It depends. We'll talk about it a bit later, I assume. But right now, let's assume that coupling introduces that dependency in the life cycles of connected components. Now, propagating changes in almost all cases is not something that we want. We want to be able to modify each component individually. Once we introduce those connections between components.
In order to work together, they have to share some knowledge about each other and the amount of knowledge they share. It actually dictates what is the amount of cascading changes that are going to follow because of that design. That knowledge can be as simple as a knowledge of integration interfaces. Let's say I have a module, so I define its public interface. You want to work with it, you have to know the details of its public interface.
Or maybe, let's say you want to ignore that public interface and you want to use some other means of integration. Let's say you want to reach out to my database. In that case, maybe you want to, you will have to know the implementation details of my module. So that's the other extreme of knowledge that can be shared.
And of course, if I'm sharing only the integration interface, then the amount of cascading changes that are going to follow is going to be dramatically lower than in the second case where you're coupling yourself to my implementation details. So overall, these are two properties that are very
interrelated. The first one is how connected are the life cycles of those connected components, and the second one is what is the amount of knowledge that's been shared across the component's boundaries. Yeah, thank you for explaining this. I think it's such an insightful concept when I read it in the first few chapters, because maybe the life cycle is kind of like straightforward, right? Whenever you make changes, if you have to propagate into multiple places, right?
It could even be services or multiple teams. That is actually pretty bad. And we know that it's actually tightly coupled, right when we do that. But actually the most important thing that I find really interesting is about the shared knowledge, because there are explicit knowledge that you share, maybe about business requirements or maybe even the interface or contract APIs, right, when you interact with APIs. But also there's implicit knowledge that probably is a bit
tricky to identify. And that's why you find it difficult to explain the coupling by just looking at the code, right? Because it's such an implicit thing and let's try to actually analyze in terms of coupling and complexity, right? Because people associate coupling a lot with the complex code. So you try to explain it with Kinovin framework, right? So maybe a little bit of background, why is it such a relevant to talk about Kinofin when explaining this coupling
and complexity? Yeah. So first of all, I want to go back to your first comment about the relationship between life cycle coupling and knowledge, because I think it's super important what you said here, because when people are talking about coupling or rather decoupling, they mean let's take something big and break it apart into those tiny parts. Let's take a monolith and break
it into micro services. However, if you're not following a proper decomposition criteria for drawing the boundaries of those resultant smaller components, you will end up with sharing lots of knowledge. As a result, you will end up with lots of cascading changes. As a result, you're going to end up cursing that very day you started decomposing it. And as a result, your system is going to be full of complexity.
Now, complexity is something that you know it when you see it. But again, we need something more precise, something that we can explain. So that's why I chose to use Kinemic Framework. I really like how this framework, originally it's a decision support framework. It provides with different, we are talking about software engineers, so let's say different scripts for making decisions and how to make decisions in different situations.
And it categorizes those contacts into five domains, each one requiring unique approach to making decisions. If let's say you are working on a system and again we are software engineers, let's say we're working on code base and I want to make a change. If I know exactly what the effect of that change is going to be, then that code base is not complex, right? It's probably simple. That's why this domain, in kinetic terms is called a clear. It's something that is simple clear.
If I'm making a change, I know what's going to happen. If, on the other hand, I am looking at the code base and I have no clue what's going on there, however, I can consult somebody, some external expert, and that expert will tell me exactly what's going to happen. Then again, it's not complexity. This time we have a complicated system.
For example, let's say I am looking at a code base written in Go, or I don't know Scala. Even though I tried to learn these two languages, I would still prefer to call an external expert to help me out to understand what's going on there. So again, that's not a reason to call it complex. That's something that is complicated. And we arrived to the third domain, which is a complex domain. Here we have no idea what the effect of a change is going to be. Also there is no expert we can
consult. So the only option we have is to conduct a safe experiment, which means to make a change and observe it's results and based on those results to try and understand it's impact. So that's complexity. Now in this domain, we assume that there is a connection between an action and it's outcome, A cause and effect. Now in the fourth domain, which is called chaos, there is no relationship between A cause and effect, the effects are random.
Now, such things do happen in software of course, especially in production environments after deploying Fridays or on public holidays. However, this is something that is rather not related to our discussion of software design. It's more related towards the runtime of the system. But in software design, when we're changing the system, it should be somewhat deterministic one way or another. And finally, the 5th domain of Canavan, it's unknown.
When you're looking at the domain, you have no idea where you are. And in this situation you may assume that it's clear, it's something simple, but as I say, devil is in the details. And when you get to details, it's only discovered that it's complex domain or chaotic domain or whatever. So these are the five domains.
And I really like the way Dave Snowden, the author of this framework, defined complexity when the only way of identifying outcome of an action is conducting safe experiments. Unfortunately, that's something that is pretty frequent in software design when you're looking at the code base and you want to refactor. Well, if you know what's going to happen, great. Maybe it's complicated and that
expert is the compiler. Maybe compiler can tell you what's going to happen or a colleague that build that code base in the 1st place. But in many cases, when we're working with legacy systems, brothel projects or big bowl of Muds, we make a change. And I had a friend I still remember that I was working at, It was actually my first, like real workplace. And that colleague of mine showed me, look, before doing something important, I have these stones. I place these stones on my
keyboard for good luck. So once you need those stones for good luck, you're in complexity. And yeah, you need to find a way out of it one way or another. Right. I think that's a very relatable joke, right? And some people also like think of it like test in production, right? It you will only know once it hits production with the real traffic, real data and real user behaviors. So I think, yeah, when you find that kind of situation, probably it's more about complexity, right?
It's not about complicated, it's not about something like chaotic as well, right. So I think this kind of framework really, really good to picture what kind of a domain you are dealing with. And thanks for pitching in about this splitting, because people normally when they deal with coupling, they will say, OK, I will just split more, you know, like micro surveys, for example, or create more classes, create
more abstraction. The other thing about dealing with coupling for some people, right, they build more flexibility when you say coupled or it means like difficult to change. So people try to build more flexibility, degrees of freedom, right? In your book, you actually find that these two extremes actually is very dangerous and it might lead to complexity in terms of code, right? And you classify this sometimes as accidental complexity. The other one is essential
complexity, right? So tell us more about these two different complexity as well, because I think it's really important for software engineer to understand essential and also accidental complexity. Yeah, of course. So let's say that you're working on a system for a non trivial business domain. Because come on, nowadays if if the business domain is trivial, JGPT is going to do it.
So assuming that you are working on a non trivial business domain, there will be some complexity in it, complexity that is driven by its requirements. For example, let's say it's financial system. That domain is not simple or a medical system. Even worse, if there's something interesting that the system is doing, usually it involves some complexity. That's the reason the system is being built, in order to help its users. To encapsulate that complexity in the system to take care of it.
So that's the essential complexity of that business domain of the problem you're solving. There is no way of avoiding it. If you avoid that essential complexity, then your system is not doing it's job. Why do I need it if it's not going to help me to take care of that complexity, right? We cannot avoid it. So we have to manage that complexity. How can we manage it? Well, we have plethora of tools from domain driven design to design patterns, whatever.
We can talk in details about each one of them, but we'll need them 24 hours at least. So that's the essential complexity. Again, something that you manage. The accidental complexity on the other hand is something that you introduce, something that you introduce by not designing your system properly.
And again, if we define complexity as that implicit relationship between an action and its outcome, then accidental complexity is designing a system in such a way that whoever is going to work on it next, or maybe even me tomorrow, won't have any clue if a line has been changed, what effect of that change is going to be on other components within the same system. Or maybe it's interactions with other systems which are even worse and even harder to predict than effects within my code base.
Again, it's all matter of design of how you architect your system and how you design those interactions. Which means those connections, which means coupling between it's components, whether it's going to introduce that accidental complexity or on the other hand, it will help you to manage that essential comlexity. Right. So I think when we talk about complexity, Please remember in your mind, right? Are you talking about the essential complexity, which is actually the problems that
you're trying to solve? Maybe it's the business requirements, maybe it's like a functionalities that you have to build an algorithm, right? Or are you actually dealing with accidental complexity things like you weren't aware you designed something and actually the impact is a little bit unpredictable, right? Maybe you expect one conflict change, but how come that you actually deal with so many changes, cascading changes in multiple places?
So I think don't introduce too much accidental complexity and probably coupling is actually one of the root cause why accidental complexity can happen. So let's move on maybe from complexity to the other one, modularity. As you mentioned right when you write this book, you want to cover these two areas really important. So I think modularity also kind of like covered a lot right, in basic software design or you
know, programming courses. But what is something different about modularity that you want to talk about in relation to coupling? Yeah. So first of all, to define modularity, what is it? If you look up that word in many places you will find the recursive definition of like a modular system is one that build
of modular components. Thank you for that, that really really helps me. On a serious note, you may also find definition saying that a modular system is one that is built out of interchangeable components. A system that resembles Lego bricks that you can connect with each other and form something else. Now let's say you're working on a typical knowledge management system for a business. It doesn't resemble Lego bricks. I don't think so. Do you need interchangeable
components? Maybe you will have to change the database one time because you will need to run your tests on in memory database, which is not going to be a real one. But that's it. That's probably the limit of our interchangeable components. Maybe you should encapsulate all the managed services of your cloud vendor so that you can
migrate to another cloud vendor. Maybe it should, but trust me, those abstractions you're going to introduce are going to be leaky anyways, and that migration is not going to be as simple as people hope. So still doesn't mean that we don't need modularity. Now. We definitely need modularity. However, we also need to define what we mean by it. And here I want to go back to the definition of a system that I started with early on saying that a system is a set of components working towards a
goal. A modular system is a set of, let's say modular components, but working not only towards it's goal, the goal of that system today, but it is also designed in such a way that we'll be able to support goals that will be defined in the future. Now to support goals that are going to be defined in the future doesn't mean that out-of-the-box it has to implement all possible use cases. No, of course not. And that's not possible.
The design of that system should be able to withstand implementations of those future use cases, those future requirements. Now this begs the question, OK, so let's say I'm working on knowledge management system for a business in financial domain. It doesn't mean that my design has to support the option of that system turning into a driver for a printer in the future. Of course not. That's not a goal that you have
to think about. So when thinking about modularity, we have to take into account future goals, but they have to be reasonable. How do we identify those reasonable changes? Well, that's another thing that makes our job so hard. So we have to think about those future changes. Which changes are going to be reasonable and which are not. Of course, the example I used of a printer driver is a simple one. But yeah, real life is a bit more complicated.
So again, going back to the definition of modularity and modules and modular systems, the goal is to design a system in such a way so that it will support future requirements. Once it does, then we can say that those components it is composed of are actually modules. So that brings me into another problematic topic of modules versus components, because I define the system as a set of components and the modular system as a set of modules.
Now if you look up on the Internet the difference between these two terms, you will see some problematic definitions. Some sources claim that modules are, let's say, logical boundaries, whereas components are physical boundaries. Now this thinking model can be useful in some contexts. However, if you go back to the origins when the term modularity was introduced to software engineering, you will see that back then they had three properties of a module.
One of them was that it should encapsulate a behaviour, meaning that we have some behaviour that is encapsulated in one module instead of being spread across multiple modules. Second, it has to expose an interface through which other parts of the system can execute that functionality. And 3rd, it should have potential of being independently compiled. Only potential. It doesn't mean that whether you're compiling your module independently turns it into a component.
No, it is still module. Again, that definition is, in my opinion, it's flexibility is very useful. Yeah. So the difference between components and modules, it's not about the type of boundaries, whether they're physical or logical. It's about encapsulating functionality. And that's something that is essential for managing the essential complexity and not introducing accidental complexity. Right. So I think that's really insightful, right?
This is the first time I think I kind of like find the definition of modularity, you know, like the concept of knowledge boundaries, right? And you understand like the difference between modules and maybe components and things like that, right? And I think it's very important, right? When people talk about modularity, most of the times what they're doing is actually like create more abstractions, things like in object oriented
programming, right? When you want to create more modular system, you create more abstractions, right? You introduce maybe interface, you know, abstract classes and things like that. First of all, is this the right way? And second thing, right, in your book you mentioned about module is actually a knowledge boundary. So I think this is also something very insightful, right? You are not talking about physical logical boundary anymore, but it's all about knowledge boundary.
And maybe this is related to the shared knowledge that we discussed earlier. So tell us more about this abstraction and also about this knowledge boundary. Yeah, sure. So introducing abstractions it can be beneficial, but it can be a very effective way of introducing accidental complexity. Again, whether you're using it to manage the essential complexity or introducing new type of complexity depends on
your concrete context. Now, one part of me want to say it depends and then advance to the next question, but I'm not going to do it. So let's talk about what it depends on. So let's say I have two components and I'm thinking whether they're going to interact with each other directly or should I introduce an abstraction between them that will encapsulate something?
Now we have to ask, will that additional abstraction help us to encapsulate knowledge or in terms of David Pranas to hide some information, whether it will help us to use the boundary of one of the modules as a boundary of knowledge? So let's say I have a module that implements some functionality that is not trivial. Let's say it's going to be about encryption. Yeah. And let's say I made that very smart decision of implementing my own encryption algorithm.
So one way for us to work together would be, I tell you, Henry, yesterday I had a few beers and I was thinking about that super secure encryption algorithm. So from now on, I'm going to communicate all the information encrypted with that algorithm in order to decrypt it. Here's what you have to do. And then I brain dump all that after beers thinking about that encryption algorithm on you and you have to implement in your code base in order to decrypt my
data. What's going to happen is we have that knowledge of that probably dumb encryption algorithm duplicated in two places. One place is my module, which encrypts data, and the 2nd module is yours that has to decrypt that data. Of course, since I made that smart decision of coming up with an encryption algorithm, it's going to change. Probably there are going to be a security patch, let's say in
after a few days. So now I will apply the change in my code base and I'll call in and say, Henry, that's urgent. You have to modify a few lines in that algorithm because otherwise you're not going to be able to decrypt my data. That's the effect of duplicating knowledge. Now, of course, that's a simplified example. In real life, we're talking about a business domain entities communicating with each other. But that is the same. We have that knowledge that is duplicated.
Now we could have used a module to encapsulate that knowledge to hide that encryption algorithm in one place. And in that case, the functionality of that module would be encryption and decryption. It's interface would be probably 2 methods for encrypting and decrypting data, and it could be independently compiled, or it could be compiled within our monolithic code base. It doesn't matter, it's still a module.
Now, if we compare the details of an encryption algorithm versus having an interface with two methods, encrypt and decrypt data, the difference between the two amounts of knowledge is substantial, right? In that case, that abstraction is going to be super useful. It manages the essential
complexity. That encryption algorithm is encapsulated, so it helps us to make our system simpler because all those other components that will have to use it, they don't have to be aware of the internal details of that logic. On the other hand, let's say we're building a system and that pattern data transfer objects, it gets lots of hate.
Like people are saying OK, so I have my business entities and then I have to introduce Dtos that are going to be on the boundary of my application and my APIs are going to return those Dtos right Now why people love to hate Dtos? Because in their code bases, usually what they are doing is translating one data structure to another data structure which looks exactly the same. So you have just basically copying values from one data structure to another one.
However, as long as the attributes of those data structures, those types of those attributes, are exactly the same, we're not actually encapsulating any knowledge by introducing that abstraction. What we're doing is we're introducing another moving part. We're introducing another object or another class that we'll have to think about. When we are going to change our model, we'll probably have to apply the same change. Let's say we want to change the
name of a field in both places. Now in this case, that DTO is not an effective obstruction. It increases complexity. It introduces accidental complexity. Now, am I saying that DT OS are bad? Of course not. I just gave you an example in which DT OS are useless. Sorry, maybe not useless, but they're not providing the value. But on the other hand, they're introducing another something you have to think about.
And the more things you have to think about, the more the cognitive load and our cognitive load limits are not looking good. There were studies done in 50s, then repeated in 2000, and they were not that spectacular early on and the number was reduced 50 years later. And yeah, So going back to your question of abstractions, it depends. If abstraction helps you to encapsulate knowledge, it's effective.
If it doesn't, then it's just going to introduce additional moving parts and you'll end up increasing the accidental complexity of the system. I'm so glad that you brought up the DTO discussion, right? Because I think, yeah, it's like a love hate kind of a pattern for some people, they find it like a more work in terms of creating the APIs. You know, you have to build this translation, which is probably the same when you build the system first time, right?
It's like mapping the same kind of data structure. But I think if you think about it, if the system evolves, the API evolves, and your implementation details changes over time, maybe the database, how you store the data changes, right? The detail is like a boundary, right? A contract that you probably pass on with the other teams or the other service, and that doesn't change as long as you create this proper abstraction. So I think thanks for bringing that up.
Let's move to the next topic. I know that we can probably talk a lot more about complexity and modularity, but I think I want to bring up another fascinating thing that you brought up in the book, right? It's actually how to measure coupling. So in the beginning, you mentioned like sometimes it's very hard to actually put a number of how coupled is our code, right? It's always like a, you know, rough idea, maybe estimation.
But actually in your book, you bring three different dimensions that we can use to kind of like quantify complexity, coupling and things like that. And you brought up the point about integration, strength, space or distance and time or volatility. So maybe, I know it's pretty hard to explain all three at the same time, but maybe if you can elaborate, what are these 3 dimensions dimensions, How can we use them to actually assess the complexity or the levels of
coupling of our code base? Yeah. So evaluating coupling is something that we are trying to do for ages now, I think since 60s when the structure design methodology was coined. They started working on it in late 60s. So they introduced a model for evaluating coupling. Then there was kinesins and then there were some metrics and those metrics try to put a number on on coupling. The problem with that approach is that it was based on counting variables or counting methods.
But is that number something that you can really, really trust? And there is a talk that I did at a few conferences with my friend Sonia Natanzon in which we're talking about software design metrics and basically saying that you shouldn't trust them. And there is a metric called stability, which is about the relationship between the incoming connections and outgoing connections, afferent coupling and afferent coupling as an ASL speaker really hate these terms, like passionately.
That metric says that if there are more modules that depend on you, then the number of modules you depend on, then your stability score is going to be higher. So in that talk I'm showing an example which is supposed to be perfect from that sense. Like a module that let's say 100 other modules depend on it, and our module only depends on one other external component. That's it. So the ratio is one to 100. So it's supposed to be super
stable. However, again, as I mentioned a couple of times, the devil is in the details. And on the next slide I'm showing how that one dependency is implemented. And what I'm showing is that it uses reflection to read a value of private field. So what's going to happen? Well, once I'm introducing that dependency on an implementation detail, any change the implementation details of that external module has the potential of breaking my component.
Now, once I have something that has to be changed in my component, then I have 100 other dependencies that are probably going to be affected by it. So now what we're getting is a perfect stability score. Ended up being big bowl of mud basically. So that's why initially I try to avoid putting numeric values on coupling, because it's all about details, Details that are hard to quantify and hard to
describe. So instead, in the book I propose a different model of evaluating coupling that is based on three dimensions as you mentioned. The first one is the dimension that measures the knowledge. What is the amount of knowledge that is shared between two connected components? How do you measure knowledge? Can you put it on a scale and have a number next to it? Of course not. Maybe someday, I don't know.
By the way, when I was doing talks at conferences on this subject, almost always somebody from audience says that, hey, you really have to think about a way of doing this automatically, of evaluating knowledge in the book. I said, OK, I tried, I failed. That's topic for further research. If you want to do it, good luck, I'll be happy. So knowledge. So we cannot put a number in it yet.
However, we can look at that early work, back from late 60s, early 70s, the structure design methodology, and back then they introduced a model called Module coupling. It had 6 levels that are kind of challenging to apply in our modern systems because that model is based on languages such as COBOL and Fortran. It's like fun, but the opposite of fun. Instead of having to learn those languages and what those keywords mean and how to adapt
to translate them to our world. In the balancing coupling book, I proposed a different model. I called it integration strength. It is based both on structured designs model and another one. I'll talk about it in a minute. So basically it adapts the six levels of integration strength into terminology. That's going to be more convenient for us today. The four terms are contract model, functional, and intrusive coupling. They are not defining the amount of knowledge, they're defining
the type of knowledge. So let's start from the biggest one, intrusive coupling. This one means that I'm using something other than public interfaces for integration. Let's say I'm using reflection, let's say I'm using another micro services database directly, or something else, whatever that wasn't intended for integration. I'm introducing an intrusion into it's boundaries. That's why it's called intrusive coupling.
Now, once we are introducing intrusive coupling, we have to assume that the author of that module might have no clue that we're doing that thing, which means almost any change that they're applying has the potential of breaking the integration. So lots of knowledge. The integration interface is fragile. It's implicit, so expect cascading changes. Another type of knowledge that can be shared is the knowledge of functional requirements. I called it functional coupling.
If previous one was about how that upstream module or component is implemented, this one is about what is the functionality that the component is implementing. Now let's say that we have two components and they implement closely related business functionalities. That means that probably they will have to change simultaneously because of the same changes in business requirements. That means we have functional
coupling. An extreme example of functional coupling would be let's say we have the same business rule implemented in two places or the same business algorithm or the same business invariant. And from the business standpoint, if the requirements or the definition of that rule changes, they have to change simultaneously because otherwise the system is going to be in an invalid state. In that case, we have a very strong case of functional coupling. By the way, they don't have to
be physically connected. I call it wireless coupling. Another case of functional coupling is, let's say we have multiple operations that require concurrency control, probably because they're going to work on the same set of data, right? In that case, again, functional coupling. Or maybe we have operations or functionalities that have to be implemented in a specific order, one after another. That's also case of functional coupling. That requirement of being executed in a specific order is
probably there for a reason. Probably it introduces some kind of business dependency there. So that's the level of functional coupling. Here. We are sharing the knowledge of our business requirements. Now to implement those business requirements, usually we have to model a business domain. We have to understand what's the system we are implementing and define a model that represent
that business domain. And then we are going to implement the functionality, the requirements in code using that model. If you have two components that are based on the same model, which means if the model changes both of them have to change, then you have model coupling. And finally the lowest level is contract coupling. So contract coupling we can think about it as a model of a model. Remember the discussion about DT
OS? I used an illustration of an ineffective DTO and then you handry elaborated and discussed an effective use of DT OS. In that case, those effective details are contracts, integration contracts. That's a model of a model that was crafted with the purpose of encapsulating that model that's being used internally. Whenever that model changes, we can contain those changes behind the same integration contract. So we are minimizing the knowledge that we are sharing across our boundary.
So overall, these are four types of knowledge. We can share the knowledge about our integration contracts, about how we see and how we think about the business domain. It's model, model coupling. Then we can share knowledge about our business requirements, functional coupling. And finally, we can share knowledge about our implementation details and that's intrusive coupling. So overall, these 4 levels are not going to put an exact number on the weight of knowledge you're sharing.
However, these are four different types that signal different amounts of knowledge that's been shared. Now if you're going to read the book, then you'll see that each one of these types has also degrees except for intrusive coupling, functional model, and contract coupling. They have degrees so that first of all you can compared to designs of the same integration strength and also to give you a more fine grain control on the knowledge that's being shared
there. So again, integration strength overall 4 levels and three of them have degrees. Now the four levels are based on the structure designs, module coupling model from late 60s, early 70s and those degrees that I'm using for functional model and contract coupling are based on a model called Kinesis which was introduced in 90s. So integration strength kind of combines both.
Let's say that you evaluated that knowledge using integration strength, You have two components and you identified what is the knowledge that's been exchanged between them. Does it say that let's say functional coupling is necessarily bad, or model coupling, is it worse than a contract coupling? Well, it depends If you can reduce it, of course you have to reduce it. If you can turn a model coupling into concert coupling, probably you should do it, but not always.
Sometimes you have to share a model, or sometimes you have to share business requirements, right? Does it mean that your design is bad? No, it depends on the next dimension, the dimension of distance between those connected components. Now, since the beginning of our discussion, I was using abstract terms to describe systems. I was saying we have sets of components. I purposefully didn't mention whether those components are methods, objects, services, or
whole systems or whatever. That's because we can introduce coupling across different levels of abstraction. We can have coupling between methods, coupling between objects, coupling between modules, namespaces, services, whatever. Whole systems. Now the higher we go on that layer of abstraction scale, the higher the physical distance between the source code in which those components are implemented. Like extreme case, let's say you have two methods within the same
class. Probably they are going to be close to each other, probably in the same file, right? Different objects, probably different files, different namespaces, different folders, different services, maybe different repositories, different systems, maybe different companies, etcetera. So the higher you are on that scale feel longer the distance. Why is that important? It's important because if you combine it with the knowledge, you get a sense of whether you're going towards complexity
or towards modularity. Because let's say that you have two components with functional coupling between them, which means they're sharing a lot of knowledge and you're putting them in separate micro services, which means the distance between them is big as well. Now that functional company kind of implies that we're sharing lots of knowledge. So if something is going to change in that knowledge, that change is going to be propagated across the boundary.
So both of them are going to be changed simultaneously. Now, if the distance is big, it is going to be an easy change. Probably not. The bigger the distance and the harder it is, the harder it is going to be to imply the change. In other words, we can say that the bigger that distance, the more coordination effort will be needed to implement a change that affects both couple components. So if we have both integration strength and distance high, we
get complexity. We're looking at the system in which we want to change the component. But in order to understand the effects of that change, we have to investigate components that are located far away from us and maybe in different repositories even. Is it easy? No. Will it require a cognitive efforts, lots of them, right. So that will result in cognitive load and as a result it will result in complexity. Now what if we do the opposite of that? Let's say we have two components
that I'm not sharing knowledge. Let's say we are on that contract coupling level. So we're putting them close to each other in the same a module, the same namespace, the same package with the recalled. So both values are low. And if all we have is 2 components, that probably, yeah, who cares. But usually in real system, it's not going to be two, It's going to be way more.
And once you have way more unrelated things located close to each other, then when you have to make a change, you suddenly have to find that thing you have to change, right? And the more options you have, the higher the cognitive load, the higher the cognitive load as a result, the higher the complexity. So at this point, we can identify complexity as a situation in which integration strength is equal to distance, both are low or both are high,
we get complexity. Now, what is modularity event? Well, modularity is the opposite of complexity. If you're working on modular system, you should know exactly what the effect of a change is going to be. So we can apply it here as well. Let's say that if complexity is in the case of both strength and distance being equal, then modularity is when they're not equal.
And again, extreme examples, if you have high integration strength and there is no way for you to reduce it because that's the business domain, that's your essential complexity, deal with it, then how can you manage it? Well, you can put those closer related things close to each other. You can minimize the distance between them.
Yes, they will have to change together, but once they're close to each other, the cognitive load on you is going to be lower because it's almost like modifying the same thing or vice versa. Let's say we have minimal knowledge shared across couple components. We have contract coupling. Well, what should we do? The distance should be the opposite. Let's spread them apart. Let's spread them across, let's say name spaces or services, whatever.
Instead, in those resultant services, we'll put only things that are related to our modules, in other words, those that do have high integration strength. So that's the relationship between distance and integration strength. We can use them for evaluating complexity and modularity of the code base. Now there is other dimension, and that's the dimension of time or the dimension of cutting corners. I call it being pragmatic. Let's say we have two components with functional coupling between
them. No, not functional. Let's say intrusive coupling between them and big distance between them. Let's say we have two systems. Once we're talking about systems, then the distance is big and we are introducing intrusive coupling between them. Is that design necessarily bad? Well, that's tricky question because from complexity standpoint, we should say yes, right? However, what if that upstream system is not going to change? Never.
Let's say it's a legacy system and you have to integrate with it, and that system is dead. Like in your company, nobody has that courage of touching it, but you still have to integrate with it. So should you roll up your sleeves and get your hands dirty and implement additional endpoints for proper integrations through contract coupling? You probably could. However, given that that's a legacy system and it's not going to change, it's fine to take its data from its database, for
example. So yeah, you are introducing intrusive coupling. However, since the volatility of that upstream system is low, then you are not going to feel any pain in your future because of that intrusive coupling. So overall, we have 3 dimensions. We have integration, strength and distance for showing us the way, whether we're headed towards complexity or towards modularity. And we have that dimension that can help us to make pragmatic decisions based on the
volatility of our components. Now, if you combine the three of them together, basically if you're into domain driven design, then supplying subdomains, generics, when you're integrating generic subdomains, I say that it's OK to cut cores. That's something that usually is implemented, as Eric Evans says, with a rapid application development framework. Is it going to be super modular? Probably not. Why is it OK? Because those subdomains are not going to change frequently.
Core subdomains, on the other hand, that's where I should expect your changes and that's not a place to cut corners. That's a place where you want modularity. And if you follow the management design that aggregates basically take the idea of functional coupling to extreme, we have those transactional boundaries. So we are putting all those entities that share those transactional boundaries within the same aggregate bounded context are there to protect our
models. So we can use the same model of the business domain within bounded context, but not a cross bounded context. Cross bounded context, we need integration contracts in the language. These are open host service, published language or anti
corruption layer. Yeah. And if you analyze design patterns, architectural patterns, or cases where people are saying that one pattern is evil and should be considered harmful versus people saying that that pattern will save your life, well, consider those extreme opinions from the perspective of those 3 dimensions. Probably you're going to find the explanation for those conflicting opinions in one of
those dimensions. Wow, I think I feel like I am listening to like a insightful lecture, right? So I think what you just explained, right, with different scenarios, different kind of permutations, I think it kind of like opens up our eyes a bit, right? Like how do you analyze complexity? How do you analyze things that probably it's a bit difficult to change. Probably also think about dealing with legacy and you bring in your bread and butter
DDD concept as well, right? As you probably write through the last chapters of the book, right? I think all these kind of like now starting to get more sense, right? You bring up the topic of coupling, but also you bring up the topic of architecture design and also DDD itself, right? I hope people by listening to this episode are maybe better by reading your book, actually gets more tools to actually discuss about software design.
Because yeah, these three different dimensions are really, really critical if you want to talk about the complexity, modularity of the system, right? And whether your system actually can take the trade off, right? Because coupling, as you mentioned right in the book, you cannot really eliminate coupling because the software has to work together. There will be a level of coupling. Whether the high coupling is bad, again, it depends on the context, right? If it's not so volatile,
probably it's not so bad. And you bring in the topic of DDD. So maybe as we move on to maybe the later part of our conversation, I hope you still have the time, right? So in your last part of the book, after we understand about definition of coupling, we understand about 3 dimensions of coupling, how we assess our system design. The last is about balancing the
coupling. Now you understand you have all the tools and the knowledge required and your software design, you advise us to actually know how to balance this coupling when you make decisions in your software architecture. Probably a little bit more practical tips, right? Now that we know all this, how would you advise us software engineers to think about balancing coupling in our software design?
Yeah. So when you were making software design decisions, at whatever level of abstraction, think about those 3 dimensions. What is the knowledge that is being shared? What is the distance across which the knowledge is being shared? And also, of course, what is the volatility of that knowledge is going to be? How can you evaluate that volatility? Well, that's a place where you can use different models. I prefer domain driven design subdomains, but there are other methods.
Now, my model of balanced coupling, it's not going to give you a score like a number, like a grade that you can say, hey, I implemented a system with 99% of balanced coupling. Unfortunately not. Maybe somebody someday will join forces and implement it, but the devil is in the details.
It's not something that is really trivial, but at the same time I wanted to offer that model that you can keep in the back of your head, something that is easy to remember, something that doesn't require memorizing tons of different patterns. All you have to remember is that there are four types of knowledge, and that's basically,
and there are three dimensions. If you evaluate the knowledge and you compare it with a distance, you know whether you're headed towards modularity or complexity. If you're headed towards complexity, then you can look at the volatility and decide whether it's something that is worth your effort or maybe you should focus on something else. So overall, I would say keep these.
I hope that they are simple, these simple terms or ideas in the back of your head when you're making software design decisions and apply them. Again, it's not something that is going to be easy to incorporate in continuous integration pipeline, but it's something that is supposed to be easy to incorporate in your software design decision making
process. Yeah. So for different types of knowledge sharing, right, Just to recap a little bit, there's an intrusive coupling, there's a model coupling, functional coupling and so contract coupling, right. So these four very important. Maybe you should bring up these kind of terms when you discuss collaboratively about your software design and then not just about this knowledge sharing. Because again, like coupling, probably the most problematic one is about knowledge sharing, right?
The shared knowledge, the thing that we talked about in the beginning. So after you analyze all this, you also bring in the three different dimensions. So integration strength is basically those four things, right? And then you'll bring up the topic about distance and also about the volatility. So All in all, if you probably bring all these variables inside your discussion, maybe you'll make a better decisions, right?
And also don't forget as well, maybe in the beginning you design A better balance coupled system, but over the time as because business change, you know software change which you also covered in your book. There are so many reasons why software will change, right? And let's not forget, when something major change happen, you also bring up again this topic to actually make sure that your change can rebalance your software design in terms of coupling.
And also in the end, right, Eventually it doesn't create like a big ball of mud or the worst case is distributed big ball of mud, just like the crazy microservices that people are struggling with. So I think All in all, these are very good topics, right? For people who would like to understand further, because I'm sure maybe just by listening, this conversation is a little bit too short, right? Maybe you should check out Let's book.
I think it's coming out soon and hopefully people will be better to get a better tools, you know, like in terms of coming up with better software design. So let's wrap up. But before I let you go flat, I don't know whether you still remember last time I asked you this question. I asked you about these three technical leadership wisdom, I think 2 years apart, right? And a new book as well.
Maybe if you can share a little bit of what kind of wisdom that you can share with us, maybe with the theme of coupling. So yeah, is there anything that you want to share with us? Yes. So three tech leadership wisdom, that's our question. But we started by discussing the things that we kind of understand on the gut feeling level. But it really, really helps to get past that gut feeling level towards more explicit
definition. So if you stumble upon something that you cannot explain clearly, I strongly recommend getting into it, learning what's going on there because chances are you are not alone. Usually there will be more people struggling to define a concept. For me it was, for example, coupling and modularity. And once you are there, once you've found you were able to find such term and to define it, then you should probably share your wisdom with the world because people are going to be
grateful to you for doing that. So that was the first one. And the second one would be about modelling. So modelling is a very important part of what we're doing. And as software engineers, I don't think we spend enough time on training that muscle, that modelling muscle. We spend more time doing workshops on Kubernetes and Lambda functions, for example, things that are more technical.
Modelling is about our ability to understand the real world, those real world systems that we have implemented in code. So I would say bend time modelling, it's super important. It's super important to train that muscle to get better at it. And it also helps to analyse other models and models are everywhere. Even if you are looking at a model of toy car steel model. So think about analyse it from
that perspective. The model is not a copy of the real world, it's human made contract that is supposed to solve a problem. So ask yourself what is the problem that this specific model solves? Does it do a good job at it? If it's a toy car and then probably it's goal is to mimic that possession of some cool car, how cool is that etcetera with your software models. And then of course, apply that knowledge for evaluating what I called earlier models of models, integration contracts.
How effective are they of encapsulating knowledge? And that will help you to become a much better software designer. Again, at whatever level of abstraction you're working on, it doesn't matter. The underlying ideas are the same. And that brings me to the third tech leadership wisdom. And that would be about design. And design is another overloaded term that different people understand in different ways. We have graphical design, we have software design, we have product design, whatever.
But if you ask yourself about what's the purpose of design, it's usually to solve the problem. The design is of a solution. So again, getting better at the design, it's like getting better at modelling, but at a different level of abstraction. Evaluate designs. Let's say you're looking at the microphone I'm using right now. What about it's design? Is it good or bad? This mute button which is impossible to reach, is it good? Does it solve the problem or should I keep my app open on the
screen all the time? Probably that says something about the design. And once you're will get into that notion of design and again design of whatever from appliances to software underneath, usually there are the same principles that are driving that design being good or bad. And usually there will be some representation of distance and knowledge in software design. As I said, we have a integration strength and distance and balance and graphical design. For example, you have sizes of
components on the web page. The greater the distance, the bigger should be the size, right? The greater the the distance that your mouse travels. So yeah, these are the topics that are sort of philosophical, but underneath they will definitely make you a much better software architect, software designer, or just software engineer. That's what I call myself. Right, I really love this meta with them, right. So you kind of like will bring up a topic that is a quite high
level. I like the term model of model actually. So it explains about the contract really, really well, right? So you have a model and then you come up with another model to actually exchange the knowledge or information between maybe two components or two services, right? So flat, it's a pleasure to actually discuss with you about this topic. I think coupling is something that we all software engineers really, really need to
understand well. And I find that in many software engineers, when we talk about software design and this is less included in the design, simply because we talk about architecture patterns, probably technologies, right, or maybe called technologies, Kubernetes and things like that. But actually talking about coupling, modularity, complexity, knowledge, boundaries and things like that is very little in the discussion. So I hope people are more well equipped now with the book,
right? And for people who love to maybe ask you questions, maybe continue discussion or maybe just find out more about this topic. Is there a place where they can find such resources online? Yeah, so you can find me on Twitter. You can find me on LinkedIn. I assume the links are going to
be in the show notes. I have a blog, which looks like a very sad place right now because, yeah, I didn't have time to update it. And every month somebody, some nice person, emails me saying that a link on your blog is broken and I promise to fix it. But here we are. So yeah, I would say Twitter and LinkedIn, these are the places to get news. The Coupling book supposed to be published in September 24. At the moment it's available on O'Reilly online learning platform.
That draft is probably the same one that's going to be published, just improved with professional copy editing and professional illustrations. And yeah, also there are going to be a summary chapter, but that's about it. Right, So I really highly recommend the book, right? So I think you can also check it out on O'Reilly. I read it as part of this preparation, right? So I think it's pretty much robust and complete, I would say, in terms of content. So yeah, I think good luck with
your publication. So hopefully people can get to understand about this concept much better. So thanks again for your time flat. Thank you so much, Henry. It's been a pleasure.
