A critique: Clean Architecture and TDD
I learnt a lot from globally-renowned authority figures in software engineering. But I also found both theoretical and practical holes. Let's probe deeper into the holes.
I learned a lot from Uncle Bob.
I like Clean Code. (It was one of my earliest interests when I started working as a developer).
I like Clean Architecture. (Well, to be honest, Clean Architecture isn’t new, it’s a more well-popularized synthesis of previous approaches - Hexagonal Architecture combined with Jacobson’s Use Case Driven approach, but I’ll save that for another time).
I write a lot about TDD and Clean Architecture. But at the same time, I’ve encountered many problems, holes, and contradictions… inconsistencies in “interpretations“ and inconsistencies regarding the “best“ way.
Beyond the rose-colored glasses
I will use the word “critique“ in the remainder of this article.
I just want to make sure we’re clear about the definition of “critique“ (Oxford Languages):
noun: a detailed analysis and assessment of something, especially a literary, philosophical, or political theory.
verb: evaluate (a theory or practice) in a detailed and analytical way
Thus, a critique intends to analyze and assess a theory in a detailed and analytical way.
Why a critique? Well, given any concept, theory, or practice:
A superficial understanding leads us to extremes - “yes, I agree, it’s the best“ or “no, I disagree, it’s the worst“
A deeper understanding comes through probing the weaknesses and limitations, both theoretically and practically
A critique of Clean Architecture
After spending years in ORM-driven development, it was a real mental “refresher” to discover Clean Architecture. My learning journey was the following:
I couldn’t find any sample project by Uncle Bob which illustrated Clean Architecture. Later, when I did find an example (via Clean Coders), I found inconsistencies between the theory and the practical example shown.
I compared several high-starred GitHub templates and samples of Clean Architecture. Result: many inconsistencies between the interpretations themselves and inconsistencies with the theory
I searched for conflicting interpretations on Stack overflow discussions and came upon further divergence
Furthermore, at a conceptual level, the following questions started running through my mind:
Is Clean Architecture “the“ optimal way? Under what circumstances can Clean Architecture lead to suboptimal solutions?
What are the pros and cons of the different approaches to implementing Clean Architecture? Which of these implementation “variants“ is optimal?
To what extent is it true that the database is just a detail? Can things go wrong if we consider the database as just I/O or as BL too? Should the database be included or excluded in “unit“ tests?
Do use cases in Clean Architecture correspond to the original concept of use cases in UML? What happens to use cases when we shift from monoliths to microservices?
Note: Due to all these challenges, I proceeded in learning Clean Architecture PRACTICALLY through prototyping - I made a small Sandbox project, implemented one interpretation of Clean Architecture, saw the stumbling blocks, then another interpretation, then another (intertwined with DDD too)… I remember I went through over 5+ prototyping iterations, combined with back-and-forth research before I applied it to a “real“ project. Even when applying it on a real project, I started applying it on a small isolated module before applying it on a wider scale. Why? Because every time I see new problems, in this way, we can minimize risk. Start with a sandbox while learning, then apply to a real project later.
A critique of TDD
TDD also wasn’t without challenges. One of the biggest problems I saw was that even though the TDD cycle is “simple“ there was no guidance regarding writing good tests, which is orthogonal to TDD.
Writing robust tests is the #1 determining factor in the long-term maintenance costs associated with our test suite. If we fail to achieve this, then it doesn’t matter whether or tests were done with TDD or Test Last; the test suite will keep breaking; we’ll just be getting false alarms and will have to spend so much effort maintaining the test suite! (with lower confidence).
I didn’t find any proper examples of TDD beyond the trivial calculation implementation, fizz buzz, and Fibonacci sequence, and no guidelines for applying it to real projects
I experienced issues with over-mocking to the extent that the test was a replica of the source code; through the test, I pretty much already implemented everything, so the source code was just a copy-paste of the test
I experienced the problems that TDD critics often emphasize, the feeling that unit testing is a waste of time and that the tests could “freeze“ architecture because they keep on breaking
I saw the possible contraction - on hand, TDD was supposed to encourage refactoring. But on the other hand, if I constantly have to fix breaking tests while refactoring, doesn’t this contradict the TDD cycle?
Over time, I shifted my perspectives:
Most tutorials classify unit tests as white-box tests. But I moved away, more towards black-box tests, contract-like API-like tests. Why?
In my earlier years, I viewed tests as helping me achieve SOLID design (as was the mainstream thought). Indeed, my earlier tests were tests of the UML design rather than functionality. I moved more towards a functional, behavioral perspective. Why?
I went through both schools of thought in TDD. But I no longer view the distinction between the two schools as meaningful; I’ve moved on to a more comprehensive multi-dimensional approach. Why?
Last, we can’t forget the TDD debate between Uncle Bob and Jim Coplien. Is there a disagreement, or is there agreement - at different levels? What if they were saying the same thing? How do we reconcile Test Driven and Contract Driven approaches, and where does Architecture fit in?
Note: As you’re reading the above, you’ve probably seen the word “test” and not talking much about the TDD cycle. The reason is that most of the problems I’ve observed were problems of structurally-coupled tests (rather than behaviorally coupled tests) - this can occur regardless of whether or not we’re adopting TDD. So I see that as a problem that needs to be solved first.
Want to read my critique?
In the past, most of my posts synthesized ideas from globally-renowned authority figures, particularly Uncle Bob, Kent Beck, Martin Fowler, Alistair Cockburn, Eric Evans, and Vaughn Vernon. I will continue to write similar content as part of the FREE edition of the Optivem Journal.
But, starting tomorrow, I’m starting a new additional content category, “Thought Streaming“ as part of the INSIDER edition, where I’ll share my perspectives (so it will be highly opinionated!). My current rough upfront plan includes the following:
A critique of Clean Architecture & the problems I see in it
A comparison of different variants of Clean Architecture on GitHub
Guidelines for my “upgraded“ version of Clean Architecture
A critique of different approaches regarding TDD & Unit Testing
My current perspective regarding TDD & Architecture
… And more!
The above content is my current set of “upfront“ ideas. I write iteratively, so I may adapt content/sequence based on feedback I receive through comments, considering what you want to hear more about.
So what’s the key difference:
In the FREE edition, you get access to synthesis from authority figures
In the INSIDER edition, you get all that AND you get my perspectives - a critique of Clean Architecture and TDD
INSIDER Edition: Who is this for?
You’ve already read nearly everything from Uncle Bob, Kent Beck, Martin Fowler et al., and you’ve also adopted their ideas in practice, and you’ve seen challenges and problems
You’re not looking for yet another “how to“ (you’ve already seen multiple “how-tos“ on YouTube and GitHub), but instead, you want to zoom deeper into concepts, deeper into tradeoffs and gray areas, deeper into un-answerable questions
You have a mathematical mindset, you see criticism positively, and you don’t want just the regurgitation of one mainstream perspective, but you’re looking for alternative ways
You enjoy reading because then you can control the pace of reading (and thinking), which you can’t get with videos
INSIDER Edition: Who is this NOT for?
If you haven’t yet read anything, then this is not for you; I do recommend starting with books, starting with the theory, learning the concepts and definitions, watching YouTube tutorials on Clean Architecture
This is not for people who are looking for a step-by-step how-to guide; if that’s what you’re looking for, then you can search for YouTube videos regarding how to implement Clean Architecture, also Uncle Bob’s Clean Coders videos, search for Clean Code on GitHub
If you’re looking for a pure black-and-white guide (very clean “for“ or “against“), then this isn’t for you either, because here I’ll open up more questions than answers
If you don’t like reading books, then this isn’t for you either
Let’s start our journey
Do you want to learn more about Clean Architecture, TDD & Microservices in practice?
What happens after you subscribe? You will receive emails with the INSIDER edition posts starting tomorrow - Thursday, 25th August. I will publish them generally once weekly (generally on Thursdays). If you have any additional questions, feel free to let me know.
With a 100% satisfaction guarantee
I used to have a 100% bug-free guarantee when I worked as a contractor.
Now in writing, I have a 100% satisfaction guarantee too. If, for whatever reason, you’re not satisfied with the Optivem Journal INSIDER edition, then please contact me within 30 days for a full refund.
Critique of Clean Architecture and TDD: Overview
Critique #1 - Distributed Use Cases
In this article, we showcase the problem that Use Cases in Clean Architecture (and the promise of being able to unit test the entire Use Case behavior) becomes problematic when we shift from monolithic to microservice architecture. In future articles, we will examine how to test use cases in distributed systems (because Clean Architecture does not answer that question!)
Critique #2 - "The database is a detail"
In this article, we showcase an important concept in Clean Architecture, which is that business logic should only reside in Use Cases and Entities, not in the database, so that we can fully unit test the business logic; that the DB is just a detail which is not part of the architecture, that the DB should be swappable; we summarize the benefits of such an approach. In the next article, we will analyze that this approach is not globally optimal.
Critique #3 - The Database is NOT a detail
Uncle Bob makes the proposition that business logic should only be in Use Cases and Entities, and that the DB is just an I/O mechanism. Universally applying this leads to suboptimal system quality.
Critique #4 - Is Unit Testing Harmful?
Unit Tests can be done "wrong" - resulting in high maintenance cost, risky and expensive refactoring, and even harmful for architecture and design. Unfortunately, this is very common for many teams!
Critique #5 - Unit Testing Class Design?
You might often hear the phrase “TDD helps me with design“. For many people, this means improving the design of their classes, driving their UML class design through TDD. But there's a BIG problem!
Critique #6 - Combinatorial Explosion in Unit Testing
How do we handle combinatorial explosion? When there are many classes, many dependencies, many pathways? How do we decide the right entry points for our tests?
Hi!, how could I become anInsider subscriptor?