Even sensible principles may lead to excessive coupling

It all starts with the book

While I was reading Sam Newman's "Building Microservices", I've found a very interesting paragraph - let me quote it here for easier reference (I've bolded what I want to put an emphasis on):

"My general rule of thumb: don't violate DRY within a microservice, but be relaxed about violating DRY across all services. The evils of too much coupling between services is far worse than the problems caused by code duplication."

Just to make sure we're all on the same page - DRY stands for Don't Repeat Yourself.

A step further ...

I totally agree with Sam's statement, I'd even generalize it a bit further:

Share any kind of compenents (artifacts / libraries / services) to reduce TCO & control technical debt, BUT only as long as it doesn't tighten the coupling.

It's not just shunning code duplication you should sacrifice to avoid excessive coupling, but also:

  • deification of architecture "cleanliness" - I despise this term, for me it's undefined, but there are still many people who prefer its "intuitive meaning" to some other, more precise but more verbose ones; one great example is herd instinct to normalize all the databases - many people do that just for the sake of doing it, not accepting the fact that in majority of cases it's absolutely impractical - all that just because they believe that makes data architecture "clean"

  • used component uniformity - for instance: using 1 single IoC container library everywhere VS using several different libs that are more fit to particular scenarios / preferred by various teams - yes, more libraries means more artifacts to track, more APIs to tame, more potential bugs / quirks to troubleshoot, but on the other hand:

    • it means more exploration & learning experience for developers
    • deciding on the tools to use on that level empowers development team
    • not every change is an improvement, but there's no improvement without change
    • creating an artificial dependency on artifacts ("you all have to use the same library, in the same version!") impairs development agility
  • forcing common tools / workflows - finding a common denominator to fit everyone's needs is rarely possible (even if it always seems to be "around the corner"); instead - let every team (if they want to) have their own deployment tools / artifact repository / etc. KISS > DRY ;) Just make sure that all of them are aimed to reach the same goals / serve the same (general) purpose.

... but not too far

It doesn't mean that in multi-team environment each team is on their own & should create / pick every element of their application & development architecture on their own. Co-operation (including adaptation of other teams' ideas) is much more than advised - what I mean is leaving a sensible margin of freedom without forcing the rules that are beneficial only in theory.

Obviously it all makes sense as long as:

  • licensing model doesn't get in the way (for instance, each library could require a separate fee not dependant on scope of usage)
  • learning / adaptation / integration effort is insignificant (if you decide to try equivalent solution on a differnt platform, with a differnt required skillset, there really should be some reasonable justification)
  • being shared is NOT (even partially) a sense of purpose for the component - it applies to things like communication contracts (message definitions, etc.)

photo credit: Bicycle Art - Downtown Portland via photopin (license)