Agile Architecture and Optimization
At one point in our discussion, he at least partially adopted the role of devil's advocate with me, criticizing object-oriented development as a "straight-jacket" architecture which leads to fundamentally unchangeable inefficiencies. I don't think he truly believes this, but he definitely believes in the need to get "down and dirty" with code in order to ensure application performance. Based on my perspective as an agile software developer, I have attempted below to articulate how architecture and design can facilitate the integration of highly optimized code in a successful object-oriented software project.
Clearly, this is a work in progress and will be refined as our discussion continues.
Architecture
Architecture must be flexible and easy to change. It should never represent a straight jacket which prevents the developer from meeting the user requirements.
A refactorable architecture is one that can be radically and iteratively changed based on user requirements, including the need for quicker performance. Refactorable architectures have two main prerequisites: 1) modularity, and 2) testability. A modular architecture decouples a specific algorithmic approach or implementation from the rest of the application, making it easier to adapt the implementation based on new user stories or the need to optimize performance.
I strongly believe that object orientation—and the best practice of coding to interfaces—provides one very effective key to modularity. Indeed, both a good object-oriented language and application architecture should support the ability to plug in the best possible implementation, even if it exists outside of the language or design paradigm. In rare cases such as signal or image processing, there may be a need to use highly optimized procedural or assembly code, and every prominent object-oriented language provides for these bindings. Clear examples of this are the ability to integrate C code directly into C++ or Objective-C classes, and the ability to make calls to native C libraries in Java. Accordingly, a good architecture should shield these implementation specifics from client code and minimize the impact it can have on the rest of the application.
Secondly, an easy-to-run suite of unit and acceptance tests are fundamentally necessary to enable agile refactoring of the whole architecture. Without these tests, it quickly becomes impossible to change core aspects of the application design in order to improve performance or in light of new use cases. A test suite provides a harness to ensure that the agile development cycle—including benchmarking and optimization— is workable and more easily managed.
It is my sense that in most large applications, the foremost cause of performance problems is poorly designed and overly-complex architectures, not individual code implementations. The J2EE EJB specification is a prime example of this principle. While application server vendors such as IBM and BEA work tirelessly to optimize their implementation, the overall specification of EJB is at the root of many performance problems in J2EE applications. Assuming capable and diligent programmers, efficiency at the unit level is a natural part of the thought process, while architectural efficiency is too much a part of the big picture to be obvious while implementing individual pieces of code. Developers need to constantly consider and refine the overall shape and design of the application throughout the development process.
Performance, Efficiency, and Optimization
Optimization should be a product of evidence-based analysis, driven by user needs. Software is a multi-dimensional discipline, and performance is one of several important dimensions.
For desktop and rich web clients, the speed and responsiveness of an application is critical to its success. As a result, performance is part of the application's overall usability. In a customer-driven process, this means that it is the user who decides how efficient and responsive an application should be, not the developer. Focussing on code optimization early in the project makes it all too easy for developers to lose themselves in a kind of efficiency myopia, leading to a compromise of user requirements in exchange for optimizations which ultimately may have no real-world benefit.
Given this problem, there are two essential aspects to effective performance optimization: 1) concrete performance specifications decided by the user, and 2) metrics and benchmarks by which to measure the actual performance and effectiveness of an optimization. Real-world, evidence-based observations must drive the process of optimization, not theory. Rod Johnson provides a clear overview of how to do this in his J2EE without EJB book.
Ultimately, performance is only one requirement out of many. Although code-level efficiency may be a particularly interesting aspect of software development, that in itself doesn't necessary make it more important. A balance must be struck between a number of considerations in order to ensure a successful software product. It is the developers' responsibility to balance the need for a performant application with the ability to scale the code to future needs, to avoid bugs, and to develop the software on time and within budget. All of this should be driven by user needs, not developer interest.
The bottom line is that an application must provide the necessary features, responsiveness, and stability at the right price. In my mind, this is accomplished by letting the customer drive the requirements, by emphasizing a modular, changeable architecture, and by ensuring that tests and metrics play a central role throughout the development and optimization process. Agile development and object-oriented design can play a key role in ensuring this success.