Modularity is expressed in good code, but it primarily comes from good design. Here are some questions to ask about any code you work on that might help you improve its modularity:
How many global variables does it have? Global variables are modularity poison, an easy way for components to leak information to each other in careless and promiscuous ways.[48]
Is the size of your individual modules in Hatton's sweet spot? If your answer is “No, many are larger”, you may have a long-term maintenance problem. Do you know what your own sweet spot is? Do you know what it is for other programmers you are cooperating with? If not, best be conservative and stick to sizes near the low end of Hatton's range.
Are the individual functions in your modules too large? This is not so much a matter of line count as it is of internal complexity. If you can't informally describe a function's contract with its callers in one line, the function is probably too large.[49]
Does your code have internal APIs — that is, collections of function calls and data structures that you can describe to others as units, each sealing off some layer of function from the rest of the code? A good API makes sense and is understandable without looking at the implementation behind it. The classic test is this: Try to describe it to another programmer over the phone. If you fail, it is very probably too complex, and poorly designed.
Do any of your APIs have more than seven entry points? Do any of your classes have more than seven methods each? Do your data structures have more than seven members?
What is the distribution of the number of entry points per module across the project?[50] Does it seem uneven? Do the modules with lots of entry points really need that many? Module complexity tends to rise as the square of the number of entry points, too — yet another reason simple APIs are better than complicated ones.
You might find it instructive to compare these with our checklist of questions about transparency, and discoverability in Chapter 6.
[48] Globals also mean your code cannot be reentrant; that is, multiple instances in the same process are likely to step on each other.
[49] Many years ago, I learned from Kernighan & Plauger's The Elements of Programming Style a useful rule. Write that one-line comment immediately after the prototype of your function. For every function, without exception.
[50] A cheap way to collect this information is to analyze the tags files generated by a utility like etags(1) or ctags(1).