Modularity Deep Dive - pt. 1
Part 1 | Part 2
Why is it so hard to "do the right thing"? It's always easier to insert more code than it is to refactor. First, you have to decide what goes where, as you take things apart. Then you have to re-implement all the tests and clean up all the errors that occur as responsibilities move and change. It's both difficult and a lot of work. Sometimes, when I do a good job of it, I get to reuse the component in a new, and unforeseen way. Those moments make me feel like some sort of genius programmer. I like those moments. That, among other things, is what modularity can do for you.
I wrote this post, because, approximately two years ago project Jigsaw was getting a lot of attention. Since then, I've spent a lot of time reading and thinking about modularity. This article is not about JPMS, but modularity as a design concept. In this post I'll talk about:
- Why do we want to make things modular?
- What makes a good module?
I was born in '82 and, as a student, I didn't do a lot of extracurricular reading. For a long time I've been unaware of a lot of the academic history of computer science. Thankfully, I watch a lot of YouTube. I came across a video of GOTO conf 2016 where at minute 34 the host seems to call me out personally for never having read David L. Parnas' work on modularity. So, I did.
The take-away from the paper "On the Criteria To Be Used in Decomposing Systems into Modules", as the title suggests, is a criterion. A rule that tells you where to draw the lines when you draw those boxes on paper. You know, those boxes that only vaguely map to the actual code you end up writing? The rule, Parnas concludes, is to modularize around difficult design decisions. For example: how do we store our data? Are we sure? How likely is it to change? Let's abstract it. Our data storage and schema is now abstracted into a module that hides the details. If done well, it makes it easier to change later. At the same time, it makes concrete what our requirements are. This rule sounds awfully familiar. I must have heard it somewhere else on YouTube.
It sounds a lot like Robert Martin on Clean Architecture. Both Parnas' paper and Clean Architecture highlight something important about software design. The reason why we need to create modules is to postpone decisions, which gives us future agility. Or as Bob puts it:
"Later is always better when you're making decisions; you have more information later."
Using "decision deferment" as a guiding principle produces a different delineation of modules than the one of "steps in a data processing pipeline". Knowing this certainly does not make it an easy thing to do. Thinking through it takes time. Time not spent writing code, which can feel or look unproductive. On the upside, it can save you long discussions about the choice of technology and futile attempts at predicting far into the future. Another benefit is that modules that can change independently, can also change simultaneously. Leading to efficient parallel development.
The answer to the first question "Why write modular code?" is: In order to remain agile. Had you asked me two years ago, I might have said something about reusability. But now I know that reusability is an emergent feature of how modules are implemented, and not the primary reason for them. Being agile is easy at the start of a project, because you have no other code. Staying agile takes deliberate and careful design.
Now the answer to the second question "What makes a good module?" seems pretty obvious: One that makes future change easy. I believe modules can do these things. But only when we, the programmers, make thoughtful choices.
Part 1 | Part 2