Unix Philosophy in Application Architecture
First, let me sum up how I see the fundamental part of Unix Philosophy: "write small simple things that do one thing well". These things can be wired-up at a higher level of abstraction. In Unix, this is done with shell scripts or other programs.
In designing applications, we should embrace this at every level, methods (or functions), classes, packages, modules, services, etc...
Functions should do one thing. They should do it well. They should do it only.
I believe we should take this approach to every level of application design and architecture. Each level of an application should do little more than wire up lower level components.
Even though we've evolved past the concept of Test Driven Design , we should take the lessons learned from it. Small simple things are easy to test. Components that are easy to test are easy to use. Tests provide an executable form of documentation. And let's face it, when it executes, it's not lying. We've all encountered documentation that misses reality. There's no reality like watching the code run.
I once saw a documentary on Burt Rutan. One of his engineers stated that sometimes when they came to him with an aircraft, he would arbitrarily send the engineers back with the goal of reducing weight. These engineers always thought they had already made the plane as light as possible. But, they'd go back, review the designs, and often create another plane even lighter than the boss asked for.
When was the last time you were in a design review where the result was to send the team back to create a simpler design?
I've never been to that meeting. Now, I'm in the role of lead-developer, I think sometimes I should arbitrarily send designs back and say, "reduce the complexity of this design". My current theory of application design is, if you can't draw it on a whiteboard, from memory, then it's too complex. This does not mean engineers should work on their memory skills. It means, we need to find ways to simplify, everything, at every level.
How about performing a code review and sending the developer back with the goal of reducing the complexity of the code?
Big, complex applications tend to be buggy and hard to maintain. New developers get scared of the complexity and tend to make (as they see) minimal changes instead of refactoring in changes. The quality of the code degrades, complexity increases. Code rot sets in.
We are fortunate enough to live in a modern age. Applications can talk to each other. Instead of building huge monolithic applications, we can have smaller applications that communicate with each other. If all of the functionality really is needed in one place, it can be served with a simple thin application that wires up the multitude. It will be simple, fast, and easy to understand. It should be so simple that if something breaks, the problem is obvious.
Take a look at Yahoo Pipes. The idea is, you can call lots of little applications and mash up the data into something much more interesting and complex than any of the individual components.
- Modules reused in many applications should be very well tested
- Small single purpose modules tend to be easy to test
- High test coverage instills confidence and demonstrates reliability
The modules that an application is made up should be small, simple, and have minimum dependencies. Dependencies between modules should flow in one direction only. If you find circular dependencies, then refactor until you can break the circle. If there are circular dependencies, then your "modules" exist in conceptual form only, in reality they're just a big blob.
When I first started building components (and running an internal Maven server), I often worried that some modules were too small to be packaged separately. I've since realized that this line of thinking was wrong. The important thing is not the size of, or number of classes in, a module. The important thing is its usefulness.
With Maven and Spring, I can control (and separate) the things are needed to build the application and the ones to make it run. I can build by depending on service interfaces, and include the implementation at runtime. By enforcing the separation (in the project pom), I can ensure that the implementations are free to change and evolve. Controllers become little more than object assemblers. Code can stay simple.
Everyone knows, estimating development time is a difficult task. Most often, this is made difficult because, too much information is unknown when giving an estimate. Estimating small tasks tends to be much more accurate than estimating larger tasks. So, by breaking tasks up into smaller ones, and those into even smaller ones... we can arrive at much better estimates.
The effect of breaking development tasks up is, the application is being designed. So, whether the practice is BDUF, or an agile style of emergent design, estimates will get better.
If the practice is BDUF, expect to spend a lot of time. If you've done it, you know the risks. However, the project estimates should become more accurate.
If you practice an agile methodology like Scrum, you should find that your sprints are more likely to stay on the planned track.
Unix Philosophy, What's Not to Love
More accurate estimates, cleaner code, reduced bugs, greater predictability, more re-usable components....