Recently I helped facilitate some discussion workshops on the topic of Clean Code. Each of the discussions seemed to be predicated on a belief that readability is the most important criterion by which to assess whether code is Clean. Indeed, the groups spent a lot of time discussing ways to establish and police coding standards and suchlike. While I agree that this can be useful, I felt the discussions missed the aspects of Clean Code that I consider to be the most important.
So I thought it might be useful here to attempt to describe what I mean by the term…
Back in the day I used to say this about test-driven development:
If ever I get a surprise, it means I have a missing test.
That is, if I’m in the GREEN or REFACTOR step of the TDD cycle and my changes make something else break, I need to add a test to document something that I must have missed previously.
I don’t think that now. These days I am much more likely to say something like:
If ever I get a surprise, it means I have accidentally discovered some connascence that I was previously unaware of. I need to eliminate it, weaken it, bring the connascent code closer together, or refactor my names so that it is clearly documented.
I’ve discovered some refactoring that needs to be done, and I wouldn’t necessarily rush to add tests.
In Fundamentals of Object-Oriented Design in UML Meilir Page-Jones offers the following guidelines for system maintainability:
- Minimise overall connascence
By breaking the system into encapsulated elements
- Minimise connascence crossing encapsulation boundaries
By maximising the connascence within encapsulation boundaries
This feels like something of an “algorithm” for refactoring, if only we could quantify what “maximise” and “minimise” mean. In the next few months I’m going to have a stab at doing just that, with the help of audiences around Europe as I roll out my new talk “Love and Death: Everything you always wanted to know about coupling but were afraid to ask”. I plan to report back here as the wisdom of crowds helps me flesh out what the above algorithm might mean. First up is XProLo next week, followed by NWRUG in July. Watch this space…!
This month James Jeffries and I ran a session at Agile Manchester in which we (ie. Jim) live-coded Dave Thomas‘s Back to the Checkout kata. The twist was that during TDD’s “refactor” step we used only connascence to decide what to change.
(I know I’ve done that before, on this blog. But this time Jim and I started with different tests. And we practiced a bit first. So the resulting refactoring steps are quite different than those I wrote about earlier.)
@ruby_gem kindly pointed her laptop at the screen and recorded the session. (The beauty of this setup is that you get to see what Jim types and hear how we explain it, but you don’t have to suffer from seeing either of us.)
The slides we used are on slideshare, and I’ve uploaded the resulting video to youtube for you to view at your leisure. Comments welcome, as always.
Recently I wrote a series of posts in which I attempted to drive a TDD episode purely from the point of view of connascence. But as I now read over the articles again, it strikes me that I made some automatic choices. I explicitly called out my design choices in some places, while elsewhere I silently used experience to choose the next step. So here, I want to take another look at the very first step I took.
Am I over-thinking things with this Checkout TDD example? Or is there a real problem here?
Based on insightful input from Pawel and Ross, it is clear to me (now) that there is CoA between the currentBalance() method and the special offer object(s), because the method doesn’t give those objects any opportunity to make final adjustments to the amount of discount they are prepared to offer.
However, as things stand there is no requirement demanding that. Does that still mean the connascence exists? Or is it a tree falling in the forest, with no tests around to hear it?
I had a very interesting discussion today with Ross about my recent connascence/TDD posts. Neither of us was happy about either of the solutions to the corruption problem with the special offer object. Even with the cloning approach, it still seems that both the Checkout and the MultiBuyDiscount have to collude in avoiding the issue; if either gets it wrong, the tests will probably fail.
After a few minutes, we realised that the root of the problem arises from the MultiBuyDiscount having state, and we began to cast around for alternatives. At some point it dawned on me that the origins of the problem go right back to the first article and the first couple of refactorings I did.