Thursday, July 31, 2008

A Siege on The Ivory Tower

An ongoing debate with a former manager of mine -- not former because of this debate, btw -- regarding the design of object-oriented programming pondered into a blog sized post pondering one implication in particular. I will assume in this post you are somewhat familiar with the concept; if you aren't, it will seem like I am speaking in dead tongues through parts of this post. The basic ideas aren't hard, and wikipedia has an excellent treatment on them for the unfamiliar. Once you feel like you have a good handle, read on while keeping that page in a separate tab for when you reach an unfamiliar term. Now, on with the show.

The argument he presents can be distilled to a single quote: "objects respond to events, they do not pass data." There are several implications to this philosophy, which the similarly-minded Allen Holub goes into greater length about in a few posts. The basic idea I can gather from the conversation I had with him, along with these posts which seem to follow the same line of thinking, is that people are misunderstanding and misapplying encapsulation. This is blamed for a considerable amount of grief within the software development world, with the solution being a different, and supposedly better encapsulation. One that really cordones off your code from the evil demons that lurk within the core. Before I dig into the differing philosophies, there's something that must first be resolved.

Defining Encapsulation

As experience is quickly teaching me, a disproportionate number of technical arguments tend to boil down to disagreements on definition. Either the participants have distinct and incompatible definitions of the same term, or they have a disagreement about the meaning of a term in a definition they both agree on. So, let's start with a definition of encapsulation, in an attempt to diffuse the former.

As Wikipedia defines it, encapsulation "conceals the functional details of a class from objects that send messages to it."

Immediately, there is a problem of definition. What constitutes functional details, and what do we mean by concealment? It might sound like I am being overly pedantic, but this same sort of seemingly trivial confusion causes endless heated arguments on other topics. In this case, both are subjective terms. What details does one decide are worth concealing, and how concealed should each detail be? What qualifies as a functional detail, and what is sufficiently tangential to the functionality of the code to be ignored.

The answer I'd expect, and have gotten a number of times, from the proverbial object-oriented purist is to conceal as many details as possible, and conceal them as much as possible, regardless of the benefit each quanta of concealment provides. Allow the programmer the most minimalistic interface possible, not in the sense that it is simple, but in the sense that very little can be done with the interface exposed. The class should allow just enough access so that the user can actually do *something* at some point. Any more access than this, and class developer is providing rope than he should lest the developer interfacing with that class hang himself. The class developer walks a thin line between having something completely cordoned off and useless, or having something that someone could use *irresponsibly* and outside of its original rigidly specified use-cases. It becomes a choice between providing functionality, but being defensive about the way it the interface coded, or being defensive by *not* providing that functionality at all. The cause of this obsession is one of extreme distrust, and a belief that anyone who uses their code is either an idiot or malevolent. The definition of malevolent here is relatively broad, it pretty much envelopes anyone who might use or extend the class contrary to or in absense of requirements in the adjoining specification.

Extreme Encapsulation

The logical extreme of this philosophy is that one provides a minimalistic interface available only wrapped around another interface defined elsewhere. The only thing better than encapsulation is encapsulated encapsulation, ad infinitum. When someone describes a system that responds to events, they are almost inevitably talking about a system based on messaging. In the world of Java, this almost invariably means using something that relies on an implementation of JSR 914, colloquialy known as Java Messaging Service, or JMS. This is the ultimate of limited interface: one method, one way to deliver data to it, and one way to get data from it. Messaging systems are a good thing when their usage makes sense and adds functionality to the system, but this seems to do the opposite. The end result is the same, except now there are more steps involved, and the result of those new steps is static; it just adds more layers. A certain optimization opportunity if it weren't purporting a higher goal.

Building An Ivory Tower

It this feeling of distrust is apparently common amongst object-oriented designers and architects, at least in my experience. This distrust causes them to build their architecture in the form of an ivory tower, where those that dwell within are as secluded as possible from those without. If you must build a collosal system to make sure the entrance to it is as small as possible, then that is what you do. Metaphorically, it is like the lord who builds an collosal wall around his castle, and makes the only entrance to it a long winding hallway with a series of locked doors and guards. It almost makes one wonder if it is worth what is on the inside.

But the ivory tower architecture isn't to protect what is on the inside. The collosal castle wall and armed guards are not to protect the lord from the peasantry, it is to protect the builder of the wall from the lord. And it was built that way by specification, which the architect advocated for through the whole design process. Any explanation other than saying the architect was just covering his ass is wrong. It grants him license to handwave any criticisms away. Any inconsistency or misbehavior in the system is the fault of either the specification or the incompetent developer implementing it. The difficulty in accomodating the inevitable spec change will be blamed on the ineptitude of the specification authors. The design will also include components that are unnecessary, but further the apparent necessity of the architect. If a developer questions the component's existence, those concerns are dismissed by accusing the developer of not having context.

