“increase cohesion, eliminate coupling” usually creates more smaller modules. Functionally related is normally just a paraphrase for "most changes are done by one team". So it is more on the line of does one thing in reality vs has one reason to change. Functionally modules create less overhead and since all is done with one team the missing decoupling is of no relevance.
The downside of replacing “functionally related” as the criterion with “increase cohesion, eliminate coupling” as the criterion for design choices is the coding overhead and that the required outcome has to be broken apart in more tasks which requires more pre-planning. The upside is usually easier testing, scalability of development, less time on-boarding of new developers and making it harder for cruft to emerge.
It has to do with the things going wrong in development. 😉
If you bundle code function related and you have one team doing the development part, then product architecture talks to one product owner who writes one story and dumping that into the team. The planning then hopefully is done within the team, but often enough one guy just starts writing stuff.
On the other hand, if you don’t do “functional grouping”, then the product architect, most of the time without the help of a software architect, has to split the function and talk to several POs and an epic with several stories must be created and as the stories are interdependent, they have to be a lot more detailed, estimated way before going into production to allow for the planning who has to do what and when.
Yes, that is not how it should be done, but that is the sad reality in basically every company I had insights in the past 10 years.
That's seems like a mighty complicated structure, mis-aligned incentives, & lots of handoffs. I don't see that as a situation software design can contribute to much, not until the organizational changes are settled.
If things are "functionally related" they are/ought to be "coupled". These feel like the same concepts to me. How are they different? If I'm in one area, I don't have to think about another area because it is not related, it is not coupled. I can operate without bumping into it.
“What goes together?”, “What gets separated?”, are the right questions... Because we can't always work on The Whole. We have to be able to separate where our changes our bounded to a context and won't ripple out beyond that.
“How big should elements be?" feels entirely irrelevant. It is as large as it is required to be. No larger.
I can have a Bond reference a Money. The two are functionally related but not likely coupled (except maybe a change to Money accidentally breaking Bond).
Hrmm.. then how is relatedness "interesting" at all? How does it have an impact? Money is coupled to M. Bond is coupled to M. Thats the interesting part that deserves attention. If something goes wrong there, we have a systemic problem.
From my experience with clinical decision support systems, I'd say the argument for 'functionally related' cohesion seems like friction between the evolving structure of the system and its internal or external constraints, like established domain concepts or business requirements. It's pretty safe for a Changer to pick up a concept from a Waiter, and draw the lines of cohesion along it. (Maintaining stability of 'functionality' by giving up some optionality.) It's understandable from a social perspective, since it's tough work for a Waiter and a Changer to see the same picture -- let alone evolve it. I'd say your definition of cohesion challenges Changers to contribute their findings of the structure back into the domain, and Waiters to be open to consider such findings. That's a good thing, but also something new and scary to most. Isn't it pretty rare for software to be a source of domain knowledge? Usually it's just a sink that 'implements' knowledge.
I don't think this is unique to software. Consider metaphysics. Intuitively, we have a concept 'bee' functionally related with whatever properties go along with it. Turns out it's also coupled to a separate concept of 'flower'. Both change together to a degree, and you would not have bees without flowers. Expressing this kind of cohesion isn't always easy given the system of names and labels we have. Signifying can get tough if you look for correctness. Where's the boundary? And yet, pattern recognition is naturally very simple for us. Maybe we should stress less about cutting systems into the perfect nouns. The way I see it, functionality is more like a constraint on the actual system than a way of structuring it.
Whether things are functionally related is most often less relevant than the way they might become unrelated. (Solution) architecture boundaries should be along the possible future fault lines; so the system can gracefully decompose.
Why don't you just do TDD and let it take care of all the design issues :)
Sorry about the tongue in cheek comment but I really find it cringey when proponents of TDD claim that it's not about testing but it's about design, and imply that your designs will automatically be awesome if you just do TDD.
As an actual comment, I think people are not accustomed to thinking in terms of coupling / cohesion and "functionally related" is another (maybe more natural) way of saying "tightly coupled".
1️⃣ What is the downside of replacing “functionally related” as the criterion with “increase cohesion, eliminate coupling” as the criterion for design choices?
"Increase cohesion & decrease coupling", unlike "group functionally related elements" doesn't mean we want a specific type of cohesion (functional). It's more like saying – "strive for the highest type of cohesion possible and use your judgment" vs. "functional cohesion please!". Different requirements (one is softer than the other) can lead to different designs.
Also, when we say "Increase cohesion & decrease coupling" it's not exactly clear what elements should we try to decouple (one can easily interpret it as "increase cohesion between elements and also try to keep them decoupled ¯\_(ツ)_/¯"). "Group functionally related elements" implies "keep functionally unrelated elements separate" which usually, but not always, means decoupling them. In either case, it would be better if we're more explicit about coupling/decoupling target(s).
2️⃣ What exactly does “functionally related” mean? Can someone give me examples of things that are functionally related, & things that might seem functionally related but aren’t?
The first thing that comes to mind is an enterprise app. Entities, value objects, repositories, domain services which belong to a vertical slice of functionality or a subdomain, are functionally related (or functionally cohesive, if cohesion is a measure of relatedness). In the same enterprise app: repositories, mappers, exceptions, etc. (that devs often put in a separate package) might seem functionally cohesive (because they perform a similar function) but are logically cohesive instead (or at least they're less functionally cohesive than elements within a vertical slice, which usually change together).
Functionally: in a way that relates to the purpose or use of something
Related: Belonging to the same group, type, family
So, for 2 things to be functionally related, they must be belonging to the same group, type, family and are in a way that relates to the purpose or use of something.
2 things that are functionally related would be, 2 police officers are functionally related. Their function is the same and they belong to the same group
2 things that appears to be functionally related, but are not would be, a mob soldier and police officer. Their function is to protect however, they are not related
What is interesting in these example is, you need prior knowledge of the subjects to be aware of the difference. Without a full description of what a mob soldier is, you could mistake them for a police officer.
In other words, experience is required to see that the first example is functionally related while the second is not.
This required experience is where the difficultly hides. The clearest example of "functionally related" will be a poor substitute for living and working with the concepts.
Can "functionally related" be replaced with "increase cohesion, eliminate coupling", I would argue, no. These two concepts are not the same.
I would say, "increase cohesion, eliminate coupling" an action you apply to "functionally related" concepts. It is not a synonym for "functionally related".
I wasn't asking if they are synonyms. I agree with you that they are clearly not syonymous. I was asking which goal is more effective for software designers, or rather what would be lost by replacing "group functionally related elements" with "increase cohesion & decrease coupling".
For me, this is largely about experience. To see what is "functionally related" requires experience & knowledge of a domain. This could be finance, travel, logistics, database design, driver design, hypervisors, etc. It is more about the domain.
On the other hand, increasing cohesion & decreasing coupling, is measurable. Cyclical vs unidirectional dependencies. Class/method size. Code branch complexity.
An experienced team will find both are important and cannot/would not ignore either. An inexperienced team would struggle with functionally related, but can excel in increasing cohesion & decreasing coupling.
A functionally related systems with low cohesion and high couple can make sense from a domain perspective but is hard to maintain and evolve. A high cohesion and low coupling system without functionally related components would be easy to change but difficult to understand its purpose.
When asking, which goal is more effective for software designers? I would say, increasing cohesion & decreasing coupling first and functionally related second because the former can be change by introducing experienced people. The latter is hard to change even with introducing experienced people.
"A high cohesion and low coupling system without functionally related components would be easy to change but difficult to understand its purpose." Until I see an example, I think this is a strawman.
How can I describe some functionalities in the most precise way? I should even describe the running cases, or, put another way, the test cases. That is, I can accurately describe the functionality through tests.
Let's say that I have a functional change and I have to change the code. If the implementation of the functionality in the code resembles its description outside the code (the tests we were talking about, for example), the modification will be done with optimized effort. One way to do this is to apply the test first approach to design.
How could I be wrong?
An example. The functional description says:
<<I should display the list of elements. of type X.
If I select one and choose the "business processing Y" option, then I want the result to be displayed as well and then persisted.>>
I can do a single test for processing-displaying-saving (can I?) and that lets me put them all in one function. Then, over time, I will learn that each part of processing-displaying-saving can vary independently. There are certain aspects that "strategically" will vary independently: I/O from the system (UI, persistence, network), business rules. I can add another "strategic" element: their orchestration.
How do the two types of separations reconcile? I can slice my code "horizontally" according to functionalities and "vertically" according to the aspects described above.
Another wrong example (we have tried :)) is to use slice both horizontally and vertically using strategic concerns: one package with all UI elements, another with all persistence elements. It's cohesion, right? No, because we forgot a whole dimension of variability, the functional one.
I think it's inherently difficult to define when things belong together and when apart, unless you already have a feel for each, and subjective definitions often make devs queasy.
I switched my own metaphor to modularity: always increase modularity, i.e. group bundles of functionality behind simple, clear interfaces.
The benefits of modularity in the large are obvious, and one of the many things TDD taught me was that you can implement modularity almost all the way down!
This sounds like Parnas' "information hiding" from "The Criteria..." paper. Information hiding has been a helpful way for me to think about design, but coupling is more helpful to me. I think they boil down to the same thing--if two elements are coupled with respect to a change, then the information necessary to make that change cannot be hidden one from the other.
The temptation (not our fault, just preferential attachment doing it's devious work) is to attach new elements to already-large elements like Thing & Customer. This tends to reduce cohesion (more random stuff that doesn't change in sync), which tends to increase the cost of coupling. I want a Cleaner Wrasse coming around moving those un-cohesive elements elsewhere, preferably close to the elements they are coupled with.
Does that help? It's hard talking in the abstract.
That's a tricky design problem to be sure. We had it also at Gusto--the shared objects like Employer & Employee were huge. Pulling out cohesive wrappers/facets helps--BillingCustomer, etc. but it also introduces the possibility of nasty coupling between the underlying object (like Customer) & its many wrappers.
“increase cohesion, eliminate coupling” usually creates more smaller modules. Functionally related is normally just a paraphrase for "most changes are done by one team". So it is more on the line of does one thing in reality vs has one reason to change. Functionally modules create less overhead and since all is done with one team the missing decoupling is of no relevance.
The downside of replacing “functionally related” as the criterion with “increase cohesion, eliminate coupling” as the criterion for design choices is the coding overhead and that the required outcome has to be broken apart in more tasks which requires more pre-planning. The upside is usually easier testing, scalability of development, less time on-boarding of new developers and making it harder for cruft to emerge.
Tell me more about "more pre-planning". I'm not getting it.
It has to do with the things going wrong in development. 😉
If you bundle code function related and you have one team doing the development part, then product architecture talks to one product owner who writes one story and dumping that into the team. The planning then hopefully is done within the team, but often enough one guy just starts writing stuff.
On the other hand, if you don’t do “functional grouping”, then the product architect, most of the time without the help of a software architect, has to split the function and talk to several POs and an epic with several stories must be created and as the stories are interdependent, they have to be a lot more detailed, estimated way before going into production to allow for the planning who has to do what and when.
Yes, that is not how it should be done, but that is the sad reality in basically every company I had insights in the past 10 years.
That's seems like a mighty complicated structure, mis-aligned incentives, & lots of handoffs. I don't see that as a situation software design can contribute to much, not until the organizational changes are settled.
I agree. As I said, things that go wrong in development...
If things are "functionally related" they are/ought to be "coupled". These feel like the same concepts to me. How are they different? If I'm in one area, I don't have to think about another area because it is not related, it is not coupled. I can operate without bumping into it.
“What goes together?”, “What gets separated?”, are the right questions... Because we can't always work on The Whole. We have to be able to separate where our changes our bounded to a context and won't ripple out beyond that.
“How big should elements be?" feels entirely irrelevant. It is as large as it is required to be. No larger.
I can have a Bond reference a Money. The two are functionally related but not likely coupled (except maybe a change to Money accidentally breaking Bond).
Hrmm.. then how is relatedness "interesting" at all? How does it have an impact? Money is coupled to M. Bond is coupled to M. Thats the interesting part that deserves attention. If something goes wrong there, we have a systemic problem.
From my experience with clinical decision support systems, I'd say the argument for 'functionally related' cohesion seems like friction between the evolving structure of the system and its internal or external constraints, like established domain concepts or business requirements. It's pretty safe for a Changer to pick up a concept from a Waiter, and draw the lines of cohesion along it. (Maintaining stability of 'functionality' by giving up some optionality.) It's understandable from a social perspective, since it's tough work for a Waiter and a Changer to see the same picture -- let alone evolve it. I'd say your definition of cohesion challenges Changers to contribute their findings of the structure back into the domain, and Waiters to be open to consider such findings. That's a good thing, but also something new and scary to most. Isn't it pretty rare for software to be a source of domain knowledge? Usually it's just a sink that 'implements' knowledge.
I don't think this is unique to software. Consider metaphysics. Intuitively, we have a concept 'bee' functionally related with whatever properties go along with it. Turns out it's also coupled to a separate concept of 'flower'. Both change together to a degree, and you would not have bees without flowers. Expressing this kind of cohesion isn't always easy given the system of names and labels we have. Signifying can get tough if you look for correctness. Where's the boundary? And yet, pattern recognition is naturally very simple for us. Maybe we should stress less about cutting systems into the perfect nouns. The way I see it, functionality is more like a constraint on the actual system than a way of structuring it.
That's why I restrict "perfect" to a verb, not an adjective.
Whether things are functionally related is most often less relevant than the way they might become unrelated. (Solution) architecture boundaries should be along the possible future fault lines; so the system can gracefully decompose.
Why don't you just do TDD and let it take care of all the design issues :)
Sorry about the tongue in cheek comment but I really find it cringey when proponents of TDD claim that it's not about testing but it's about design, and imply that your designs will automatically be awesome if you just do TDD.
As an actual comment, I think people are not accustomed to thinking in terms of coupling / cohesion and "functionally related" is another (maybe more natural) way of saying "tightly coupled".
1️⃣ What is the downside of replacing “functionally related” as the criterion with “increase cohesion, eliminate coupling” as the criterion for design choices?
"Increase cohesion & decrease coupling", unlike "group functionally related elements" doesn't mean we want a specific type of cohesion (functional). It's more like saying – "strive for the highest type of cohesion possible and use your judgment" vs. "functional cohesion please!". Different requirements (one is softer than the other) can lead to different designs.
Also, when we say "Increase cohesion & decrease coupling" it's not exactly clear what elements should we try to decouple (one can easily interpret it as "increase cohesion between elements and also try to keep them decoupled ¯\_(ツ)_/¯"). "Group functionally related elements" implies "keep functionally unrelated elements separate" which usually, but not always, means decoupling them. In either case, it would be better if we're more explicit about coupling/decoupling target(s).
2️⃣ What exactly does “functionally related” mean? Can someone give me examples of things that are functionally related, & things that might seem functionally related but aren’t?
The first thing that comes to mind is an enterprise app. Entities, value objects, repositories, domain services which belong to a vertical slice of functionality or a subdomain, are functionally related (or functionally cohesive, if cohesion is a measure of relatedness). In the same enterprise app: repositories, mappers, exceptions, etc. (that devs often put in a separate package) might seem functionally cohesive (because they perform a similar function) but are logically cohesive instead (or at least they're less functionally cohesive than elements within a vertical slice, which usually change together).
See you at DevTernity!
Functionally: in a way that relates to the purpose or use of something
Related: Belonging to the same group, type, family
So, for 2 things to be functionally related, they must be belonging to the same group, type, family and are in a way that relates to the purpose or use of something.
2 things that are functionally related would be, 2 police officers are functionally related. Their function is the same and they belong to the same group
2 things that appears to be functionally related, but are not would be, a mob soldier and police officer. Their function is to protect however, they are not related
What is interesting in these example is, you need prior knowledge of the subjects to be aware of the difference. Without a full description of what a mob soldier is, you could mistake them for a police officer.
In other words, experience is required to see that the first example is functionally related while the second is not.
This required experience is where the difficultly hides. The clearest example of "functionally related" will be a poor substitute for living and working with the concepts.
Can "functionally related" be replaced with "increase cohesion, eliminate coupling", I would argue, no. These two concepts are not the same.
I would say, "increase cohesion, eliminate coupling" an action you apply to "functionally related" concepts. It is not a synonym for "functionally related".
I wasn't asking if they are synonyms. I agree with you that they are clearly not syonymous. I was asking which goal is more effective for software designers, or rather what would be lost by replacing "group functionally related elements" with "increase cohesion & decrease coupling".
For me, this is largely about experience. To see what is "functionally related" requires experience & knowledge of a domain. This could be finance, travel, logistics, database design, driver design, hypervisors, etc. It is more about the domain.
On the other hand, increasing cohesion & decreasing coupling, is measurable. Cyclical vs unidirectional dependencies. Class/method size. Code branch complexity.
An experienced team will find both are important and cannot/would not ignore either. An inexperienced team would struggle with functionally related, but can excel in increasing cohesion & decreasing coupling.
A functionally related systems with low cohesion and high couple can make sense from a domain perspective but is hard to maintain and evolve. A high cohesion and low coupling system without functionally related components would be easy to change but difficult to understand its purpose.
When asking, which goal is more effective for software designers? I would say, increasing cohesion & decreasing coupling first and functionally related second because the former can be change by introducing experienced people. The latter is hard to change even with introducing experienced people.
"A high cohesion and low coupling system without functionally related components would be easy to change but difficult to understand its purpose." Until I see an example, I think this is a strawman.
How can I describe some functionalities in the most precise way? I should even describe the running cases, or, put another way, the test cases. That is, I can accurately describe the functionality through tests.
Let's say that I have a functional change and I have to change the code. If the implementation of the functionality in the code resembles its description outside the code (the tests we were talking about, for example), the modification will be done with optimized effort. One way to do this is to apply the test first approach to design.
How could I be wrong?
An example. The functional description says:
<<I should display the list of elements. of type X.
If I select one and choose the "business processing Y" option, then I want the result to be displayed as well and then persisted.>>
I can do a single test for processing-displaying-saving (can I?) and that lets me put them all in one function. Then, over time, I will learn that each part of processing-displaying-saving can vary independently. There are certain aspects that "strategically" will vary independently: I/O from the system (UI, persistence, network), business rules. I can add another "strategic" element: their orchestration.
How do the two types of separations reconcile? I can slice my code "horizontally" according to functionalities and "vertically" according to the aspects described above.
Another wrong example (we have tried :)) is to use slice both horizontally and vertically using strategic concerns: one package with all UI elements, another with all persistence elements. It's cohesion, right? No, because we forgot a whole dimension of variability, the functional one.
I think it's inherently difficult to define when things belong together and when apart, unless you already have a feel for each, and subjective definitions often make devs queasy.
I switched my own metaphor to modularity: always increase modularity, i.e. group bundles of functionality behind simple, clear interfaces.
The benefits of modularity in the large are obvious, and one of the many things TDD taught me was that you can implement modularity almost all the way down!
Actually, that gives me an idea of how to define "functionally related".
Design a simple, clear interface for some functionality. The code that implements it should be cohesive and is by definition functionally related.
The code that relies on the interface should be decoupled, i.e. rely _only_ on the interface, and is by definition not functionally related.
Makes the interrelationship and boundaries between the concepts more clear.
This sounds like Parnas' "information hiding" from "The Criteria..." paper. Information hiding has been a helpful way for me to think about design, but coupling is more helpful to me. I think they boil down to the same thing--if two elements are coupled with respect to a change, then the information necessary to make that change cannot be hidden one from the other.
The temptation (not our fault, just preferential attachment doing it's devious work) is to attach new elements to already-large elements like Thing & Customer. This tends to reduce cohesion (more random stuff that doesn't change in sync), which tends to increase the cost of coupling. I want a Cleaner Wrasse coming around moving those un-cohesive elements elsewhere, preferably close to the elements they are coupled with.
Does that help? It's hard talking in the abstract.
That's a tricky design problem to be sure. We had it also at Gusto--the shared objects like Employer & Employee were huge. Pulling out cohesive wrappers/facets helps--BillingCustomer, etc. but it also introduces the possibility of nasty coupling between the underlying object (like Customer) & its many wrappers.