Developing on Internet time still requires strong software-engineering principles
For several months in 1999, I worked primarily on the backend of a Web-based, ecommerce project. Almost all project software was implemented using Java and server-side Java technologies, including servlets, a homegrown version of JavaServer Pages (originally developed before a stable version of JSP became available), and JDBC to connect to and communicate with an Oracle database. There was even a touch of XML, but its use was limited, since the mainframe software-processing customer orders were XML-illiterate.
The development team worked long hours and weekends to meet unrealistic marketing-driven schedules, but it was an exciting time, and no one seemed to mind the extra time commitments. In fact, it was the most exhilarating, fun-filled software project on which I have worked, mostly due to the use of Java and Java-related technologies. In retrospect, I learned two general lessons — sort of a good news/bad news story — that I would like to share:
- This Java stuff really works.
- Software engineering on Internet time is extremely difficult.
This Java stuff really works
In my former programming lives, I have used a number of languages extensively. In fact, I can label each of the various periods of my professional career by its dominant programming language — the FORTRAN years, the Pascal years, the Ada years (Ada-83, not Ada-95), the C++ years, and now the Java years. Each step along this path was marked by a new programming language that was both more powerful and, until Java, more complex than its predecessor.
I found both Ada and C++ to be very complex, but when they were introduced into my career, their power more than compensated for the effort needed to master the complexities. Still, I found both languages lacking. For example, neither language supports reflection, and both have only minimal standard libraries when compared to Java. I have also constantly struggled with issues of object ownership and memory management, especially with C++; these issues are addressed directly by Java’s automatic garbage collection. I was particularly disillusioned with Ada when I tried to apply it on a real project where reuse was important. At the time, I didn’t fully understand the nature of my frustrations, but I recognized that Ada-83, while supporting reuse better than such earlier languages as COBOL, FORTRAN, and Pascal, still came up short in its support for reuse. Something was missing. I later found those missing features — classes as user-defined types, inheritance, polymorphism, and so forth — in C++; but C++ started out as a complex language and went on to spawn a plethora of even more complex features and constructs whose interactions made the language very difficult to use.
And then along came Java. Now, the word Java actually encompasses more than just a programming language, as Judge Thomas Penfield Jackson recognized when preparing his findings of fact in the Microsoft antitrust trial. Here is how the court accurately defines Java — a concept that has baffled countless computer journalists:
73. The term “Java” refers to four interlocking elements. First, there is a Java programming language with which developers can write applications. Second, there is a set of programs written in Java that expose APIs on which developers writing in Java can rely. These programs are called the “Java class libraries.” The third element is the Java compiler, which translates the code written by the developer into Java “bytecode.” Finally, there are programs called “Java virtual machines,” or “JVMs” which translate Java bytecode into instructions comprehensible to the underlying operating system.
Prior to 1999, I had read a couple of books on Java, written a number of toy programs, and used it in a couple of prototyping efforts. Although the early versions of Java were somewhat immature, I quickly recognized that the language had more power and flexibility than C++, but had jettisoned the excess baggage the latter language clung to while trying to remain compatible with C. It was a clean, elegant, consistent, and relatively simple language, albeit with an ugly syntax. When the concept of applets was first introduced, many people became excited, but I have always considered other aspects of Java, such as networking, JDBC, RMI, JavaBeans, servlets, and JSP, to be more interesting. Hence, I quickly accepted the opportunity to use Java for server-side software development.
Still, I had never tested Java in a trial-by-real-project, and I was more than a little worried about performance-related issues associated with interpreted Java bytecode using a JVM. If you are going to attack Java, performance has traditionally been its Achilles’ heel. Since Java’s initial release, a number of introductions have sought to improve its performance, including faster JVMs, better just-in-time (JIT) compilers, and Sun’s new HotSpot technology. And, when all else fails, you can always fall back on native methods and/or true compilers that translate directly to the machine code of the underlying platform, bypassing the JVM. To my surprise, my team needed none of these radical approaches to achieve the necessary performance and scalability — interpretation of Java bytecode using our JVM (with its built-in JIT compiler support) was sufficient.
Moreover, Java makes programming fun, and it was a key reason that I enjoyed working on this project. On the project, my team encountered none of the major problems reported by many early adopters of Java. In contrast to Ada and C++, Java got out of the way — the technology was a facilitator rather than an obstacle. Certainly, there is some complexity associated with learning Java, but after mastering fundamental object-oriented principles and a few basic language structures, most of Java’s complexity lies in the magnitude of its libraries. But that’s not a bad thing. Since the library APIs are relatively easy to use, having an extensive set of libraries promotes consistency and reuse, and it reduces the overall programming effort. Java’s complexity is in the right place. I hope that Sun will continue to take a minimalist approach to the language itself, and that future versions will concentrate primarily on improvements to the libraries and JVM performance.
The bottom line is that Java technology has matured. It works, and it is now ready for prime time!
Now for the bad news.
Software engineering on Internet time is extremely difficult
Based on years of experience as a software developer and as a consultant in object technology, I observed that the most common problem facing software projects has been schedule pressure. This problem is only magnified when you try to accelerate it to Internet time. Schedule deadlines often become so tight that developers often have to work frantically just to deliver usable code on time, leaving practically no time for activities like documenting the design beyond a few comments in the source code. With good developers and relatively small projects, you can get away with these shortcuts — for a while, at least. But as the guy in the TV ad for FRAM oil filters used to say, “You can pay me now, or you can pay me later.”
Even though Java is a great language, and the Java-related technologies have matured to the point that they work really well, they do not relieve us of our obligation to follow generally accepted software-engineering practices. In particular, programming Web applications in Java is not so different from other, more traditional forms of programming that you can eliminate major steps in analysis, design, testing, configuration management, and so forth. The ecommerce project that I mentioned earlier had started falling into this trap. Fortunately, new IT managers were brought in shortly before I joined the project, and they quickly began to introduce structure and organization into the development process.
Now, I am not suggesting that you should use a documentation-driven, process-heavy method for Web development. Not every user interaction needs to be captured with detailed interaction models, not every class needs to be described using class and state diagrams, and not every method needs a formal specification beyond the source code. The software community has generally moved away from such approaches to lighter-weight processes based on prototyping and iterative/incremental development cycles. But effective project communication requires, at a minimum, documentation of the major software requirements and key architectural decisions. Fo such documentation, it’s best to use a notation such as the Unified Modeling Language (UML), which is widely recognized as the primary notation for specifying object-oriented analysis and design models. (For additional information about UML, see the Resources section below.)
As an example, consider an architecture description that models user interaction and Webpage navigation using state machines. Let’s focus on a scenario in which a customer has decided to purchase several items from a Website and is currently viewing the shopping basket. Further, suppose that the corresponding Webpage contains a form with fields that let the customer delete items and/or modify quantities, and buttons that let the customer submit the order, cancel the order, or update the page (based on changes to the fields). At this point in the ordering process, there is state information pertaining to the items and quantities being ordered, and several possible button-related events that the customer can initiate. You can model this portion of the process as shown in the following UML statechart diagram. Of course, you could extend the model to model the entire process of order placement on the Web.
Using servlets, you can easily capture state information using the named properties of a session object, and you can develop routing objects in Java that implement state transition functions based on current state and user interaction. Stereotyped as control objects, these routing objects evaluate the user request and current state, perform related actions, and return appropriate HTML pages to the user. A high-level diagram such as this, together with a few sentences about the role of routers and session objects, can be invaluable when indoctrinating new project team members, when attempting to reuse parts of the architecture, and when moving an application to a maintenance function. For more detailed guidelines on using UML to model Web applications, see the Resource listings for Jim Conallen’s whitepaper, “Modeling Web Applications with UML,” and his recently published book, Building Web Applications with UML.
The project I described earlier faithfully applied the principles of object-oriented development including encapsulation, the Liskov Substitution Principle (see Resources), design patterns, and so forth. We had an effective source code control system, and it was used consistently within the project. The overall design was clean and efficient, and it worked. But when I first joined the project, the only design documentation that I saw outside of the source code was a single sequence diagram someone had drawn using Visio. What I wouldn’t have given during my first three or four weeks for a model of the architectural baseline! As we push forward into this exciting new eWorld, we need to remember the software engineering lessons from past projects.
The authors of this month’s server-side Java computing articles will be holding a free online seminar on February 10 at 10:00 a.m. Pacific Standard Time. Register to join at https://seminars.jguru.com.