OBSEV:: Dangerous Coupling: A coupling that most people aren’t aware enough of !
Behavior coupling problem might seem obvious, and easily evitable. But not when, with a little observation, we notice that it is everywhere!
It has been a long time since the last time I committed posts here. I’ve been submitting quite frequently posts on the Architecture section on InfoQ community and sharing them here too. Actually I tend to like what I am doing with InfoQ. Often I think about an idea or a subject and then go to find arguments of different people about that subject. For me this is very richening and I hope that it has been informative to readers of both InfoQ and this Blog. However, sometimes I don’t succeed to find a loud argument about a subject that seems important to me. I open my blog writer to try to talk about it, mostly I fail because of lack of time. I’ve started working a couple of months ago on a very interesting project. And I can’t help but to keep thinking about lambdas and calculations.
Aside from all that personal stuff, I’ve decided this time to finish this post, to try to represent some ideas and my own opinion about an important topic that I find not sufficiently treated in communities.
When we talk about design in software development we talk about modularity, the fact of partitioning a problem into modules that interact to produce the expected result. Mostly this modules are partitioned through separation of concerns principles. Modules can be Objects, Functions, Namespaces, Modules or any other modeling tools that exit in the programing language of choice. Interaction of these components needs to be defined by combining and composing these components and designing their interactions in a certain way. Examples of interaction definitions tools are workflows ,orcherstration tools, and even Dependency Injection frameworks.
Interaction, composition and combining, all of that means coupling. And we know damn well that any coupling should be loose and well mastered. That is why actually we use interfaces, abstract classes, and a whole toolbox of patterns, to control the coupling that we have to face in our software design and architecture. One very important point one should not forget, is that these patterns do NOT remove coupling. Dependency still exists after using these patterns and tools, the difference is that coupling becomes more loose, in a way that components wont depend statically on particular implementations, but rather on a contract that should be satisfied by an implementation that is bound later on (at runtime or preprocessor time). Notice my use of “static dependency” in this context. We can imagine these patterns and tools’ role is to turn the static coupling between components’ implementation into a dynamic one (late binding, strategies, overloading, polymorphism…).
Dynamic (runtime) coupling is inevitable, at least when we want to do a modular code. I guess it is still ok to have runtime coupling until we fall into behavior coupling. Behavior coupling is one of the most annoying bug generators I cross everyday. Behavior coupling is a kind of runtime coupling. It is when two components are dependent on some state. And component B can’t succeed if component A doesn’t do its job on some shared state with B before. Taken into such a small context, behavior coupling might not seem that harmful, but the problem rises when several components are coupled behaviorally.
Behavior coupling problem might seem obvious, and easily evitable. But not when, with a little observation, we notice that it is everywhere!
An example of an unsafe behavior coupling, is stateful event-based GUIs. In general, they have a special lifecycle. Complexity goes wild when several components need to manipulate the view. And ask these components all manipulate a shared state, they become all behaviorally coupled. I’ve seen a lot of projects where producing the expected behavior on the interface becomes a nightmare.
This kind of problem is not exclusive to GUIs. Actually all stateful components make candidates of a runtime coupling generator. The problem with behavior coupling becomes serious when it shows as ad-hock mutation of a shared state. Meaning that there is no well defined diagram or component that described the interaction of the several components with that state. It is indeed possible to have some kind of tools that can trace this interaction to draw a diagram that represents the behavioral dependency. However I think it is a good practice to explicitly define dynamic dependency, coupling and interaction in some formal way. This can be workflows (through xmls or other technology) that describe explicitly in a visual (or at least visualizable) manner , the runtime interaction. Yet a workflow is just a technology, and one needs a lot of discipline and modeling efforts to master the state of the view, and so the behavior dependency between several components.
A better solution to this problem is not to have state, no mutation. When there is no mutation and no shared state, then there is no behavior coupling. The question that is raised right away is how can one do real world applications without mutation and without state. Isn’t programming without state contradicts object oriented programming? Isn’t the world stateful? Databases, GUIs, how can we ever interact with that yet not having mutable state?
First of all, OOP is about having a state, but not absolutely a mutable state. Immutable object brings isolation to the logic of components which has its good side effects on modularity (removes behavior coupling) and performance (especially when talk about concurrence and multi-threading).
But what about GUI frameworks, and databases?
Databases, GUIs, File System these are just some kinds of IO sources that somehow indispensable for a useful enterprise application. By the end most of the value of an enterprise application comes from its interaction with the outside world. And the outside world does have a mutable state. But this doesn’t mean that enterprise software should have mutable state internally. One can push out the mutation and keep it in the outside world. This keeps the software safe and uncoupled behaviorally. That is merely what functional programming solution to IO is about. Keep your software clean from mutable state.
Keeping mutable state out turns your software into a function, a side effect free function; which given the same input produces the same return value (evaluates to the same value). The input when in the case of a database are data from the database ( and maybe inputs from the UI) and the output is a list of commands, different kinds of commands: Some UI command that when executed by the UI (outside world) will mutate the state of the interface. And other database commands (also outside world) that when executed by the database driver will mutate its state. However, in this case the software given the same input will produce the same list of commands. This keeps your software out of mutation and side effects. Which means out of behavioral dependency and coupling, and so safe with much less bugs.
This technique is based on Monads, something Haskell programmers are quite familiar and comfortable working with. In Haskell by the way, you do not have a choice but to isolate IO inside a monad (IO monad), that is one of the reasons why Haskell is viewed to be the only pure functional programming language.
I am not a functional programming geek. I have a very heavy imperative programming culture. And that is exactly why I write this post. I am a C# programmer. And I felt to share what Haskell taught me about good practices of programming.
No Comments »
No comments yet.
RSS feed for comments on this post. TrackBack URI