It is a pathology deserving of a name. If what I read most days is true, it is a pathology running rampant, and it seems to infect a disproportionate number of Java architects. Allen Holub even succumbs to it when he declares that flexibility is only a requirement of frameworks that can't make assumptions about how they will be used before-hand. How is he so certain the software he develops won't collapse under the tenuous requirements he swears are carved in stone? And how many frameworks that are now in widespread use throughout multiple corporations were ever built up with widespread use and praise as their eventual goal? I suspect the percentage is rather small. No one who authors a simple program ever thinks of "will be used worldwide as one of the canonical solutions to the larger problem it is solving a part of" as a use-case at the start. It just happens, and the design needs to be there from the start to accomodate that. Relying on restricting what the user can do to prevent errors can only scale so far, and then adding new functionality becomes a nightmare. The internal design has to change, but is hard to change because it was so rigid to begin with. Just because your restrictions and encapulations keep your users from having to care about the design of your software doesn't mean you don't have to. Design your software to be robust, fault-toleratant, and flexible, even if the specification implies you don't have to care. Eventually, you always will.

Modeling The World of Office Politics

So what motivates this sort of design? A lot of the design that goes into software is in the form of metaphors, so what metaphors are these architects relying on to construct their software architectures? They seem to mirror a common architecture of a different kind: the organization structure of the corporation. If two teams are building two software systems that interface with one another, the corporate structure and dynamic between those two teams will come out in the code they write. If they tend to collaborate a lot, and the corporate culture is one where openness and trust are cultivated, the interface between the two systems will likely be relatively thin, maybe involving something like a remote procedure call from one directly into the other. However, if the two teams are defensive and territorial, and the corporate culture is likewise, the interface between the two systems will be mired in the same sort of bureaucracy and layering as real-life communication in the company. If each department acts like an ivory tower in real-life, it is inevitable that their software will be constructed likewise. It is the corrolary to Conway's Law.

But, knowing this, can we then start making better designs by actively fighting against this sort of organizational inertia. The stakeholder will all resoundingly answer "No.", but not for the reason you might suspect. They probably seeless crufty designs as a worthy ideal, but to suggest improvements assuming that is the goal entirely misses the point. The goal of these designs is not efficiency; the goal is protection of power and the status quo. The goal is to simulaneously protect the interests of the department writing the software against the meddling of other departments, and to protect the architect against seeming less valuable because the system he wrote wasn't sufficiently complex to justify his employment. It is the sign of an organization functioning off of assumptions that have been proven false time and time again: that more complex software is harder to write, that the only sure way to protect one's job and department is to engage in petty turf wars, that the status quo is always desireable to change.

The chasm between the designs of an architect who's primary motivation is to create something that solves a business problem efficiently and effectively, and the architect who's primary motivation is remaining an architect as long as possible, is as wide as an ocean. It will be blindingly obvious which problem an architect is solving for first, and it will be obvious from that what sort of culture the company is cultivating. I think you can learn a lot about a corporation just based on their software systems, and that the design appears busted suggests to me that the company is also busted.

The Good Sort of Encapsulation

I've spent probably a half hour of your life railing against one of the principles of object-oriented programming. Let me assure you I have no problem with the OO design philosophy, including encapsulation, and think there are several domains where it creates nice metaphors. My issue is with the designers who take the philosophy to what they believe is the logical conclusion, and use it as justification to build walled gardens - a means of usurping power, where any resulting useful software is just a happy accident. They don't encapsulate to abstract, but where they encapsulate to cover their butts.

I do believe that encapsulation is a worthwhile concept however, but not for the same purpose. I see it as being useful in the same way as namespaces and lexical scopes, as a means of abstracting functionality and data so the developer doesn't have to care about how something is done; the same reason why structured programming was such a win. Declaring data and internal methods private that should be private is not what I am against; I am not against information hiding and functionality on which the user should not be depending. It is the hiding and encapsulating of the interface that troubles me; the interface that tries to be as elusive as possible as providing any sort of functionality, including that which the user *should* be taking advantage of. By doing so, it introduces accidental complexity elsewhere, where things have to bend in order to work correctly. What if the other classes are designed the same way?

You may not handle all the capabilities in your initial design, and you should build your software with the assumption that things might change, which invariably means keeping things as simple as possible. Holub deviates from this, by saying that that although maintainability is paramount, complexity is to be embraced as inevitable. Perhaps I'm just an edge case, but I have observed a tautology between complexity and lack of maintainability. Claiming that the system can be constructed such that a particular bit of functionality is confined is fine, but I can't help but wonder what work is involved in locating that functionality when maintenance is necessary, especially if layered under proxy objects. For that matter, what happens when the method signatures of those proxy objects change? I wonder about the claim that such compositional gymnastics will eliminate forever the need to change more than one class at a time. Or is this yet another difficulty we sweep under the rug by assuming a priori a perfect high-level design? I think it's worth taking one of the tips out of Pragmatic Programmer, and design the system for potential changes from the start. It seems like the route for less potential headaches, and is less likely to devolve into a squabble over who is responsible for the pain of dealing with the inevitable changes that must occur.

Thanks for reading. Feel free to leave any thoughts that you might have.