The structure of Netty.


Netty's package structure is fantastic.

Every programmer should study it; every system should mimic it; every project manager should print it out, slap on a wall, and say to developers, "That."

Netty is an, "... an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients," but that doesn't matter here as we are not analyzing its behaviour. Instead, behold figure 1.

Figure 1: Netty's evolution

Figure 1: Netty's package structure evolving over 7 years.

Figure 1 presents a spoiklin diagram (circles are packages; straight lines are down-the-page dependencies; curved lines are up-the-page dependencies) of Netty's evolving package structure, and if you can't see immediately how well-structured it is, then take a peek at Junit, Struts or Ant.

Nor is it merely the case that, "Good structure is in the eye of the beholder." Structural disorder offers an objective measurement of how poorly a program is structured: the lower the structural disorder, the better the structure. Netty's disorder lies far below almost all others, see Table 1.

Program Package structural disorder
Ant 81%
Junit 76%
Struts 74%
Lucene 73%
FitNesse 61%
Spring 35%
Netty 26%

Table 1: Structural disorder of all programs reviewed in this series.

Figure 2, what's more, shows how this final structural disorder is no accident. Netty's structural disorder has been low throughout its seven-year lifetime.

Figure 2: Netty's structural disorder

Figure 2: Netty's structural disorder through 11 releases (with others for comparison).

So: why is this package structure so good?

Given a diagram like figure 1, we can ask two quick questions to evaluate - loosely - the depicted structure's merit.

In commercial software development, "Good structure," simply means, "Cheap to update." Furthermore, evidence suggests what every programmer with a grasp of ripple effects knows: that the more things X depends on, the more likely will be impacted by ripple effects and hence the more costly X might be.

Thus, pick a package that heavily depends on others an ask (A) can we easily identify the depended-upon packages and (B) how small a subset of the whole are these depended-upon packages?

Badly structured programs obscure these dependencies and scrutiny often reveals dependencies on almost the entire system. Well-structured programs, however, clearly present the depended-upon packages and they are few.

Let's ask these two questions first of a badly structured program.

Figure 2 shows the nightmarish 90% structural disorder of Jenkins, and then shows the highlighted transitive dependencies from the five packages (tool-tipped) that depend most on others.

Figure 2: Jenkins, Jenkins

Figure 2: Jenkins, oh Jenkins.

Tracing dependencies in Jenkins clearly ... poses a challenge, with many packages depending on over 75% of the rest of the system.

Figure 3 repeats the experiment but showing the transitive dependencies from the five Netty packages that depend most on others: epoll, spdy, websocketx, http and nio.

Figure 3: Netty's impact sets

Figure 3: Highlighting (in blue) the worst transitive dependencies in Netty.

In striking contrast with Jenkins, we can see the Netty packages depended upon, and how few there are. Netty has 55 packages but the maximum depended upon by any other is just 12, that's just 22% of the system.

Is Netty's package structure perfect? Of course not. Particularly the circular dependency between internal and concurrent creates regrettably strong coupling in that core internal/concurrent/channel/buffer/util package cluster.

Looking beneath the surface, indeed, Netty's class structure is bad. Netty's designers apparently abandoned some of their excellent structural principles when they built their class-level. A shame.

But looking at that package structure ... wow.

Finally, instead of analyzing Netty's crucial releases, an architectural observation presents itself. Netty's architects seem to have decided upon a rather brilliant deployment strategy. Downloading Netty gives you both an all-in-one jar file, but also 13 jar files which contain the separate parts of the system. Presumably you can load all Netty or just the parts of it you want.

One jar file, the "common" jar, houses the internal/concurrent/channel/buffer/util package cluster, whereas others hold, for example, "codec", "tcnactive", "transport", etc., suggesting that these latter jars are clients of the common jar but are not clients of one another and so have no dependencies on one another. Thus in their very deployment, Netty's designers may have enshrined the separation and encapsulation of their sub-systems that has lead to this industry-beating package structure.

The only remaining question is: why don't more projects follow Netty's lead? Why does Jenkins have a 90% structural disorder? Why didn't Jenkins' designers properly partition their system to reduce inter-package coupling? Why does the software development field so willingly accept the costs that these poor structures generate?

Aren't we better than this?

Summary.

If there were an annual award for the best Java package structure in use today, Netty would have won it seven years in a row.