Refactoring with Tangrams

Remember tangrams? Remember how they would mess with your intuition, that the silhouettes they drew seemed to lie?

I’m working on a large refactor that is composed of two similarly-sized and similarly-structured systems. Let’s imagine these systems as two similar tangram tiles.

You can arrange these in a variety of ways, some more aesthetically appealing or easily described than others.

And so it was that I found myself with a software architecture resembling this configuration…

The smaller shape represents the underlying domain-agnostic framework for the whole system. Call it the foundation. The larger one is where all the business rules and domain logic lives. Call it the superstructure.

This shape looks unstable and un-elegant. In this analogy, the foundation sort-of supports the superstructure in that it’s mostly on the bottom, but parts of it end up pretty high up where a “foundation” probably shouldn’t reach. On the other end, the pointy bit that sticks out on the bottom is where the superstructure bypasses the foundation completely, which suggests the foundation isn’t providing what the superstructure needs. Plus, the entire system doesn’t sit flat.

Through years of organic software growth, these two systems had developed into this configuration that was difficult to change without risking even more instability.

I considered a few methods to rearrange the shapes that could resolve the instability and awkwardness.

Trying to shift the superstructure higher up without fundamentally changing its shape, so that the foundation could at least actually serve as the base and the whole thing could sit flat, was a lot of work against gravity (if you’ll permit the mixed metaphors) and still ends up with pointy bits sticking out, just elsewhere.

Moving entire pieces from one side to the other, like this example from earlier, would be a lot of effort for little gain.

I tried to think outside the box a bit: I could shift responsibility from the superstructure to the foundation to make the entire system bottom-heavy again. This would still require moving a lot of mass around, even if it isn’t going very far. And it isn’t even clear if this is a meaningful improvement.

After some more aborted out-of-the-box ideas, I decided the best course of action would be to sand off the pointy bits and deal with the remaining, irregularly-shaped pieces that still got too close to the tops and bottoms where they shouldn’t. At least it would be a rectangle that sits flat.

That rectangle sparked an idea. Consider the silhouette – the view from the outside – of the entire system.

Hey… you can construct that with these pieces instead!

This layout is still a little awkward, but the relationship of the silhouette to the parts that make it up is much less surprising! And all we had to do to get here is to redraw the lines between the shapes. All the mass stayed where it was. We have rearchitected the system away from one where everything was just a little bit wrong to one where the core is highly regular and it just has some hacks bolted onto it. (Ah, software in the real world.)

It’s now a lot easier to move the hacks around and change the silhouette, since it has relatively little connection to the core. If we want to make a change to the silhouette for some reason (that is: the system, holistically, as seen from the outside), we can do it with less effort and more flexibility.

Now it lies flat.

Now it lies flat and is symmetrical, so the hacks don’t grate quite as much. The possibilities are endless, and with some effort, we could even restructure the core to elegantly subsume all the mass from the hacks – there isn’t that much of it!

(Yes: it is ever so slightly wider. Conservation of mass, and all that.)

Let’s rewind a bit. What if we did put in the effort to move the original tiles around without redrawing the lines or moving mass between them?

In a silhouette, that looks like this.

You might be able to see where this is going.

A different architecture! Like the other one, it’s not really a two-layer architecture anymore. The precence of the square is an improvement, since squares have even less variability to consider than rectangles. There’s still a triangle sticking out the side, but it matches the side of the square perfectly: a good abstraction boundary, perhaps? Say: a minimal but fully-expressive API that is used mostly at a high level, but doesn’t prevent consumers from using the low-level parts when necessary. That sounds like a practial design to me.

The refactor isn’t done yet, but at least now I have a plan that is realistic, relatively less effort, and has a long-term path towards something halfway decent. Thanks, tangrams.


Previous Post
The Movie/TV List
Next Post
Let's Use Hardware Subscriptions for Good

tags: programming