Ada 95 Quality and Style Guide Chapter 1
Style is an often overlooked but very critical attribute of writing. The style of writing directly impacts the readability and understandability of the end product. The style of programming, as the writing of source code in a computer language, also suffers from this neglect. Programs need to be readable and understandable by humans, not just comparable by machines. This requirement is important in the creation of quality products that not only meet user needs but also can be developed on schedule and within estimated cost. This book is intended to help the computer professional produce better Ada programs. It presents a set of specific stylistic guidelines for using the powerful features of Ada 95 (Ada Reference Manual 1995) in a disciplined manner.
Each guideline consists of a concise statement of the principles that should be followed and a rationale for following the guideline. In most cases, an example of the use of the guideline is provided, and, in some cases, a further example is included to show the consequences of violating the guideline. Possible exceptions to the application of the guideline are explicitly noted, and further explanatory notes are provided, where appropriate. In some cases, an instantiation is provided to show more specific guidance that could be enforced as a standard. In selected cases, automation notes discuss how one could automate enforcement of the guideline.
Ada was designed to support the development of high-quality, reliable, reusable, and portable software. For a number of reasons, no programming language can ensure the achievement of these desirable objectives on its own. For example, programming must be embedded in a disciplined development process that addresses requirements analysis, design, implementation, verification, validation, and maintenance in an organized way. The use of the language must conform to good programming practices based on well-established software engineering principles. This book is intended to help bridge the gap between these principles and the actual practice of programming in Ada.
Many of the guidelines in this book are designed to promote clear source text. The goal of these guidelines is to improve the ease of program evolution, adaptation, and maintenance. Understandable source text is more likely to be correct and reliable. Easy adaptation requires a thorough understanding of the software; this is considerably facilitated by clarity. Effective code adaptation is a prerequisite to code reuse, a technique that has the potential for drastic reductions in system development cost. Finally, because maintenance (really evolution) is a costly process that continues throughout the life of a system, clarity plays a major role in keeping maintenance costs down. Over the entire life cycle, code has to be read and understood far more often than it is written; thus, the investment in writing readable, understandable code is worthwhile.
The remaining sections of this introduction discuss the organization of this book and how the material presented can be used by people in different roles, including new Ada programmers, experienced Ada programmers, object-oriented programmers, software project managers, contracting agencies, standards setting organizations, and planners of the transition to Ada 95 from existing Ada 83 (Ada Reference Manual 1983) programs.
The format of this book follows the well-received guideline format of the Ada Quality and Style: Guidelines for Professional Programmers, version 02.01.01 (AQ&S 83) (Software Productivity Consortium 1992). The style guide is divided into sections that map to the major decisions that each programmer must make when creating high-quality, reliable, reusable, and portable Ada software. Some overlap exists in the sections because not all programming decisions can be made independently.
Individual chapters address source code presentation, readability, program structure, programming practices, concurrency, portability, reusability, and performance, and a new chapter addresses object-oriented features. Each chapter ends with a summary of the guidelines it contains. The last chapter shows a complete implementation of the Dining Philosophers example, provided by Dr. Michael B. Feldman and Mr. Bjorn Kallberg. Many of the guidelines in this book were used to create this example. An appendix provides a cross-reference matrix between the Ada Reference Manual (1995) sections and the guidelines in this style guide.
This book is written using the general software engineering vocabulary developed over the last 20 years. Software engineering is a rapidly evolving discipline with relatively new concepts and terminology. However, to establish a common frame of reference, needed definitions are extracted from the Ada Reference Manual (1995) and Rationale (1995).
Throughout the book, references are made to other sources of information about Ada style and other Ada issues. The references are listed at the end of the book. A bibliography is also provided.
In this book, the term "Ada" refers to the latest Ada standard, released in February 1995 (sometimes also known as Ada 95). References to the earlier Ada standard are clearly denoted as "Ada 83."
1.1.1 Source Code Presentation and Readability
Chapters 2 and 3 directly address the issues of creating clear, readable, and understandable source text. Chapter 2 focuses on code formatting, and Chapter 3 addresses issues of use of comments, naming conventions, and types.
There are two main aspects of code clarity: (1) careful and consistent layout of the source text on the page or the screen, covered by Chapter 2, that can enhance readability dramatically; (2) careful attention to the structure of code, covered by Chapter 3, that can make the code easier to understand. This is true both on the small scale (e.g., by careful choice of identifier names or by disciplined use of loops) and on the large scale (e.g., by proper use of packages). These guidelines treat both layout and structure.
Code formatting and naming convention preferences tend to be very personal. You must balance your personal likes and dislikes with those of other engineers on the project so that you can agree to a consistent set of conventions that the whole project team will follow. Automatic code formatters can help in enforcing this kind of consistency.
Chapter 4 addresses overall program structure. Proper structure improves program clarity. This is analogous to readability on lower levels and includes issues of high-level structure, in particular the use of packages and child packages, visibility, and exceptions. The majority of the guidelines in this chapter are concerned with the application of sound software engineering principles, such as information hiding, abstraction, encapsulation, and separation of concerns.
Chapter 5 presents guidelines that define consistent and logical language feature usage. These guidelines address optional parts of the syntax, types, data structures, expressions, statements, visibility, exceptions, and erroneous execution.
Chapter 6 defines the correct use of concurrency to develop predictable, reliable, reusable, and portable software. The topics include tasking, protected units, communication, and termination. One major area of enhancement of the Ada language has been better support for shared data. The task mechanism had been the only available approach to protecting shared data. The guidelines in this chapter support the use of protected types to encapsulate and synchronize access to shared data.
1.1.5 Portability and Reusability
Chapters 7 and 8 address issues of designing for change from slightly different perspectives. Chapter 7 addresses the fundamentals of portability, the ease of changing software from one computer system or environment to another, and the impact of specific feature usage on portability. Chapter 8 addresses code reusability, the extent to which code can be used in different applications with minimal change.
The portability guidelines discussed in Chapter 7 need careful attention. Adherence to them is important even if the need to port the resulting software is not currently foreseen. Following the guidelines improves the potential reusability of the resulting code in projects that use different Ada implementations. You should insist that when particular project needs force the relaxation of some of the portability guidelines, nonportable features of the source text are prominently indicated.
The reusability guidelines given in Chapter 8 are based on the principles of encapsulation and design for change. These guidelines stress that understanding and clarity, robustness, adaptability, and independence are useful and desirable, even when reuse is not expected, because the resulting code is more resistant to both planned and unplanned change.
1.1.6 Object-Oriented Features
Chapter 9 defines a set of guidelines in common objected-oriented terms that exploit some of the features of Ada 95 that are not in Ada 83. The guidelines discuss the use of the new Ada features of type extension (tagged types), abstract tagged types, and abstract subprograms to implement single inheritance, multiple inheritance, and polymorphism.
Chapter 10 defines a set of guidelines intended to enhance performance. It is recognized that some approaches to performance are at odds with maintainability and portability. Most of the guidelines in this chapter read ". . . when measured performance indicates." "Indicates" means that you have determined that the benefit in increased performance to your application in your environment outweighs the negative side effects on understandability, maintainability, and portability of the resulting code.
This book is intended for those involved in the actual development of software systems written in Ada. The following sections discuss how to make the most effective use of the material presented. Readers with different levels of Ada experience or different roles in a software project will need to use the book in different ways.
There are a number of ways in which this book can be used: as a guide to good Ada style; as a comprehensive list of guidelines that will contribute to better Ada programs; or as a reference work to consult for usage examples of and design-tradeoff discussion on specific features of the language. The book contains many guidelines, some of which are quite complex. Learning them all at the same time should not be necessary; it is unlikely that you will be using all the features of the language at once. However, it is recommended that all programmers (and, where possible, other Ada project staff) make an effort to read and understand Chapters 2, 3, 4, and Chapter 5 up to Section 5.7. Some of the material is quite difficult (e.g., Section 4.2, which discusses visibility), but it covers issues that are fundamental to the effective use of Ada and is important for any software professional involved in building Ada systems.
This book is not intended as an introductory text on Ada or as a complete manual of the Ada language. It is assumed that you already know the syntax of Ada and have a rudimentary understanding of the semantics. With such a background, you should find the guidelines useful, informative, and often enlightening.
If you are learning Ada, you should equip yourself with a comprehensive introduction to the language. Two good introductory texts on Ada 83 are Barnes (1989) and Cohen (1986). Both authors have published new books that cover Ada 95 (Barnes 1996, Cohen 1996). Once you become familiar with these texts, you are encouraged to use them in conjunction with Rationale (1995). The Ada Reference Manual (1995) should be regarded as a companion to these books. The majority of guidelines reference the sections of the Ada Reference Manual (1995) that define the language features being discussed. Appendix A cross references sections of the Ada Language Reference Manual to the guidelines.
At first sight, Ada offers a bewildering variety of features. It is a powerful tool intended to solve difficult problems, and almost every feature has a legitimate application in some context. This makes it especially important to use Ada's features in a disciplined and organized way. Following the guidelines can make learning Ada easier and help you to master its apparent complexity. From the beginning, you can write programs that exploit the best features of the language in the way that the designers intended.
Programmers experienced in using other programming languages are often tempted to use Ada as if it were their familiar language but with irritating syntactic differences. This pitfall should be avoided at all costs; it can lead to convoluted code that subverts exactly those aspects of Ada that make it so suitable for building high-quality systems. You must learn to "think Ada." Following the guidelines in this book and reading the examples of their use will help you to do this as quickly and painlessly as possible.
To some degree, novice programmers learning Ada have an advantage. Following the guidelines from the beginning helps in developing a clear programming style that effectively exploits the language. If you are in this category, it is recommended that you adopt the guidelines for those exercises you perform as part of learning Ada. Initially, developing sound programming habits by concentrating on the guidelines themselves and their supporting examples is more important than understanding the rationale for each guideline.
The rationale for many of the guidelines helps experienced programmers understand and accept the suggestions presented in the guideline. Some of the guidelines themselves are also written for the experienced programmer who must make engineering tradeoffs. This is especially true in the areas of portability, reusability, and performance. These more difficult guidelines and rationale will make you aware of the issues affecting each programming decision. You can then use that awareness to recognize the engineering tradeoffs that you will eventually be asked to make when you are the experienced Ada programmer.
As an experienced Ada programmer, you are already writing code that conforms to many of the guidelines in this book. In some areas, however, you may have adopted a personal programming style that differs from that presented here, and you might be reluctant to change. Carefully review those guidelines that are inconsistent with your current style, make sure that you understand their rationale, and consider adopting them. The overall set of guidelines in this book embodies a consistent approach to producing high-quality programs that would be weakened by too many exceptions.
Consistency is another important reason for general adoption of common guidelines. If all the staff of a project write source text in the same style, many critical project activities are easier. Consistent code simplifies formal and informal code reviews, system integration, code reuse within a project, and the provision and application of supporting tools. In practice, corporate or project standards may require deviations from the guidelines to be explicitly commented, so adopting a nonstandard approach may require extra work.
Some of the guidelines in this book, particularly in the chapters
on concurrency, portability, reusability,
object-oriented features, and performance, focus on design tradeoffs.
These guidelines ask you to consider whether
using an Ada feature is an appropriate design decision for your
application. There are often several ways to implement a particular
design decision, and these guidelines discuss the tradeoffs you
should consider in making your decision.
As an experienced object-oriented programmer, you will appreciate the effort that has gone into elegantly extending the Ada language to include powerful object-oriented features. These new features are integrated tightly with the existing language features and vocabulary. This book is intentionally written to provide a view from the perspective of style; therefore, Ada object-oriented features are used throughout the book. Disciplined use of these features will promote programs that are easier to read and modify. These features also give you flexibility in building reusable components. Chapter 9 addresses object-oriented programming and the issues of inheritance and polymorphism. Earlier chapters cross reference the Chapter 9 guidelines.
You will find it easier to take advantage of many of the concepts in Chapter 9 if you have done an object-oriented design. The results of an object-oriented design would include a set of meaningful abstractions and hierarchy of classes. The abstractions need to include the definition of the design objects, including structure and state, the operations on the objects, and the intended encapsulation for each object. The details on designing these abstractions and the hierarchy of classes are beyond the scope of this book. A number of good sources exist for this detail, including Rumbaugh et al. (1991), Jacobson et al. (1992), the ADARTS Guidebook (Software Productivity Consortium 1993), and Booch (1994).
Technical management plays a key role in ensuring that the software produced in the course of a project is correct, reliable, maintainable, and portable. Management must create a project-wide commitment to the production of high-quality code; define project-specific coding standards and guidelines; foster an understanding of why uniform adherence to the chosen coding standards is critical to product quality; and establish policies and procedures to check and enforce that adherence. The guidelines contained in this book can aid such an effort.
An important activity for managers is the definition of coding standards for a project or organization. These guidelines do not, in themselves, constitute a complete set of standards; however, they can serve as a basis for standards. Several guidelines indicate a range of decisions, but they do not prescribe a particular decision. For example, the second guideline in the book (Guideline 2.1.2) advocates using a consistent number of spaces for indentation and indicates in the rationale that two to four spaces would be reasonable. With your senior technical staff, you should review each such guideline and arrive at a decision about its instantiation that will constitute your project or organizational standard.
Two other areas require managerial decisions about standardization. Guideline 3.1.4 advises you to avoid arbitrary abbreviations in object or unit names. You should prepare a glossary of acceptable abbreviations for a project that allows the use of shorter versions of application-specific terms (e.g., FFT for Fast Fourier Transform or SPN for Stochastic Petri Net). You should keep this glossary short and restrict it to terms that need to be used frequently as part of names. Having to refer continually to an extensive glossary to understand source text makes it hard to read.
The portability guidelines given in Chapter 7 need careful attention. Adherence to them is important even if the need to port the resulting software is not currently foreseen. Following the guidelines improves the potential reusability of the resulting code in projects that use different Ada implementations. You should insist that when particular project needs force the relaxation of some of the portability guidelines, nonportable features of the source text are prominently indicated. Observing the Chapter 7 guidelines requires definition and standardization of project- or organization-specific numeric types to be used in place of the (potentially nonportable) predefined numeric types.
Your decisions on standardization issues should be incorporated in a project or organization coding standards document. With coding standards in place, you need to ensure adherence to them. Gaining the wholehearted commitment of your programming staff to use the standards is critical. Given this commitment and the example of high-quality Ada being produced by your programmers, it will be far easier to conduct effective formal code reviews that check compliance to project standards.
Some general issues concerning the management of Ada projects are discussed by Hefley, et al. (1992).
Many of the guidelines presented here are specific enough to be adopted as corporate or project programming standards. Others require a managerial decision on a particular instantiation before they can be used as standards. In such cases, a sample instantiation is presented and used throughout the examples. Such instantiations should be recognized as weaker recommendations than the guidelines themselves. In some cases, where the examples are extracted from a published work, the author's style is used unchanged.
Other guidelines presented in this book are intentionally phrased in terms of design choices to consider. These guidelines cannot be instantiated as hard-and-fast rules that a project must follow. For example, you should not interpret Guidelines 6.1.1 and 6.1.2 to mean that a project is forbidden to use tasks. Rather, these guidelines are intended to help the designer make the tradeoffs between using protected objects and tasks, thus leading the designer to make a more informed choice between these features.
The guidelines in this document are not intended to stand alone as a standard. In some cases, it is not clear that a guideline could be enforced because it is only intended to make the engineer aware of tradeoffs. In other cases, a choice still remains about a guideline, such as how many spaces to use for each level of indentation.
When a guideline is too general to show an example, the "instantiation" section of each guideline contains more specific guidelines. These instantiations can be considered a standard and are more likely to be enforceable. Any organization that attempts to extract standards from this document needs to evaluate the complete context. Each guideline works best when related guidelines are practiced. In isolation, a guideline may have little or no benefit.
Transitioning issues fall into two major categories: the incompatibilities between the languages, in particular, upward compatibility, and exploitation of new language features.
Upward compatibility of Ada 95 was a major design goal of the language. The small number of incompatibilities between Ada 83 and Ada 95 that are likely to occur in practice are easily overcome (see Ada 95 Rationale [1995] Appendix X entitled Upward Compatibility). Detailed information on compatibility issues can be found in Taylor (1995) and Intermetrics (1995).
The transition planner can gain insight from this book into the exploitation of language features in two ways. First, Table 1 shows the impact of new Ada 95 language features on style guide chapters. Second, Appendix A maps Ada Reference Manual (1995) sections to specific style guidelines.
This style guide uses the following typographic conventions:
Serif font General presentation of information.
Italicized serif font Publication titles and emphasis.
Boldfaced serif font Section headings.
Boldfaced sans serif font Subheadings for guideline, instantiation, example, rationale, notes, exceptions, automation notes, caution, and subheadings in Summary sections.
Typewriter font Syntax of code.