June 4, 2008

...Learn TDD with Codemanship

Dynamic Languages, Substitutability & Unreused Abstractions

Just a quick note today about dependencies and change propagation.

I've talked before - in typically ill-considered and handwavy terms - about the need to unify principles of software and system design across the various levels of organisational granularity:

* Code statements in methods
* Methods in classes
* Classes in packages
* Packages in applications
* Applications in service-oriented and/or enterprise architectures

At each level, units of code are organised inside higher-level units of code. And as much as possible, we want the effect of changes to be localised inside the units of code in which they start.

There are, to my mind, three ways in which we can minimise the 'ripple effect' of change propagation. Since change propagates along dependencies, then we can:

1.Have fewer dependencies - if we write less code, then that'll mean less dependencies, too
2.Encapsulate dependencies - for the code we had to write, wherever possible we should see to package dependent items together inside the same higher-level units of code
3.Depend on things that are less likely to change - for the dependencies we can't encapsulate, we must seek to have them go towards units of code that are less likely to end up changing

The third strategy has been traditionally talked about in terms of depending on abstractions (e.g., 'program to interfaces'). But it is not abstractness that is necessarily the deciding factor here.

The real question should be: how easy would it be to replace X without anything that depends on X being effected? And the quality we're looking for here is not abstractness, it's substitutability. In dynamically typed languages like Ruby and SmallTalk, everything is substitutable, which may go some way to explaining why some programmers favour the freedom those languages give them.

In statically-typed languages, like C#, things often have to be declared abstract or virtual to allow us to subtype them (e.g., methods in C# can't be overridden unless they are declared such that they can be). The tendency of developers who follow the 'program to interfaces' mantra to declare interfaces for pretty much everything could be argued to be an attempt to make up for this shortfall in inherent substitutability.

Certainly, in my own experience, these interfaces-for-the-sake-of-interfaces clutter up projects and make code more complex and harder to maintain. I added a complementary OO design principle called Reused Abstractions to try to counter this - sometimes very damaging - tendency.

Surely we could find a way to provide the high level of substitutability we need without resorting to redundant abstractions cluttering up our code?

Posted 1 week, 2 days ago on June 4, 2008