the “anchor adapter”

Warning: academic theorizing and hypothesizing follow. Oh, and half-baked pontification.

I just finished refactoring reek to drive in a major new chunk of functionality (configuration files) which I’ll release soon, when I’ve had time for some thorough testing.

The refactoring needed to accommodate the change was huge, occupying much of my free time over the course of two months. Pretty much the whole of the tool’s original architecture has been revised. Why so big and so complex? Because the original code relied heavily on constants and class methods; they helped me get the early versions written quickly, but they represented a significant barrier to long-term flexibility. I’ve been wondering why that should be; why do constants and class methods stand in the way of adaptable test-driven code?

I think the answer lies in viewing the application through the lens of Hexagonal Architecture. Let me explain…

It seems to me that constants, global variables, classes, class methods, etc all live in a space that’s “anchored” to the runtime environment, which is itself a singleton. Anything anchored to that singleton is going to hinder the independence and isolatedness of unit tests, and also reduce the application’s flexibility in well-known ways. So far so standard. Now, suppose we model the singleton as a notional point that is external to the application. Hexagonal Architecture tells us we must access the singleton via an Adapter — in this case, an Adapter provided by the programming language and/or runtime. I’ll refer to the singleton as the application’s Anchor, and therefore claim that it is accessed through language features in an Anchor Adapter.

Now, I believe that the Domain Middle should not depend directly on Adapters. So any code that makes direct use of the Anchor Adapter must therefore be considered outside of the Domain Middle, and hence part of an Adapter — and hence also inherently outside the space where unit tests live comfortably.

Which is why constants and class methods add friction to unit testing.

Or rather: This model fits nicely with my penchant for Hexagonal Architecture, and lets me justify my unease at testing in and around class methods. And probably adds nothing to our understanding of software development.

6 comments so far

  1. Ashley Moran on

    I’ve gone through the “how would you write this if Ruby didn’t have inheritance?” thought process, and concluded that the answer is always, “better”. Now I don’t use (class) inheritance any more.

    So, as an idle question… how would you write code if Ruby didn’t have class methods?

    • Kevin Rutherford on

      Well, new would have to be redesigned, but that aside I don’t foresee any problems. Do you have a specific case in mind where class methods can’t be avoiced?

      • Ashley Moran on

        No, I can’t think of any off the top of my head. How about this as an experiment: from now on (well, on some project it might work on) I will create only one constant of my own, to a class with exactly one class method, which will provide an object from which everything else can be generated.

        Is that feasible, or is that just plain crazy?

      • Kevin Rutherford on

        I tend to think of the gloabl space as somewhere for the runtime to put things so I can get at them from anywhere; it’s certainly not somewhere I want to put any of my own code if I can possibly help it.

  2. Ashley Moran on

    Do you think that constants that reference value types (and class-method constructors for them) also add friction?

    • Kevin Rutherford on

      Not the constants themselves, perhaps. But anything that uses them will be more difficult to test.


Leave a reply