Crafting Code Podcast
$ cd episodes/009-coupling-and-cohesion
~/podcast/episodes/009-coupling-and-cohesion $ ls -1a ~/podcast/episodes/009-coupling-and-cohesion $ cat episode-summary.txtCoupling and cohesion are fundamental concepts in software systems. From types of coupling to Conway's Law, Matt and Allan (sans Dave) discuss how these two ideas can affect your day to day work beyond just the formal definitions.
~/podcast/episodes/009-coupling-and-cohesion $ cat themes.txt- Coupling and cohesion
- Conway's law and team structure
- Domain-driven design
- Hexagonal architecture and adapters
$ cat transcript.txt
[00:00:00] Allan Stewart: Welcome to Episode 9 of the Crafting Code Podcast, where we discuss the importance of doing the right thing at the right time with the right tools. I'm Allan Stewart, a software architect, and lately I've been thinking about using anti-patterns as a way to help people understand what can go wrong in my software system.
[00:00:17] Matt Baker: Hey, my name is Matt Baker, software architect. Lately I've been thinking about where the software industry is headed.
[00:00:23] Allan Stewart: Today we are sans our usual co-host Dave Adsit, but we're going to solder on anyway and talk about coupling and cohesion. So coupling and cohesion is an interesting topic that gets brought up a lot in software projects. I first learned about it in a very academic way. And the academic definitions of these start with coupling is the degree of interdependence between software modules. So we're measuring how closely connected two routines or modules or functions are. And cohesion is the degree to which the elements inside a module belong together. And those are great definitions that oftentimes don't tell us anything about what it actually means to us. So it's like telling me all the elements that make up my car. What do I do with that? Matt, what do you think? What does coupling and cohesion actually mean to the average software developer in their day-to-day work?
[00:01:24] Matt Baker: That's a great question. You know, for me, coupling, the way this shows up for me day-to-day, oftentimes it's when things fail, when something goes wrong in my system, how many things go down with it? So at runtime, the service goes down that maybe I'm hosting a service on my team. How many other parts of my system does it take down with it? How many dependent services are going down? And there's also a design time consideration here. You know, when I change my, or let's say I'm dependent on a vendor's API and I need to upgrade the version of their API that I'm using to maybe fix a security hole or something. I'm pretty tightly coupled to that API. And let's say that the upgrade path, or in order to make the upgrade, I also have to change the way I've implemented against their interface. That could be a design time coupling. That'll impact me. And that happens quite a bit. So for me, it's when things fail or when things are changed, or let's stick with when things fail, how many things are impacted or how many things go down? That would be coupling. Cohesion is a bit more of a slippery one. It's one of those that I feel like I understand inside my brain, but when I try and explain it outside, it's clear I don't. But generally speaking, cohesion for me is how many places do I have to go in order to make a change. So depending on how you decompose the system or if you're working in, you know, whatever, a single code base, how many files do I have to open up in order to implement a new feature or maybe change the way I'm doing, like, some state persistence? And the more places I have to go in order to change something, generally the less cohesion I feel, right? And the flip side of that is if I want to add a feature to a particular piece of a product and I only have to change code in one area, that to me would, you know, indicate there's a high sense of cohesion in this feature.
[00:03:16] Allan Stewart: Yeah, I agree with that. Another thought that comes to me for cohesion is, could you take something away from this thing? If taking something away is difficult, so for example a class, if you take out a private member variable or a function, if that class can't operate correctly, then there's a good chance that that had high cohesion. They belonged together. But if you can easily take something out, And it's like, oh, this doesn't belong here. It shouldn't be in the same file or the same class or module or whatever. I've thought about it in the past to use just a really simple example. Sesame Street. I grew up watching some Sesame Street, and they used to sing the song that one of these things is not like the other. To me, that's kind of the essence of cohesion. This thing doesn't, it's not the same. Don't put things that aren't the same together.
[00:04:06] Matt Baker: Yeah, I really like that. On a very micro level, like, I, I'm thinking of a class, and I, I like the litmus test you just gave, or, you know, you take a variable out, if one of the functions breaks, that's a degree of cohesion, but if seven break, that's a greater degree of cohesion. That's a cool model. I like that. Yeah, and there's a
[00:04:25] Allan Stewart: bunch of different axes by which things can be alike or different, you know. So, like, with physical objects we would think about them in terms of, like, different classifications, right? Are these all the same color or are all these the same size or shape? There's similar things in software that we can look to and see, like, do these, are they the same? Are they similar? Do they go together? Or if they, sometimes they're dissimilar, but they obviously go together. Coupling can be easy to tell a lot of times in code. I reference something and now I'm dependent on it. There, there you go, that's coupling. But I kind of often imagine that kind of like those molecular models students will get to learn, like, chemistry or something like that, or if you're old enough to remember tinker toys, that you've got these different elements that you can connect together with, like, rods, essentially. When you take these two things and you connect them together with a rod, now they're coupled, and you might have different lengths of rods, and maybe some are flexible or springy or something else, but there's these different ways you can connect things together, and that's the, that's the degree of your coupling. It affects software projects in a similar way, because when you connect up these things, depending on how you connect them up, can really change the structure. So if you connect them up in, like, triangle and, like, pyramid kinds of shapes, they become very rigid and they don't move, which can be great if you're trying to support a structure, right? Like a physical structure. If you lay them all out in, like, a chain, then it might still be very bendable. You might be able to bend and twist and flex. And I feel like software needs to have some of those same kinds of principles. How you connect the pieces matters. Some parts should not be able to move around a lot. And it's okay to have perhaps a tighter coupling between things that are supposed to be together. But other things, you want to be able to have some flexibility. You want to be able to bend. And if you just keep connecting rods between all of the pieces, then eventually, and especially if it's haphazard and it's just like, oh, well, I can connect these two pieces, so I will. Then pretty soon, you've got like this interconnected ball of mud, right? It becomes the ball of mud that we often refer to in the software industry. And then you're stuck and have a hard time moving your software progress from there.
[00:06:54] Matt Baker: Yeah, the Tinker Toys example is interesting to me, and I could imagine, like you said, a haphazardly connected blob that definitely maybe has a high degree of cohesion. Everything's in one place. I don't know. But the coupling could be interesting. What's another example of coupling and cohesion that sticks out to you, Allan?
[00:07:15] Allan Stewart: So another one that I've thought about is Swiss Army Knife. With a Swiss army knife, there's a degree of coupling and cohesion that you don't get on, like, a basic knife. So, like, if you just have a pocket knife that only has one blade on it, that's really high cohesion. This belongs together. You've got some blade on it and some handle so that you can hold it and manipulate it. Right. And it's all connected. And that's, that's great. High cohesion, and low coupling, but it only does one thing. It's a knife. And so you get these other gadgets like a Swiss army knife, and now you've got a higher degree of coupling. There's multiple blades or tools that are all interconnected. And usually they open up on one side or maybe on two sides, but depending on how they're attached to each other, one tool might not be usable unless unless you close the other tool. They're coupled together. The cohesion goes down. When there was just one single blade and that was all you had, then that's really high cohesion, sort of like the identity function of cohesion, right? It's the one thing. If you've got several different kinds of blades, then that might still be really good cohesion, right? So, like, you might have, like, a straight blade and you've got a serrated blade and you've got, like, a sawtooth blade, blade, all for kind of very similar types of usage. You can go in and cut materials, right? And depending on the kind of material, you choose a different blade, and that's perfect because you don't need two blades at the same time. But the cohesion drops quite a bit as you add all these different other types of tools. Online, you see, like, all kinds of weird, funny things, memes online about, like, a tongue depressor or a magnifying glass or a flamethrower or something attached to your Swiss army knife. And these things are not similar. Like the corkscrew. A corkscrew is one that I've often seen, and it's there so that people can quickly open up bottles, but does it really belong with the other ones?
[00:09:22] Matt Baker: Yeah. Like how often are you using a corkscrew and a tongue depressor, like, in the same hour?
[00:09:28] Allan Stewart: Right. And your saw blade. Yeah. So the context for a Swiss army knife is very different, right? So, like, even if we throw out some of, like, the silly ones where, you know, you open it up and there's a penguin attached to it or something ridiculous, the context for a Swiss army knife is, like, you're going to go camping and you've got one tool that fits in your pocket and it can do a bunch of different things. Maybe not very well Well, each individually, it's not like the best tool, but it's a nice, well-rounded tool, because you can't carry your entire shop with you, but you just have the one thing. Or maybe you like to carry a pocket knife just around the house with you. And so you can, instead of going and finding the thing that you want, you can just pull out your pocket knife and choose the appropriate tool and use it. That's great in the context of Swiss Army Knife. But in the context of writing code, you almost certainly do not want to try to fulfill a whole bunch of different purposes in a really mediocre way. I don't know. There may be some cases where it could make sense, but most of the time, that high degree of coupling, where you have all these different kinds of things, and they're all connected, and they force interactions with each other, is something that you don't want. It limits your ability to change the software. It limits the usefulness of the software product that you're trying to build. And that's one of those places where cohesion and coupling get to you. You're trying to build something. And for the sake of understanding, for the sake of usability, refactoring, etc., you want to have things have a much narrower focus and be intentional about how you couple the different components of your software project together.
[00:11:12] Matt Baker: Yeah, it's interesting thinking about a knife or, like, a Swiss army knife. I could see, and I'm sure I've done this myself. You pick up a multi-tool or a knife and you open it up and see what's inside there. And it's kind of like a Cracker Jack box, almost all the things you're going to find. And I'm sure I've said something along the lines of, oh, that's pretty cool. You know, like, just not for any purpose other than, like, oh, this thing has a knife, a sanding board and clippers. Neat, you know. And I feel like in a similar way, sometimes when we talk about code, or maybe more specifically when we're writing code, you might hear someone say, "wouldn't it be cool if," right? And, and, uh, like, usually it's a bad idea that follows that, like, "but wouldn't it be cool if I combine these two things," and, like, uh, I could see someone potentially saying, "wouldn't it be cool if I had a knife that just had all this stuff in it?" Like, the thing that I'm trying to point out is there's no, like, real specific goal in that statement other than, like, it'd be cool, right? It'd be neat, like, if I could do this. And maybe there's even a knife maker out there that thought it would be neat to show off how well they could combine a bunch of things together for no other reason than to show that they could do it. And I know that I've done that myself in code. I know other people that do that in code, right, where it's like, "wouldn't it be cool if," and they throw out coupling, cohesion, and really the goal now is, like, how can I glue these things together in a cool way that maybe, like, reduces the code count or things like that, but, like, all the while introducing producing maybe some pretty nasty coupling. And staying with this knife thing, this multi-tool theme, I was working out in my yard the other day, taking off some chicken wire fence. And it had been nailed into a tree, and then the tree kind of grew around the chicken wire. And most of the items were, no, they were all nails. And so I'd taken three of them out by hand. I was just able to pull the chicken wire, and the nail came out with it. But the fourth didn't. And I had a multi-tool on me. I opened it up. I flipped open the knife and in just a complete idiot moment, I took the blade and I put it behind the nail and tried to pry it out with it and the blade snapped immediately. Not the right tool for the job. But then all of a sudden, I had a multi-tool without a knife. And as time went on, I kept the multi-tool because there's so many other useful tools in it. But every time I need a knife, I have to go get something else. And what the point I'm driving towards here is that sometimes, like, the way you should be combining things or the cohesion that you're working in emerges. Like, you, you learn about it over time, so in this case when I broke my knife, and then I, you know, went forward using the tool, every time I needed a knife and didn't have it in that tool, that was something telling me that there's a sense of cohesion here. Like, whatever you're doing, like, you probably want a knife in here. Not, it may be disregarding all this stuff in that tool for a second, like, you want a knife, and you don't have a knife. So, like, and now I find myself with a multi-tool and then a knife, and, and, and either hand, and these things are definitely coupled together, right? And, uh, both of these properties, more so cohesion, like, in anecdotally, cohesion emerges for me more than coupling, but I think both of these properties emerge, and what I mean by that is it's almost a fool's errand to try and and predict these things up front. Sometimes you have to, like, if you're joining large disparate systems and maybe a substantial distributed system, you should take some time and really make sure you're doing things right. If it's a change, it's going to be really hard to unwind. But oftentimes I think you can benefit from, like, the changeability of software and just try something and see what it tells you. So in my case with my knife breaking and then always needing a knife or needing a knife a lot of the times when I have that tool, that's an emergent property for me saying, you want a blade in here. And I think in code, we get a really similar thing. Like, if you're always reaching for, you know, I'm struggling to find an example right now, but if you're always using one piece of code or if you're always reaching for, let's say, a library when you're using one piece of code in your system or an additional library, maybe, like, there's something telling you that that code that you're using in the library you always reach for, they go together. I don't know how, but they go go together. So the reason I think the emergent property is interesting and worth talking about is it allows you to exploit the changeability of software. So instead of trying to predict all that stuff up front, like, how should I couple this system together? Does this class have, like, the right degree of cohesion? Those are, like, thought tracks you can get on that, if you're not careful, you can lose a lot of time. And often you can short circuit that stuff by just putting something out, building it, doing it kind of maybe in the easiest way possible. Don't take a lot of time to worry about the coupling more than what you feel is responsible. And then just let it tell you over time. Like, if you notice that every time, you know, a product manager maybe asked for a feature change on something and you're changing two or three different places every single time, that's a property telling you that these things are related in some way. And similar with coupling, if you find that when one part of your system goes down, three or four disparate parts also go down every single time in a kind of a nonsensical way. Like, if your login goes down and then, like, your ability to make an espresso also goes down, like, that's a weird coupling. An emergent property has said, hey, these things are coupled. And then it's up to you, you know, as the observer to decide whether or not you like that.
[00:16:25] Allan Stewart: It reminds me that we often talk about the desirable state to have high cohesion and low coupling. If coupling is kind of an emergent property, there's some times where it's just obvious, right? It's like, these things just do not go together. Don't stick them together just because you can, right? Don't start off saying, "well, the entire monolith is going to to be in one file." That's going to make you sad real quick. But I guess in a way that's emergent too, right? Like, after a few minutes, you realize, "I do not want to keep scrolling up and down in this file." That's the emergent property of cohesion telling you, "nope, this all doesn't belong together." But what it's going to say is that kind of the inverse then I think is true with coupling. Sometimes coupling does emerge. You just find it. It's like almost growing in your code base and coupling things together that ought not to be, if you're not careful. And so you've got to watch out for that, because you have to have some sort of coupling or else you can't get anything done. Zero coupling means that these things are not connected in any way. They don't do anything together. And that's not very useful.
[00:17:36] Matt Baker: form. Yeah, I think all your code would just exist in these little silos, not doing anything with one another. And I think this is a good point to call out. Either of these properties taken to the extreme kind of result in a lack of usefulness. So really what you're playing is a balancing game here. You know that, I think what we've observed, at least what I've learned in my career, that these things matter. Sometimes they're abstract, sometimes they're squishy, is she, but, you know, they, they do matter. You don't want to optimize for them at the expense of everything else for sure, but just be sure you're asking yourself things like, "Hey, when I couple these two things together, does that make sense? Is this the same coupling?" Allan, you've mentioned a few times like this. It's almost like entropy or entropic coupling, let's say, where, where you're just, as your system's building, you just connect things together maybe without a little, you know, a lot of forethought and intention and why not? That's why you do it. But at some point you might become aware that, "Whoa, oh, like these things are too coupled. In order to release this small 20 line module, I have to also release 4 million lines of code in these other libraries." If that's a problem for you, and if that's taking a release cycle from minutes to days or weeks, like, coupling is a property that you should be paying attention to, right? Because if you reduce the coupling in that case, potentially ship a lot faster.
[00:18:54] Allan Stewart: Yeah. It's a hard thing with software sometimes. You just kind of have to say, "it depends." All the time. I can't think of a single thing in software where you can just say, "always do this, take it to 11, die on this sword, because it's always true and there's never any exceptions," but rather it's all, like, these optimization functions. How can I find a nice balancing point between all of these disparate things that are happening? And balance is important because it affects your ability to deliver software. Coupling and cohesion are two Two very raw, very basic principles that can make or break a software delivery cycle. High coupling can make it so that your release process is much longer, much more complicated. There's a lot more to test. Low cohesion can mean that, you know, a simple bug, you have to go into 30 files to fix the same thing, manifest in slightly different ways each time, because the things that were supposed to be together weren't together.
[00:19:56] Matt Baker: I have a little, like, consulting quip thing I keep in my pocket where I say, "things that change together stay together." So when people ask me about cohesion, I usually like to pull that out and say, you know, like, you can really get lost on this topic. But for me, it's things that change together stay together. And oftentimes, that's something again that emerges, you just have to be working in a system for a while and start noticing what things are changing together. And And I'm not proposing, like, you keep a journal, like, "when I changed this, A, B, and C had to be changed." Like, not at all. Like, it's more of an intuitive thing I'm saying. You're going to get a feel over time. Once you have an instinct that tells you, "oh, if I change that file, I have to change that file," that's what I'm talking about. That's when something has emerged and you're learning, you know, things that change together stay together. And all of this kind of is underscored by this, I want to talk about scope for a minute, or, like, a degree of impact. Like you were saying, Allan, Allan, if you maximize for these things in isolation, you're not going to get anywhere. And anyone listening is probably picking up on Allan's experience because he has a lot of it because he's able to tease apart here that you don't want to maximize for these things at the expense of anything else. It's always a balancing act. It's always a big, "it depends." There's always nuance. There's going to be times where you want to do, like, an obscene amount of coupling and there's going to be time where you don't want to do any of it. There's going to be time where you abandon all cohesion. There's going to be times where you maximize for it, and it's a big, "it depends." So don't get stuck in, like, the, I try to remind myself anyway, don't get stuck in, like, the good, bad, best, worst with this topic, because it's, it's a very nuanced one, and, like, Allan was saying again, I think it's a tributary experience, that it always depends. Everyone laughs at us when we say that, but it's because it's true. Um, anyway, I want to talk about scope, so impact when you're talking about coupling, cohesion, if you're in a small little code base, if you're writing the next little, like, Twitter bot or some little thing, coupling, cohesion don't really matter. They will to a degree, sure, but that degree compared to a big distributed system is less. And so when I'm going and writing code, if I'm in a newer system or if I'm, like, in a new file, a new module I'm working on, the first thing I'm not thinking about is coupling, cohesion. But as that system grows, your choices of coupling, cohesion are going, the results of your choices, rather, are going to represent themselves. And I believe it's your job to pay attention to those things and start responding to them. I think the great example for me here is monoliths to microservices. Like, that transition is a lot of things. And one of the things that is, is the decoupling exercise, right? You're saying, like, "I can't have this monolith as one thing because there's lots of reasons not to have a monolith." When I'm going to pick is when a piece of the monolith goes down, the whole monolith goes down at runtime, right? So if a piece of your website goes down, the whole thing's gone. That's great for your customers, so you might, that's coupling, right? And, um, that might not matter when you only have two pages, like, if you have a login page and then a profile page for something. Maybe it's okay that it all goes down, but when you have 50 pages and, and, you know, 50 times as many customers becomes, it becomes less okay. So there is a concept of scope here and of impact, uh, and the, the thing I think that's important, or the reason I think it's important to highlight that is, don't get lost trying to maximize coupling and cohesion in these small code bases or looking for, like, "oh, that's incorrectly coupled," or if that's perfectly coupled, when it doesn't matter. Because when they're really small, it really doesn't matter. But as your code starts to grow, the choices you made, the results will kind of start to present themselves. Listen to those and respond to them. And then often it really helps to frame what you're experiencing in terms of coupling and cohesion.
[00:23:36] Allan Stewart: Yeah, I like that. And it also reminds reminds me that you can have really good coupling and cohesion in a monolith. It might be a big code base. It's like a single code base, but how you divide up things inside that monolith can be done really well. In the same way, you can't just assume that if you go to a microservices architecture, let's pull apart the monolith and make everything separate. That doesn't automatically improve your coupling and cohesion. Instead of having a big ball of mud, you have now a whole, it's like a mobile.
[00:24:12] Matt Baker: I've heard it, spaghetti with meatballs.
[00:24:14] Allan Stewart: Spaghetti with meatballs, yeah. Where it's just, okay, now it's just everywhere. And there's lots of little balls of mud in orbit around each other, in a really complicated mess. So looking at those things that you were just talking about, right? What kinds of effects are you seeing? How is it impacting your system? I like that a lot. Up. One of the things I find interesting, I don't hear a lot about different types of cohesion, but you do hear about types of coupling. So a lot of the normal or general use coupling is when it's like, okay, this thing depends on that, right? So class A references class B, or it depends on it because it takes an instance of class B or it news one up or something like that. And now you've got coupling. And the degree of coupling depends on how much they have to know about each other. So if you're newing up a class B, you have to know about all the constructor parameters that it takes. But if you dependency inject class B, then you don't care about how it gets made. I just need one of those. And that's kind of the typical way that we often talk about coupling. But there are a number of different kinds of coupling depending on where you look. More different types than I feel like I could really explain at any given point. If you stop me on the street and ask me about, like, a random type of coupling, I might not know, just because people have identified so many of them, but a couple that I think are interesting. One is temporal coupling. Temporal coupling is literally about time. Like, how are these things connected through time? One of the examples that is often cited for temporal coupling is in your code, if you knew up a class, can you immediately use all the functions on that class? Or is there a prescribed order of things? And so that's easy to understand at the class level, but systems start to be that way too, right? Like, can you operate this function before the user account is created? Or does it require a user account first? Are there discrete steps in a process that have to be followed in a particular order? Those are temporal couplings, or anything, really anything else that involves time. If you have to make API calls and you have to wait, right? It takes time for something to happen. There's usually other things going on in your system. And so you're waiting for this temporal coupling to resolve itself. And you have to be aware that that is one of the ways you you are connected to something else, right? It wasn't that straight rod that was easy to understand. This is like, you know, connected through like a magnetic force that is almost invisible and it only activates in certain times.
[00:27:10] Matt Baker: The example, the service level example you're talking about is where temporal coupling really crystallized for me. And it was in an architecture that was an event-driven architecture that also had some API calls as a means of integration. And the model I saw that helped me understand it was when one service makes an HTTP call to another, that's a pretty high degree of temporal coupling. And that's true because in order for service A to work, it needs to be able to call service B. So they have to exist and be available in the same temporal space. And so if B is down, A is down, at least for the function that I'm talking about where A means B. But you can really reduce that temporal coupling by introducing an event. It depends wildly on your use case here, but if you can, let's do an example. So if your service wants, if I have a service that's responsible for signing up users, and part of that is charging their credit card, that's probably going to mean I connect the user creation service to the credit card service over HTTP. TTP. So they're going to have a high degree of temporal coupling, right? I'm not, when the user tries to say, "sign me up, sell me an account," I'm going to charge their card right then. And if I can't charge their card, I'm not going to give them an account. Now the reverse of that would maybe a different situation. Let's say when a user is watching a video, I just want to log it. So when, when there's a service that says, "okay, user's watching video," and I want to also drop a log onto a disc somewhere. That could be an event driven thing, right? I could just publish an event that says, "hey, this user watched this video." And at some time from that point forward into the future, I don't care when, it's something else is gonna pick that up and write a log to disk that says that user watched the video. So that's a low degree of temporal coupling, right? Another way of saying that is, I don't need my logging service running in order to watch a video. When the logging, if it's down, when the logging service comes back online, it'll process its queue of messages, it'll process its events, maybe off of, like, a log like Kafka, and we're all caught up, right? And you can kind of see how that stands in contrast to where one service needs another in a temporal frame. And that model, that's where it really clicked for me, this idea of temporal coupling and the ways to think about it or watch for it.
[00:29:25] Allan Stewart: Yeah, thinking about those things that are synchronous and asynchronous can help us understand where temporal coupling is appearing in the system. And I think it's worth noting that almost anything can be done either way. You can take almost anything that's synchronous and turn it asynchronous, but there's, there's a cost involved, and there's, like, difference of expectations, right? So, like, in your example, sometimes, yeah, you won't create the account unless you can charge it, but maybe it's okay to create the account, and you put, like, a charge pending on their account, and, you know, they can't do anything. There's an additional cost there. Now you have to have a different layer of access control. It's like Like, yes, you can be logged in, but you can't do anything because we're waiting for this payment to happen. But it also means that, you know, your flaky payment processor doesn't hurt you as badly if it's not up.
[00:30:20] Matt Baker: What an interesting, like, way for coupling and cohesion to impact the user experience, right? Like, sometimes this stuff might fly under their user experience radar, but the example you gave was really good. Like, if I'm a user and I sign up and you don't charge my card and you limit my ability to use the site until my card is charged, that's a particular user experience. Or, like, what if you come back and eventually say, "sorry, I had to revoke your account. Your card was declined." That's arguably a poor user experience. So it's interesting that we're seeing in this conversation impact to the user from the way we've connected our systems together.
[00:30:59] Allan Stewart: Another form of coupling that I've encountered that is one I didn't really think about for a long time is data coupling, if you're sharing data or using the same data, that's a form of coupling which may not, like, it may appear in your code, because, you know, you're sharing the same class and this class is holding the data that you care about, but other times not, especially if you add something like a HTTP call or, or a message system, and now all of a sudden everybody cares about the same data, but it's getting serialized and deserialized, and so you're putting it back together, and the shape of that data, like the structure of how it's nested or even how it's stored, can make a huge difference. So there's a project that I've worked in where the shape of the data persists all the way through, and it's caused a lot of problems because it persists all the way from the user interface through the business logic and into the persistence store. And so if you make any kind of change to that system, like you want to add something in the database, then it pops up and appears in the user interface. Or if you want to change some business logic, then now you have to rewrite the database and the user interface because all these things are coupled together. They had to change together. And it wasn't that they're all sharing the same code, right? The front end is, you know, some JavaScript front end and the database is, you know, your favorite flavor of SQL or whatever. And in the middle is your favorite backend code. And maybe they're even all three different teams. And yet they were coupled together based on the structure of this data. What I found to be more and more important in software systems is that you should protect the structure that you care about, that helps you behave in the way that you want to behave. And when you interact with other parts of the same system, or when you're interacting with separate systems or in a distributed system, there's kind of this translation layer. In Domain-Driven Design circles, this is called the bounded context. And at the edges of your bounded context, you want to have these anti-corruption layers, which basically do this translation. I don't speak the same language as you. You give me some data and I'm going to reshape it. I'm going to throw away the parts that I don't care about. I'm going to attach it to other parts of my own data that I do care about to make it easy for me to use. And then when I pass data on to something else, I'm not going to expose everything I know. But only the things that are important for somebody else, lest they get highly coupled to my data. And now I can't change the structure.
[00:33:37] Matt Baker: Agreed. This point really resonates with me right now due to some conversations I've been having lately. I found that on this point, I diverge with what I'll call, let's say, like, data professionals, at least the ones that I've been having some conversations with. When you couple data together, other, I'm thinking about, like, data from different domains. So when you take data like a user in one context and try, or one domain and try and make it work in another domain, like, let's say, specifically you want to use the same user model throughout your entire system. Let's say, uh, there's, there's a coupling happening happening there, right? And even if it's just a conceptual one, or in this case, let's say it's a schema, that model is, everything is beholden to that particular And so when you change that model, a lot of stuff has to change. And what often happens in practice with data coupling, I found, is that the model, the more places it's used, the more it's diluted. The more it's diluted, the less effective it becomes. So over time you kind of get, like, a Your pure user object will start to grow some warts, right? All of a sudden it's going to have a little field that's only relevant to one of the 10 10 people that depend on it, one of the 10 systems. And then that problem will just repeat, repeat, repeat. And eventually you get to a place where you have this one massive object that doesn't make any sense anymore. And you need a whole nother layer of definition in order to even work with the thing. So this idea of, like, data coupling, this one's part, it just sticks out to me right now. It's kind of a powerful one and one to avoid. And one that for one reason or or another, there seems to be a divide. I'm not gonna say all software engineers think one way and all data professionals think another, and I'm not even gonna call those distinct roles in every case, but what I've found is that if you've erred more on the side of building software systems, you tend to take on this idea that I think, Allan, you and I are in favor of, which is, don't try and use the user model everywhere. Like, create models for their pockets where they make sense. And then I found with data professionals, they're more like, "I need everything to adhere to this one model." And it makes sense, you know, depending on how you're using the data. If you're a data professional, you might be consuming a lot and trying to tell stories about a whole system. And it sucks when you have to go translate a bunch of times. But there's a reason you have to do that. And it's because on the software side, we really get into a lot of trouble when we try to use the same data models everywhere. Again, going back to our earlier comment, doesn't matter if it's a simple little system. Really will start to matter if you're talking, you know, hundreds of thousands or millions of lines of code across 10, 20 teams, your data coupling can really bite you if you're not careful.
[00:36:13] Allan Stewart: So with all these different types of coupling, and we only touched on a couple, is there value in measuring coupling? Coupling was the one that is inherently easier to measure because cohesion kind of requires like, almost a judgment call, right? I don't know. Maybe there's some, like, great machine learning algorithm that can help us detect cohesion, but coupling is pretty easy. Does this reference that? Yes. Okay, great. They're coupled. Even some of the temporal coupling can be detected, whether something is initialized properly, or if you're making an HTTP call, then we can say, yep, there's temporal coupling right there. I know that there are some static code analysis tools that will help you to see the coupling in a system. How useful is that?
[00:36:58] Matt Baker: Yeah. I'll lead with, like, my own personal, purely anecdotal position that I don't know how well I could defend. I don't like these static tools, static analysis tools. I think that they can often, like, send you down bad paths where, you know, if you measure a system that has high coupling, you'll immediately go try and reduce it. Maybe you didn't need to. It might not matter. So generally speaking, should you always reach for a static analysis tool to measure your coupling? I don't think so. But I think it can can be used as a signal. And in that sense, I really appreciate these, especially as one system becomes 20 or just a big system starts to grow. You can just watch it and notice that, hey, our coupling really went up this month or it seems to be steadily increasing or over the last five months, it's tripled more in the last three years. It was pretty even. Those are all indicators that you should have, potentially have a conversation about something. In that sense, I think they're really valuable. Like all things, this can be taken too far if you have someone who's kind of the coupling cop or the static analysis cop that's running code and coming around and saying, "change this," you know, purely because they want to reduce the score or a number. Less useful, I don't buy into that as much, but as a means of just a way of getting signals out of a system, I think they're valuable. I'd agree with
[00:38:17] Allan Stewart: that assessment. One of the areas I think is really interesting is code visualizations, and oftentimes Sometimes coupling can show up there. So you see some interesting code diagrams and it can help you understand the way that your system is connected. Or you can have discussions about, like, a Netflix Death Star microservices kind of thing. But in the day to day, I'm not sure how often that that has really helped me very much. For me personally, I've usually gone to different tools at different times. And more often than not, it's a whiteboard where I'm just describing something, because it's almost like you're taking these cross sections with these kinds of tools and measuring coupling is a cross section of your system. And it might be really interesting to look at, and you might learn some really powerful things, but you have to take it in context. If you turned things in a different way, you get a different cross section and so you have to be really careful how you interpret those results.
[00:39:21] Matt Baker: I've never heard that term cross-section, and I'm going to start saying that all the time now. What a great way to put it, you know, you take a slice of it, and it tells a particular story given the way you cut it, and, you know, the term coming to my mind right now is vanity metric. Like, I definitely seem some coupling measurement results yielded effectively in a boardroom, no doubt. You know, on a PowerPoint point slide, they make a point, and if you need funding, like, if you want to get a project funded, or you're, you're an architect trying to get someone to pay attention to the risk in your system, you know, um, they can make a point, similar to the way security people, like, have their security risk scores, and they're like, "it went up by 20 units this month," and, like, no one stops to be like, "well, what the hell is a unit? Like, what does that even mean?" You know, they're just, they're making a point that, like, there's been an increase in something bad, and, uh, in those ways, you know, it could be useful.
[00:40:13] Allan Stewart: Yeah. Measuring coupling can be useful, but you have to be careful with it. And I think where you use it really does apply at these different scales, like you were talking about. What are you trying to accomplish by it? Right. Are you in a board meeting, or are you in a small little code base where it doesn't really matter very much, because the maximum degree of coupling and cohesion, you know, can't escape the small ball of, you know, your five file toy problem. And so it makes me think about how coupling and cohesion affects architectures. As we move up the stack from talking about individual lines of code, individual classes, now we're talking about deployables and distributed systems and things, then coupling and cohesion start mattering a lot. We talked about monolith a little bit before and microservices. Those are kind of like the in vogue architectures that everybody talks about. And the coupling and cohesion can matter a lot. And, like we said before, you can get it wrong either way. Choosing microservices isn't a panacea, neither is a monolith. You can also start applying some of these other areas of how we think about systems, like Domain-Driven Design. In Domain-Driven Design, you've got this concept of the bounded context, and the bounded context becomes like a container of cohesion. All the stuff that belongs together go together in this thing. Everything that's related to X is going to be part of this bounded context. And we have this ubiquitous language that means that we're talking about the same thing here in this space. And then the coupling comes, you've got some internal coupling of how that microservices put together, or how this bounded context is put together, and then how does it connect with other things that represents at a system level your coupling. If you're in more of like a monolith style system, then, or even within like a single bounded context, if you're in DDD, or a single microservice, then inside there you can look at the coupling and cohesion as a way of how you architect that piece. And there I really like like the idea of hexagonal or sometimes called, like, onion architectures, where you're putting your business logic in the middle and kind of this, like, protected space in the middle, where you're saying all the logic that really matters for my business, of, like, what are we actually trying to accomplish is in one place and it's cohesive. And these other things that are incidental to your business, like having a database, nobody pays you to have a database, but they don't want you to forget their data unless they send you a GDPR request. Then they want you to forget all the data that you've ever had. But they don't pay you to have a database or not. They pay you to accomplish some kind of service, right? That's your business logic. And so you don't want to couple those things too tightly together so that the database is directly tied up with delivering that service.
[00:43:22] Matt Baker: One point there, Allan, Allan, you used a word that was super telling for me. You said you put your business logic in the middle and you protect it. What an interesting word to use when we're talking about coupling, right? Because I'm fairly certain what you meant there is you protect it from being connected to things that are incidental, like writing to a database, or you protect it from infrastructure. You know, and so when we talk about coupling, all of a sudden we're talking about defense. Like, you want to defend certain parts of your code from being polluted from other parts of your code where you can. And it definitely doesn't make sense to interleave a bunch of infrastructure code in your business logic. And just as an aside, this hexagonal architecture, the onion architecture, right? I really hope this kind of architecture gets back into the limelight a little bit. I feel like it's maybe falling away a little bit, but it's such a powerful architecture.
[00:44:11] Allan Stewart: One thing that I think is often overlooked is that whenever anything affects your software architecture, it also affects your people. And vice versa. And we often refer to this as Conway's law, which basically says that any system that you build will inevitably mirror the communication structures of the people who build it. So if you have a company that has particular org chart, essentially, you can oftentimes look at the software and see the org chart. But, you know, if you know a few things about, like, okay, which teams own which parts of a system. In that way, I think coupling can also be observed and measured across the people. How is your organization structured? Do you have individual silos of work, right? So, like, "oh, we've got the dev team and the product team and the testing team and the security team and the operations team." If you have all of these individual teams and their silo, then you'll see that your overall software system will also have these similar kinds of couplings. The way that you communicate between team members can also be reflected inside of your software and these lines of communication. That's one of the areas I think is really interesting as far as, like, system failures go. It's not uncommon to hear about people adopting something something like Agile. They had a consultant come in and teach them all about Scrum. So they try to start doing Scrum, but they don't really change the organization. They just try to change the behavior of the people involved in software, or sometimes just only, like, one narrow band. It's like, "well, we're not going to change your product team and your testing team. We're only going to change the developers, and they're going to be Agile, and nobody else is going to be Agile." And then we wonder why it fails.
[00:46:05] Matt Baker: Some of us, some of us wonder why it fails.
[00:46:07] Allan Stewart: else. We wonder how it could have ever succeeded. And the same thing happens with this concept between the coupling between your people and your architectures. So a business says, you know, "we've heard that monoliths are bad and microservices are good. And so we're going to go and change our software architecture," but they don't change the way that the people work within the system, and the system fails because they are out of alignment fighting with each other. Right, so the org structure is trying to create one type of architecture, and then the desired state architecture is trying to change the org structure. Yeah, I'm kind of clapping and crying
[00:46:49] Matt Baker: over here all at once. Um, both ways, you know, from an org trying to change its structure being ignorant of the code that needs to change, and then people changing code structure being ignorant of the people that will be impacted. Both of those have been razors that have cut me really deep, you know. So I'm glad we're talking about this point. Um, what I want to say is that people that write code all day have a, they develop an intuition for coupling and cohesion. They understand this really well, a lot more than people that don't write code, I think, because they're just steeped in all the time, they're dealing with the trade-offs, the cons and effects of coupling and cohesion. That type of modeling that works in code can also work in designing people orgs, right? You can look at the people org and kind of think, okay, these opponents, these components have to couple together this way. And that means that when I change that group, I also have to change this group, or, like, I can't, like, put that group under a new leadership structure without also taking this one, or I have to solve for how to decouple those. Like, these are all things that if I start changing the terms, like people and orgs to, like, code and libraries, that engineers are dealing with all the time. So one thing that I would hope happens in our industry is more and more people that are designing orgs that haven't written code. I hope that they don't make those changes in isolation. I hope that someone designing an org doesn't just say, "all right, change all this stuff, all these teams, go." And just not be thinking about the code that's underneath that's going to have to change. All, like, the new communication channels that they just set up that are fresh, that are going to take some time to, like, either person on the end of those comm channels is going to have to get used to the other people. And so what I would say is, if you're a person designing an org and you're not a coder, maybe go get a coder. And just spend some time talking with them about, "how should I think about this in terms of coupling and cohesion?" And you can talk about things that change together, stay together. That's true for code and that's true for people, too. Go get the teams that you're going to impact, specifically engineers. Talk about coupling and cohesion and make an informed decision where you account for both the people in the system. On, you know, to Allan's point that coupling and cohesion affect both the code architecture and the people architecture and the reverse cuts back the other way. You know, if you think you're going to design a brand new system or implement a brand new architecture, and you're not talking about how your teams talk to one another, how you move products through, like, ideation to delivery. If you're not considering that when you're designing a new architecture for a company, especially if it's a bigger one, you're doing the same thing. You're designing with one eye closed. And when you try to implement one of the casualties, it's going to be a lot of the people's way of working around you. You're changing the way that they talk, the way they interact, the way they deliver. And those are substantial. And if you don't consider both of these things when changing org structures, coupling and cohesion will step up and get you.
[00:49:33] Allan Stewart: them. And I think it's an interesting way to look at organizations along these different axes. Once again, coupling is the one that is easy to measure, right? What does the org chart look like? Who do people report to? Who are they talking to all the time? But then cohesion is an interesting one because sometimes in an organization we say, "oh, well, we're going to do cohesion by organizing everybody by the kind of work that they do." Say, "oh, ta-da, we've got high cohesion by putting all all the UX people into the UX group and all the product managers and the product manager group and all the testers into the tester group." And that definitely has high cohesion, but the cohesion matters depending on what you want to accomplish in your system, same as it does in code, right? So you talked about earlier, how many places do I have to go in order to make a change? And a lot of times in software, we don't need 10 UX people all working on the same team on the the same thing at the same time. But there are probably 10 or 20 or 30 features that could all use UX support. And so if you, instead of, you know, instead of grouping people by the type of work they do, if you group them by the value that you're trying to achieve out of your system, then that can turn the system on its head, sort of refactoring your people structure into a format that allows you to get value done. So if the change that you want to make is deliver a release of a software package or a feature, then you want to have cohesion with all the people who can do that effectively.
[00:51:08] Matt Baker: Yeah, it's interesting. I liked your idea of if you put all the UX together, if you put all the DBAs together, security together, and their little silos, you have high cohesion. But as soon as you try and run a delivery delivery through that model, you're going to find that, okay, first I have to go to UX. Okay. Now they're done. I've got to go to security. Okay. Now they're done. I've got to go to the database. Now someone screwed up in UX, got to go back to UX, and you got to go back through it again. And so, yes, you have high cohesion, and there's a degree of coupling there, but, but you're seeing that, like, it kind of sucks. I have to go to all these stops along the way to deliver something where if your cohesion, or, like, the axis of which you were, by which you were measuring your cohesion and change to, you know, how many, let's put everyone together that's necessary to get this feature out the door. Same degree of cohesion. Well, I don't know, but same concept of cohesion, but, you know, you're interpreting it differently, going back to what you were saying, your cross-section, right? That's just a different way to look at it. And hey, you might be in a domain where you want those things separate and you want all the throws over the fence from UX to database to security, or you might not be. You might be in a domain where it's more valuable to to put everyone that's necessary to ship something, you know, into prod on one team. And, you know, that's a degree of cohesion and the way it goes.
[00:52:28] Allan Stewart: I think it depends on your domain, but then also, what are you trying to achieve at any given time? Right. So, like, maybe you do want a really cross-functional team to deliver software, but for training, you're going to bore the UX people to tears talking, you know, about dependency injection. And so maybe you should get all your software people together, to do training, and your UX people can train with each other and talk with each other about the design system and how do you make a unified look for your branding or for your distributed system or whatever it is. So the purpose for what you're trying to accomplish at any given time also affects the cohesion that is in play.
[00:53:11] Matt Baker: As we're winding down this conversation, I have a few thoughts that I'd like to share on coupling and cohesion in no particular order here. My career has taught me that you should couple slowly and with intention. Oftentimes, I'll avoid coupling and instead duplicate. And what that might look like, practically speaking, I'll write the same piece of code in two places. Instead of, when I notice that I need a piece of code that I've written somewhere else and where I'm currently working, I could couple those together. Or I could just duplicate the code. As always, it depends, but oftentimes I've found that I'll duplicate first, and that's because I appreciate coupling slow, because there's always going to be effects and impact from coupling. Sometimes you're not ready to deal with those, or sometimes you don't want to deal with those, so don't deal with them until you have to. So I avoid decoupling, and I'll duplicate. I have a rule of thumb, usually three times. I'll duplicate something three times, and after the fourth time I'll say, "wait a minute, there's something that's happening here. I should pay attention to it." Along the lines of duplicating code, oftentimes I hear people, when I give that line, people will say, "well, it's not DRY." And I've come to resent this. The DRY thing, DRY is an acronym that stands for don't repeat yourself. And it's similar to the S, the single responsibility principle in the SOLID acronym. And my response to that has now become, sometimes something is so dry that it chafes. DRY in and of itself is not something to just optimize for all the time. And so it's okay to duplicate a little bit. For me at least, I've decided it's okay to duplicate if it means I can avoid coupling, or even kick coupling down the road a little bit, you know, to make the choice of how, of how I'm going to couple things when I have as much information as I can, if I can delay that. Oftentimes they will. So couple with intention, slowly, and don't be afraid of duplicating on your road to eventually coupling. Last thing I would say, don't get stuck trying to find the perfect decoupling or the most appropriate cohesion. Don't get stuck thinking, "oh, my architecture is not right, these things shouldn't be coupled, uh, I'm going to change them," when the only reason you're saying that is because you read the latest microservices book and it said you were doing it wrong wrong. Don't get in that trap. You can really get into this mode of, like, what's right and what's wrong, ignorant of the context, and try and achieve this, like, perfect technical polish in this case with either coupling or caesium. Don't do that. My advice is just pick what makes sense in the moment based on what you know. Avoid making choices, like, coupling choices that are hard to undo if you don't have to make them. Just take your time. Maybe most important for me lately, when I've been and design discussions with people is, especially when you get to kind of like a choice that could go either way, I say, "just pick one." Just pick one and go pay attention to the effects. Like, I don't think that we as an industry spend enough time really appreciating how quickly software can change and how you can use that to make a decision and then revert that decision later. So don't get stuck. There's no perfect decoupling or there's no perfect cohesion. Make a choice and see how it goes. Like we've been talking about, if you decouple a system a certain way, and then you find out that every time you need to change one component, you have to change all of them, consider putting those things back together. You know, similarly, if you find that a runtime when part A of a 20-part system goes down and it takes 15 other parts with it, think about, like, disparate parts, think about whether or not that makes sense. And if it doesn't, decouple those things, you know, but you can do that after spending some time living with the results of your actions instead of trying to get it right the first time or trying to, like, lead with the perfect thing. So, and I'm saying all this because these are all things that I've struggled with. You know, I've struggled with coupling too early because I thought it was the right thing to do or because it was the perfect design. I've struggled with spending a lot of time, like, analyzing cohesion to see if, like, it's right without realizing that right is such, like, a contextual and subjective word. And then for me, just being okay with getting it wrong and fixing it when I find out the way I got it wrong. That That was a hard one battle for me personally. And so those are maybe the points of guidance I would give as we wrap up here.
[00:57:28] Allan Stewart: High cohesion, low coupling. It's a good guideline. The definitions sometimes are very academic, but the implications of them really impact us in our day to day. As always, we recommend that you, our listeners, join up with a community of professionals by attending a Software Crafters group or meet up near you, or any other community group where you can discuss things like coupling and cohesion. Here in Utah, the Utah SC group at utahsc.org meets the first Wednesday of each month in Draper. Maybe we will talk about coupling and cohesion with you there.
Copyright © 2026 - Crafting Code Podcast