Jack C. Wileden
Programming languages have historically been the primary vehicles for supporting good software engineering practices. Today, despite almost thirty years of software engineering research, programming languages remain far and away the most important tools available to software developers. The emergence and explosive growth in popularity of Java is the latest evidence of the primacy of programming languages in this regard.
While the continued predominance of programming languages in supporting good software engineering practices seems assured, a number of challenges can be foreseen. One of these is the ever-increasing importance of component-based approaches to software development, with concomitant demands for improved support for interoperability. Another is the likely proliferation of software based on more dynamic languages and run-time infrastructures in software used in public or commercial applications, not just research labs.
Historically, programming languages have certainly been the primary vehicles for supporting good software engineering practices. Familiar, perhaps to the point of being overlooked, are such examples as structured control flow and user-defined complex types, which are now found in virtually every language. More obvious, if less widespread, is the support found in modern languages for such fundamental software engineering practices as data abstraction, software system modularization, principled use of concurrency and user-controlled exception handling. Moreover, it is worth noting that, while naturally primary, the role of programming languages has never been limited to aiding programming. For instance, among the earliest and most successful tools for software specification and software design were the so-called PDLs (program design languages), which were simply minimally abstracted programming languages. Similarly, the most widely used and effective software analysis tools have traditionally been the syntax analyzers and type checkers provided by programming language systems. Even such concepts as assertions and pre- and post-conditions have found their greatest acceptance and application when provided as programming language constructs.
Today, despite almost thirty years of software engineering research, programming languages remain far and away the most important tools available to software developers. Fortunately, recent trends in language design (and, more importantly, language usage) have generally encouraged improvements in software engineering practices. In particular, object orientation has proven to be THE key to enticing working software developers to embrace data abstraction, modularity and strong typing. More recently, object-oriented languages have brought user-controlled exception handling and improved modularization through richer namespace control mechanisms to the attention of working software developers. And again, it should be emphasized that the role of programming languages is not limited to aiding programming. For example, the integral support for (class) libraries in modern languages is the single most effective impetus for software reuse. The best tools for software specification and design continue to be based on (now object-oriented) programming languages. Perhaps most strikingly, the (object-oriented) design patterns phenomenon is popularizing notions of software architecture and software design much more rapidly and broadly than would have been imaginable absent a close connection to programming languages.
The emergence and explosive growth in popularity of Java is the latest evidence of the primacy of programming languages in encouraging and supporting good software engineering practices. First, Java brings even stronger typing, less error-prone memory management, integrated exception handling and modularization support into the software development mainstream. More importantly, Java is succeeding in promoting software security (in Hoare's sense of preventing a software module from damaging anything outside its own boundaries) to the level of a first class concern for software developers. Beyond that, the Java language system inherently provides automatic and strong support for security. The impact of Java alone seems likely to assure the predominant role of programming languages in delivering the benefits of good software engineering practices to software practitioners for the foreseeable future.
While the continued predominance of programming languages in supporting good software engineering practices seems assured, a number of challenges can be foreseen. One of these is the ever-increasing importance of component-based approaches to software development, with concomitant demands for improved support for interoperability. Another is the likely proliferation of software based on more dynamic languages and run-time infrastructures in software used in public or commercial applications, not just research labs.
The importance of component-based approaches to software development grows as the base of legacy software increases and the mechanisms for finding, obtaining and connecting to that software become more available and more capable. In an increasingly global software interchange forum, or market, such as that emerging through the mushrooming ubiquity of the World Wide Web, the demand for interoperability will inevitably intensify. Currently, support for interoperability, both within and outside programming languages, is distinctly limited. In particular, language boundaries are generally a major impediment to interoperability. Hence, in addition to focusing on individual languages, researchers will need to devote increased attention to fundamental issues, such as naming, typing or persistence, in ways that span language boundaries. Similarly, language developers will also need to attend to interoperability concerns.
The more dynamic the programming language or the run-time infrastructure on which a software system is built, the more difficult it is to reason about the potential behaviors, security and correctness of that software. Classic examples of dynamicity and its attendant challenges to reasoning include user-controlled run-time storage management, user manipulation of memory addresses (pointers) and user control over creation, destruction and communication among multiple concurrent processes. While modern programming languages, such as Java, have ameliorated some of these difficulties by wresting control over certain manifestations of dynamicity (e.g., storage management and pointers) from users, they have also left or added others (e.g., concurrency). Moreover, programming language features currently receiving a great deal of research attention, including garbage collection, reflection, open implementation, incremental or run-time compilation, and dynamic linking and loading, all carry the potential of exacerbating the difficulty of reasoning about potential behavior. Since a number of these features are now, or are expected to soon be, found in production languages, their impact will increasingly be felt in software used in public or commercial applications, not just research labs. While by no means insurmountable, the implications for reasoning about potential behaviors, security and correctness will need to be borne in mind by language researchers and designers as they investigate, advocate and promulgate these and other features.
It seems a safe bet that programming languages will continue to be by far the most important tools available to software developers. While the challenges considered above, and no doubt many others, demand attention from researchers, the primacy of languages suggests that such research will be most effective if conducted from a programming language perspective. In particular, tools and techniques for supporting interoperability should be based on language-derived principles, provided at the programming language level and integrated, to the greatest extent possible, with language capabilities. Similarly, the challenges of dynamicity will be best handled by careful language design and implementation. Programming language researchers and developers of the future would do well to mimic the approach exemplified by Java -- incorporate features that encourage good software engineering practices, keep unsafe constructs out of software developers' hands, and integrate static and dynamic checking into the language definition. Experience suggests that languages emulating this approach are much more likely to lead to well-engineered software than are tools that attempt to analyze or compensate for shortcomings in software before or after the programming phase.
Convergent Computing Systems Laboratory
Computer Science Department
University of Massachusetts at Amherst
Amherst, MA 01003