Code Complete

(A detailed book review)

Book Information

Code Complete Book

Code Complete

A Practical Handbook of Software Construction

By Steve McConnell

Microsoft Press, 1993, 857 pages

ISBN 1556154844

Summary

Code Complete is programming classic. It is 900 pages of intelligent and fascinating discussion about coding software.

Introduction

This book is not about how to program in a particular language, how to use SSADM or other methodologies. This book is about improving the way that you work throughout the development cycle.

The author describes the development process as being everything from the technical, detailed design stage, right through to the integrated testing. This is the jurisdiction, or domain, of the programmer.

With an immense bibliography, the author has combined theories, common practices and hard data to make his points. Sometimes, however, he completely contradicts the commonplace practices. Here is a man who supports established practices, but backs his own convictions where they differ. Subjectivity surfaces on occasion with statements such as "If you come across one of these clowns, ask him the following". On the whole, there is the distinct impression that the author was motivated to write this book in order to help programmers, and related IT staff.

References to personal experience punctuate bibliographic references in order to put the point across. Clearly this man knows his stuff, and is not simply trying to pedal his own particular point of view that has never been proven. The author has developed his techniques over time, and has then decided to write a book on the subject. Where the purpose of his methodologies is nebulous, he backs them up with hard data.

Summary of Points

Here is a summary of the points that I believe are particularly of note.

1.       Software Accretion Method. The author recommends an accretion approach to software construction. This means the method of initially building the most basic working system possible, and then adding on layers piece by piece.

2.       Prerequisites. It seems obvious, but it is important to have all of the requirements in place before time is spent on detailed design or construction. Ensure that all the system prerequisites are outlined. The author also describes the "Myth of Stable Requirements". The important thing is to manage changes properly.

3.       Use of PDL. This is a section that I found particularly fascinating, in the section dealing with designing Routine. PDL stands for Programming Definition Language. PDL is a language similar in purpose to pseudo code, but is more abstract, using statements that resemble spoken English. The method here is to design a routine in PDL and then use the statements to form the basis of the routine. The PDL statements become the comment lines in the routine, and the functionality is filled in between the comments. This method allows the design of the routine to remain a constant, and to be clearly visible when viewing the code.

4.       Routines and Modules. The author spends a lot of time describing quality. This is particularly apparent with coding modules and routines. The author lists good and bad reasons for writing routines, and how to write quality routines and modules.

5.       Abstraction and Naming. One of the most recurring themes throughout the book is abstraction. This theme is prominent when the author discusses naming standards. For routines, modules, variables, constants and literals, abstract naming standards are extremely useful. They allow code reading to be made easier, and help to describe the program in the problem domain.

6.       Layout and Style. After mentioning quality in routine design and use, the author outlines the correct layout and styles of coding to use. Good and bad examples are shown, and the reasons for the choice of layout are mentioned. Also mentioned is how much of a sensitive area layout and programming styles are, including the hundred years war that is the GOTO debate.

7.       Management. The book is not only focused on the programmer. There is a section that is for the attention of the Programming Manager. This deals with everything from planning and scheduling right through to managing the people. When discussing planning, the importance of measurement is also mentioned. Apart from progress, quality also needs to be measured. Several methods for measuring are suggested, including formal and informal reviews.

8.       Testing. There are more opinions and suggestions for testing than just about anything else in software development. This book is no exception, however, the author describes testing in sections of Unit testing, Functional Testing, Integration Testing and Live Testing. Methodologies for all of these areas are outlined.

9.       Optimization. There may be times when a program needs to become more streamlined. The author discusses the use of code tuning techniques, and the most important issue of when to optimize and when to leave it alone.

Table of Contents

1.      Understanding Software Construction. 8

1.1.    Metaphors. 8

1.2.    Writing Code. 8

1.3.    Summary. 9

1.4.    Forwarding Actions. 9

2.      Prerequisites to Construction. 9

2.1.    Importance. 9

2.2.    Problem Definition. 10

2.3.    Requirements. 10

2.4.    Architecture. 11

2.5.    Language. 11

2.6.    Programming Conventions. 11

2.7.    Time to spend on Pre-Requisites. 11

2.8.    Adapting Pre-Requisites. 11

2.9.    Summary. 12

2.10.     Forwarding Actions. 12

3.      Building a Routine. 12

3.1.    Summary of Steps. 12

3.2.    PDL for Pros. 12

3.3.    Design the Routine. 12

3.4.    Code the Routine. 13

3.5.    Formal Checking. 13

3.6.    Summary. 13

3.7.    Forwarding Actions. 13

4.      High Quality Routines. 13

4.1.    Valid Reasons to Create a Routine. 13

4.2.    Good Routine Names. 14

4.3.    Strong Cohesion. 14

4.4.    Loose Coupling. 14

4.5.    How Long Can a Routine be?. 14

4.6.    Defensive Programming. 15

4.7.    Use of Routine Parameters. 15

4.8.    Consider the use of Functions. 15

4.9.    Summary. 15

4.10.     Forwarding Actions. 15

5.      Modules. 16

5.1.    Modularity: Cohesion and Coupling. 16

5.1.1.    Cohesion. 16

5.1.2.    Coupling. 16

5.2.    Information Hiding. 16

5.2.1.    Secrets and the Right to Privacy. 16

5.2.2.    Common Secrets. 16

5.2.3.    Barriers to Information Hiding. 17

5.3.    Good Reasons to Create a Module. 17

5.4.    Summary. 17

5.5.    Forwarding Actions. 17

6.      High Level Design. 17

6.1.    Introduction to Software Design. 17

6.2.    Structured Design. 18

6.2.1.    Choosing Components to Modularize. 18

6.3.    Object-Oriented Design. 18

6.3.1.    Key Ideas.18

6.3.2.    Design Steps. 18

6.3.3.    Typical Components. 19

6.4.    Comments on Popular Methodologies. 19

6.4.1.    When to use Structured Design. 19

6.4.2.    When to use Information Hiding. 19

6.4.3.    When to use Object Oriented Design. 19

6.5.    Round Trip Design. 19

6.6.    Design is a Heuristic. 19

6.7.    How to solve it. 19

6.8.    Summary. 20

6.9.    Forwarding Actions. 20

7.      Creating Data. 20

7.1.    Reasons to create your own Data Types. 20

7.2.    Guidelines for creating Data Types. 20

7.3.    Making Variable Declarations Easy. 20

7.4.    Guidelines for Initializing Data. 20

7.5.    Summary. 20

7.6.    Forwarding Actions. 21

8.      The Power of Data Names. 21

8.1.    Considerations in choosing good names. 21

8.1.1.    The Effect of Scope on Variable names. 21

8.1.2.    Computed-Value Qualifiers in Variable Names. 21

8.2.    Naming Specific Types of Data. 21

8.3.    The Power of Naming Conventions. 21

8.4.    Informal Naming Conventions. 21

8.5.    Hungarian Notation. 22

8.6.    Short Names. 22

8.7.    Kinds of Names to avoid. 22

8.8.    Summary. 22

8.9.    Forwarding Actions. 22

9.      General Issues in Using Variables. 22

9.1.    Scope. 22

9.2.    Persistence. 23

9.3.    Binding Time. 23

9.3.1.    Code Time Binding. 23

9.3.2.    Compiler Time Binding. 23

9.3.3.    Run Time Binding. 23

9.4.    Relationship between Data Structures and Control Structures. 23

9.5.    Use each Variable for one purpose only. 23

9.6.    Global Variables. 23

9.6.1.    Common Problems. 23

9.6.2.    Reasons to use Global Data. 23

9.6.3.    How to Reduce the Risks of Using Global Data. 23

9.6.4.    Use Access Routines instead of Global Data. 24

9.7.    Summary. 24

9.8.    Forwarding Actions. 24

10.    Fundamental Data Types. 24

10.1.     General Numbers. 24

10.2.     Integers. 24

10.3.     Floating Point Numbers. 24

10.4.     Characters and Strings. 24

10.5.     Booleans. 25

10.6.     Enumerations. 25

10.7.     Named Constants. 25

10.8.     Arrays. 25

10.9.     Summary. 25

10.10.    Forwarding Actions. 25

11.    Organizing Straight Line Code.25

11.1.     Statements that must be in a specific order. 25

11.2.     Statements who's order does not matter. 25

12.    Using Conditions. 26

12.1.     IF statements. 26

12.2.     IF chains. 26

12.3.     CASE statements. 26

12.4.     Tips. 26

13. Controlling Loops.26

13.1.     Select the kind of Loop to use.26

13.2.     Controlling the Loop.26

13.3.     Creating Loops form the inside out.27

13.4.     Correspondence between loops and arrays. 27

14.    Unusual Control Structures. 27

14.1.     GOTO. 27

14.2.     RETURN/EXIT. 27

14.3.     Recursion. 27

15.    General Control Issues. 28

15.1.     Boolean Expressions. 28

15.2.     Compound Statements. 28

15.3.     NULL Statements. 28

15.4.     Taming Dangerously Deep Nesting. 28

15.5.     The Power of Structured Programming. 28

15.6.     Control Structures and Complexity. 28

16.    Layout and Style. 29

16.1.     Fundamentals. 29

16.2.     Layout Techniques. 29

16.2.1. White Space. 29

16.2.2.   Parentheses. 29

16.3.     Layout Styles. 29

16.3.1. Pure Blocks. 29

16.3.2. End-line Layout30

16.3.3. BEGIN-END Block Boundaries. 30

16.4.     Laying Out Control Structures. 30

16.4.1.   Fine Points of Formatting Control Structures. 30

16.4.2.   Other Considerations.30

16.5.     Laying Out Individual Statements. 30

16.5.1.   Statement Length. 30

16.5.2. Using spaces for clarity. 31

16.5.3.   Aligning Related Statements. 31

16.5.4.   Format Continuation Lines. 31

16.5.5. Use Only One Statement Per Line. 31

16.5.6.   Laying Out Data Declarations. 31

16.6.     Laying Out Comments.31

16.7.     Laying Out Routines.31

16.8.     Laying Out File, Modules and Programs.31

16.9.     Summary.31

16.10.    Forwarding Actions. 32

17.    Self-Documenting Code.32

17.1.     External Documentation.32

17.2.     Programming Styles as Documentation.32

17.3.     Commenting.32

17.3.1.   Types of Comments. 32

17.3.2.   Commenting Efficiency. 32

17.4.     Commenting Techniques.32

17.4.1.   Individual Lines.32

17.4.2.   Commenting Paragraphs. 32

17.4.3.   Commenting Data Declarations. 33

17.4.4.   Commenting Control Structures. 33

17.4.5. Commenting Routines. 33

17.4.6.   Commenting Files, Modules and Programs. 33

17.4.7.   Using the "Book" Paradigm for commenting. 33

17.5.     Summary. 33

17.6.     Forwarding Actions. 34

18.    Programming Tools. 34

18.1.     Design Tools. 34

18.2.     Source Code Tools. 34

18.2.1.   Editing. 34

18.2.2.   Browsing. 34

18.2.3.   Analyzing Code Quality. 34

18.2.4.   Restructuring Source Code. 35

18.2.5.   Version Control35

18.2.6.   Data Dictionaries. 35

18.3.     Executable Code Tools. 35

18.3.1.   Code Creation.35

18.3.2.   Debugging.35

18.3.3.   Testing.35

18.3.4.   Code Tuning.35

18.4.     Tool-Orientated environments.35

18.5.     Building your own tools.36

18.6.     Summary. 36

18.7.     Forwarding Actions. 36

19.    How Program size Affects Construction. 36

19.1.     Effect of Project Size on Development Activities. 36

19.2.     Effect of Project Size on Errors. 36

19.3.     Effect of Project Size on Productivity. 36

20.    Managing Construction. 36

20.1.     Encouraging Good Coding.36

20.1.1.   Considering in Setting Standards.36

20.1.2.   Techniques.37

20.2.     Configuration Management. 37

20.2.1.   What is configuration Management?. 37

20.2.2.   Software Design Changes. 37

20.2.3.   Software Code changes. 37

20.3.     Estimating a Construction Schedule. 37

20.3.1.   Approaches. 37

20.3.2.   Establish Objectives. 37

20.3.3.   Influences on Schedule. 37

20.3.4.   Estimation vs. Control38

20.3.5.   What to do if you are behind. 38

21.    Software Metrics.38

21.1.     Treating Programmers as people. 38

21.2.     Variations in performance and quality. 38

21.3.     Religious Issues. 38

21.4.     Physical Environment. 39

21.5.     Summary. 39

22.    The Software Quality Landscape. 39

22.1.     Characteristics of Software Quality. 39

22.2.     Techniques for improving Software Quality. 39

22.3.     Relative Effectiveness of techniques. 39

22.3.1.   Percentage of Errors found. 39

22.3.2.   Cost of finding defects. 40

22.3.3.   Cost of fixing defects. 40

22.4.     When to do a QA. 40

22.5.     General Principle of Software Quality. 40

22.6.     Summary. 40

22.7.     Forwarding Actions. 40

23.    Reviews. 40

23.1.     The role of reviews. 40

23.1.1.   Reviews complement other QA techniques. 40

23.1.2.   Reviews remove corporate structure.40

23.1.3.   Reviews assess Quality and Progress.40

23.1.4.   Reviews also apply before construction.41

23.2.     Inspections.41

23.2.1.   Roles During Inspections. 41

23.2.2.   Procedure for Inspections. 41

23.3.     Other kinds of reviews. 41

23.3.1.   Walkthroughs.41

23.3.2.   Code Reading.41

23.3.3.   "Dog and Pony shows"42

24.    Unit Testing. 42

24.1.     The Role of Unit Testing. 42

24.2.     Testing During Construction. 42

24.3.     The Testing Bag of Tricks. 42

24.3.1.   Incomplete Testing. 42

24.3.2.   Structured Basis Testing. 42

24.3.3.   Data Flow Testing. 42

24.3.4.   Equivalence Partitioning. 42

24.3.5.   Error Guessing. 42

24.3.6.   Boundary Analysis. 42

24.3.7.   Classes of Bad Data. 43

24.3.8.   Classes of Good Data. 43

24.3.9.   Use test cases that allow easy manual checks.43

24.4.     Typical Errors. 43

24.4.1.   Which routines contain the most errors?. 43

24.4.2. Errors by Classification. 43

24.4.3.   Proportion of Errors Resulting from faulty construction. 43

24.4.4.   How many errors should you expect to find. 43

24.4.5.   Testing itself43

24.5.     Test Support Tools. 44

24.5.1.   Scaffolding. 44

24.5.2.   Results Comparators. 44

24.5.3.   Test Data Generators. 44

24.5.4. Coverage Monitors. 44

24.5.5. Symbolic Debuggers. 44

24.5.6.   System Perturbers. 44

24.5.7.   Error Databases.44

24.6.     Improving Testing. 44

24.7.     Planning to test. 44

24.7.1. Re-testing. 44

24.8.     Keeping Test Records. 44

24.9.     Summary. 44

24.10.    Forwarding Actions. 44

25.    Debugging. 45

3.1 Overview of Issues. 45

25.0.1.   Role of Debugging. 45

25.0.2.   Variations in Debugging Performance. 45

25.0.3.   Errors as Opportunities. 45

25.0.4.   An Ineffective approach. 45

25.0.5.   Debugging by superstition. 45

25.1.     Finding an Error. 45

25.1.1.   Use a Scientific Method. 45

25.1.2.   Tips on Finding Errors. 46

25.1.3.   Syntax Errors. 46

25.2.     Fixing an Error. 46

25.3.     Psychological Considerations. 46

25.4.     Debugging Tools. 46

25.5.     Summary. 46

26.    System Integration. 47

26.1.     Importance of the Integration Method. 47

26.2.     Phased vs. Incremental Integration.47

26.2.1.   Phased Integration. 47

26.2.2.   Incremental Integration. 47

26.2.3.   Benefits of Incremental Integration. 47

26.3.     Incremental Integration strategies. 47

26.3.1.   Top Down Integration. 47

26.3.2.   Bottom Up Integration. 47

26.3.3.   Sandwich Integration. 47

26.3.4.   Risk Orientated Integration. 47

26.3.5.   Feature Orientated Integration. 47

26.4.     Evolutionary Delivery. 48

26.4.1.   General Approach. 48

26.4.2.Benefits. 48

26.4.2.   Relationship of Evolutionary Delivery to Prototyping. 48

26.4.3.   Limitations. 48

26.5.     Summary. 48

26.6.     Forwarding Actions. 48

27.    Code Tuning Strategies. 48

27.1.     Performance Overview.. 48

27.1.1.   Quality Characteristics and Performance. 48

27.1.2.   Performance and Code Tuning. 48

27.2.     Introduction to Code Tuning. 48

27.2.1.   Old Wives' Tales. 49

27.2.2.   The Pareto Principle. 49

27.2.3.   Measurement.49

27.2.4.   Compiler Optimizations. 49

27.2.5.   When to use Code Tuning. 49

27.2.6.   Iteration. 49

27.3.     Common Sources of Inefficiency. 49

27.4.     Summary of Approach to Code Tuning. 49

27.5.     Summary. 49

28.    Code Tuning Techniques. 49

28.1.     Loops. 49

28.2.     Logic. 50

28.3.     Data Transformation. 50

28.4.     Expressions.50

28.5.     Routines. 50

28.6.     Re-Code in Assembler. 50

29.    Software Evolution. 50

29.1.     Guidelines. 50

29.2.     Making New Routines. 51

30.    Themes in Software Craftsmanship. 51

30.1.     Conquer Complexity. 51

30.1.1.   Ways to reduce complexity. 51

30.1.2.   Hierarchies and Complexity. 51

30.1.3.   Abstraction and Complexity. 51

30.1.4.   Pick your Process. 51

30.2.     Write programs for people first, and computers second. 51

30.3.     Focus your attention with the help of conventions. 51

30.4.     Programming in terms of the problem domain. 51

30.5.     Watch for "Falling Rocks"51

30.6.     Iterate. 51

30.7.     "Thou Shalt Render Religion and Software Asunder"52

30.7.1.   Software Oracles. 52

30.7.2.   Eclecticism.. 52

30.7.3.   Experimentation. 52

 

 

1.       Understanding Software Construction

 

1.1.     Metaphors

 

This section noted the importance of metaphors in software construction. Metaphors are a highly useful means of communicating ideas and concepts.

 

When technical people need to communicate with non-technical people, the language barrier typically separates them. This is because non-technical people do not have a deep understanding of the fundamentals of software construction, nor should they. It is important for technical people to be able to communicate with the non-technical.

 

This makes sense, as it is then possible to communicate an idea to another person, using something that both people understand, as a metaphor.

 

If a technical person is trying to communicate an idea to a non-technical person, metaphors and analogies can be used to help instigate a clear understanding of the concept.

 

The use of metaphors can be referred to as “Modeling”.

 

Many different types of metaphors are used when interpreting technical issues. It is important to remember that there is never a “perfect” metaphor. This means that there is not a definitive “right” versus “wrong” metaphor. It is inevitable that, over time, different metaphors will arise that may describe technical issues better than older ones. This does not mean that the old metaphor was wrong, and the new one is right. They are simply different, with one being more correct than another: “better” and “worse”.

 

Software metaphors are not algorithms. They are heuristics. To define:

 

Many errors that occur in Software Construction are conceptual errors. They are problems with what is being achieved not how it is being achieved.

 

1.2.     Writing Code

 

Quotes:

 

These quotes imply that a lot of work will prove to be pointless, and is typically discarded and started again. This comes from the old school of programming.

 

A new approach to software construction should be considered. Take the metaphor of farming. Code is generated gradually, a little at a time. The author states: “If you buy into this idea, you will end up talking about Fertilizing the system plan, Thinning the detailed design, increasing code yield through effective land management and Harvesting code.”

 

This metaphor is useful, but a better one is that of Oyster Farming. An oyster makes a pearl by the gradual addition of material around an irritant. A computer program can be initially developed to perform the most basic function possible, as long as the full cycle of the program is followed. This is essentially a skeleton program. From here, more functionality can be added little by little. The whole process is incremental.

 

This can help identify any conceptual or design issues earlier than developing the entire system on a screen-by-screen, or module-by-module basis. This helps to check the integrity of the design.

 

1.3.     Summary

 

 

1.4.     Forwarding Actions

 

 

 

2.       Prerequisites to Construction

 

2.1.     Importance

 

Why is software written? The answer should be “To solve a problem”. At the most basic level, this should be the goal of a software product. With this in mind, it is important to have a few essentials before development starts. At the very least, the reason for the software is required.

 

Prerequisites are a list of items that the developer really should have before coding starts. Even if the developer knows what the problem is, that does not always constitute everything they need before they start coding.

 

By identifying all of the prerequisites before coding starts, problems and issues can be identified and resolved before they are encountered, therefore not holding up the development process while the problems and issues are resolved.

 

There are two main contributors to this problem:

 

Developers have had a tendency to think “Well, he must know what he’s talking about…” and then get the grief when problems arise later.

 

The author described an occasion when working on a US military project. The Project Manager was a large Army General. He arrived at the office one day and wanted to see some code. He was told that the project was in its requirements phase, and everyone was writing documents, and talking to customers. Nevertheless, the general wanted to see some code. He went around all 100 developers until he found one of them writing what looked like code. In fact, the developer was writing a document-formatting utility. However, because the general wanted to see some code, and found what looked like code, he went away happy.

 

If a manager demands that a developer starts coding, there are four options identified by the author:

 

A useful metaphor is to think of the development process as a food chain. It passes from requirements, through to architecture, and down to the developers. If the environment is polluted, that is to say, the requirements are erroneous, these problems pass down to the developer.

 

Problems detected in the requirements phase are much less costly than ones discovered during development.

 

A report identified that if an error in requirements is found in development or maintenance, then it will cost 50-200 times more than if it is found during requirements or design.

 

IBM issued the following table to show the increase in cost of resolving errors during particular phases.

 

Time Detected

Time Introduced

Requirements

Design

Coding

Analysis

1x

-

-

Design

2x

1x

-

Testing (Passive)

5x

2x

1x

Testing Structural)

15x

5x

2x

Testing (Functional)

25x

10x

5x

 

This table should communicate clearly the reason for fulfilling prerequisites before coding. It costs a lot less to start things correctly, than it does to constantly change things.

 

Another consideration is the time involved, and late nights that may arise. Getting it right at the start makes life a lot easier.

 

2.2.     Problem Definition

 

As mentioned earlier, software is written to solve a problem. Ensure that the problem is clearly articulated. Then problem definition should be just that, a definition of a problem. It should not be a statement of how something is to be improved. This is an entirely different statement.

 

The problem definition is the basic building block of the software development. The foundation.

 

This ensures that the software does not solve a different problem to the one that is required.

 

2.3.     Requirements

 

Once the problem is identified, the requirements of the solution can be identified. As mentioned earlier, it costs more to fix fundamental requirements problems further down the development cycle than it does to correct them at the requirements stage.

 

Getting the requirement right will reduce the number of changes later.

 

The following table describes the increases in cost of an error in requirements:

 

Stage

Cost Ratio

Requirements

1x

Design

5x

Coding

10x

Testing

50x

Maintenance

100x

 

“Stable Requirements” are a myth, or at least, the developer’s equivalent to the Holy Grail. A situation where a customer will never change their minds is unheard of. A report concluded that, on an average system of 1million lines of code, 25% of the code would be changed.

 

The trick is to manage the customer. The author says “When customers get an idea for a new feature, their blood thins, they become excited and giddy, and completely forget the many meetings that they have had before. The best way to deal with them is to say ‘Gee that sounds like a good idea, I’ll write a specification and change control for it, and provide you with a revised schedule and cost estimate’. The words ‘Schedule’ and ‘Cost’ are much more sobering than a cup of black coffee and a cold shower under those circumstances”.

 

When presented with these circumstances, there are several options available.

 

The third option should not always be considered. However, an imaginary state must be identified, one that would justify dumping the project. Work out how close to these state things are.

 

2.4.     Architecture

 

The architecture provides a high level design of the system. This does not involve itself with such issues as screen layouts, report layouts and field definitions. The purpose of the architecture is to test the conceptual integrity of the system.

 

The main components of architecture are:

 

2.5.     Language

 

It is a good idea to define the language that is to be used to develop the software. Using the wrong tool for the job is an inefficient use of time. Use a language that will help to get the job done.

 

Using the right language increases productivity, as does using familiar languages. It has also been shown that using high-level languages is 5 times more productive as using low level languages. This is due to the nature of the languages.

 

2.6.     Programming Conventions

 

It is good practice to identify programming standards for a system. In-house development usually has it’s own set of standards. It is always easier to maintain a system with good programming standards used, than one that does not.

 

2.7.     Time to spend on Pre-Requisites

 

Project planning should typically take 20%-30% of the time. This does not include detailed design, this is done as part of development.

 

It is important to allow for uncertainty when planning. It is impossible to schedule time for tasks that are uncertain.

 

2.8.     Adapting Pre-Requisites

 

Every project is likely to be different, so it is up to the developer to decide how formal the pre-requisites should be, based on the current project.

 

2.9.     Summary

 

 

2.10.  Forwarding Actions

 

 

 

3.       Building a Routine

 

3.1.     Summary of Steps

 

Begin

Design the Routine         Check the Design

Check the Code              Code the routine

Done

 

3.2.     PDL for Pros

 

PDL stands for Program Design Language. This is similar to Pseudo Code, however PDL is more removed from the programming language level. When using PDL, use the following guidelines:

  

Benefits of using PDL:

 

3.3.     Design the Routine

 

 

3.4.     Code the Routine

 

 

3.5.     Formal Checking

 

 

3.6.     Summary

 

 

3.7.     Forwarding Actions

 

 

 

4.       High Quality Routines

 

4.1.     Valid Reasons to Create a Routine

 

   

4.2.     Good Routine Names

 

 

4.3.     Strong Cohesion

 

This means that each routine does one thing, and does it well. If it does more than one thing, then it may need to be split into other routines. If the name can clearly define what the routine does, then it is OK.

 

Acceptable Cohesion:

 

Unacceptable Cohesion:

 

4.4.     Loose Coupling

 

Coupling refers to the link between routines.

 

Good Coupling Criteria:

 

Levels of Coupling:

 

4.5.     How Long Can a Routine be?

 

This debate has been going on since routines were invented! Theoretically, 66 to 132 lines are sufficient. IBM once limited routine size to 50 lines. It is said that smaller is better, however, some reasons for larger routines are:

 

4.6.     Defensive Programming

 

This means that the routine will check any parameters that may be passed, to see if they are damaging. The routine will check, even though it is someone else’s fault that the data was bad.

 

How much defensive code to leave in a released version:

 

Be defensive about defensive programming. Otherwise programs will be slow.

 

4.7.     Use of Routine Parameters

 

Guidelines:

 

4.8.     Consider the use of Functions

 

Functions are routines that return a value. Include a check of the returning value as well as parameters.

 

4.9.     Summary

 

 

4.10.  Forwarding Actions

 

 

 

5.       Modules

 

Modules are a collective bundle of data structures and routines. These are essentially “black boxes” containing large chunks of reusable code.

 

5.1.     Modularity: Cohesion and Coupling

 

By following the rules of Cohesion and Coupling, “black box” routines can be created that are reusable, and reliable. If all similar operations within a large program use the same modular routines, then maintainability is increased. It has also been proven that system comprehension is made easier.

 

It is not always possible to create the perfect module, because there may be shared data between modules.

 

5.1.1.  Cohesion

 

Module Cohesion is similar to Routine Cohesion. Essentially, the principle of Modular Cohesion is to place together routines and data that belong together.

 

5.1.2.  Coupling

 

The ideal Module will allow clean interaction. If a module does not offer complete services, then the internal workings may need to be published. This turns the “black box” into a “white box” or a “glass box”.

 

Use routines to hide code, by using useful and appropriately abstract names.

 

5.2.     Information Hiding

 

Information Hiding is one of the most useful practices to make use of. The simple rules to follow are:

 

5.2.1.  Secrets and the Right to Privacy

 

A well-written module will be like an iceberg: only a small amount is visible from the surface.

 

5.2.2.  Common Secrets

 

These are common examples of module “secrets”, and how to minimize the effects.

 

5.2.2.1.    Volatile Areas that are likely to change

 

Accommodating change is always difficult. The goal is to isolate volatile areas so that only one module requires changes. Tips: 

 

Typical areas of change:

 

5.2.2.1.1.  Complex Data

 

Complex data structures are likely to change. Accessing the data via routines will reduce the impact outside of the module.

 

5.2.2.1.2. Complex Logic

 

Similar to the business rules section above, complex logic, such as nested “ifs”, can be bundled into a single Boolean function. Use “black box” routines.

 

5.2.2.1.3. Operations at the Programming Language Level

 

Convert several lines of code into a single function or routine. This will improve readability, and allow the section to be dealt with at a higher level of abstraction. Also, if error checking is required, the change only needs to be made in one place, rather than many more.

 

5.2.3.  Barriers to Information Hiding

 

These are mostly habitual practices from using other methods.

 

5.3.     Good Reasons to Create a Module

 

 

 

5.4.     Summary

 

 

5.5.     Forwarding Actions

 

Ensure guidelines are followed.

 

 

6.       High Level Design

 

6.1.     Introduction to Software Design

 

Levels of Design:

 

6.2.     Structured Design

 

Consists of:

 

6.2.1.  Choosing Components to Modularize

 

6.2.1.1. Top Down Decomposition

 

This methodology takes a system and builds it from the largest top levels, and expands into smaller sub-areas, using the old “Divide and Conquer” theme. The process is as follows:

 

6.2.1.2. Bottom Up Composition

 

This methodology builds a system from the functionality of low levels into larger modules:

 

6.2.1.3. Top Down vs. Bottom Up

 

 

The most important point is to use all methodologies, or parts of them, and not rely on one.

 

6.3.     Object-Oriented Design

 

6.3.1.  Key Ideas.

 

The main idea of OO design is to identify “Real World” and abstract objects. Replace these with Programming Language objects.

 

Ideas to bear in mind:

 

6.3.2.  Design Steps

 

 

6.3.3.  Typical Components

 

 

6.4.     Comments on Popular Methodologies

 

The most important thing to remember about methodologies is to never restrict yourself to one.

 

All methodologies have the following in common:

 

6.4.1.  When to use Structured Design

 

 

6.4.2.  When to use Information Hiding

 

As often as possible.

 

6.4.3.  When to use Object Oriented Design

 

6.5.     Round Trip Design

 

This means that the design is iterated until a satisfactory design is reached. Make use of all design methodologies.

 

“Design is a Sloppy Process”. This is because the right answer is hard to distinguish from the wrong one. Ask 3 people to design a system, and you will get 3 different designs. These design needs to be refined again and again.

 

 “Design is a Wicked Problem”. This is because it is sometimes necessary to solve the problem once in order to understand it fully, whereby a second design can be done.

 

6.6.     Design is a Heuristic

 

It is important to remember that the point of design is to help write the solution. This can be accomplished by using any number of methodologies. The following are useful heuristics:

 

6.7.     How to solve it

 

Remember the following:

 

6.8.     Summary

 

 

6.9.     Forwarding Actions

 

Discuss design methodologies used.

  

 

7.       Creating Data

 

7.1.     Reasons to create your own Data Types

 

 

7.2.     Guidelines for creating Data Types

 

 

7.3.     Making Variable Declarations Easy

 

 

7.4.     Guidelines for Initializing Data

 

Improper data initialization is very costly of debugging time. These are typical reasons for data initialization errors:

 

Useful tips:

 

7.5.     Summary

 

 

7.6.     Forwarding Actions

 

 

 

8.       The Power of Data Names

 

8.1.     Considerations in choosing good names

 

·         The optimum name length is suggested to be between 8 and 20 characters, typically about 16.

 

8.1.1.  The Effect of Scope on Variable names

 

Typically, longer names are better than shorter ones. This is not always the case: if a variable is to be used as a loop counter, it is acceptable to call it “a”, or “i”. The reason for this is that the variable is a temporary variable only, which will not exist outside of the scope of the procedure in which it is used.

 

If a variable is to be used throughout a module, then it should be named more meaningfully.

 

Basically, the length of a variable should reflect its importance.

 

8.1.2.  Computed-Value Qualifiers in Variable Names

 

Many programmers use prefixes to denote calculated values (e.g. Ttl, Sum, Avg). If using this approach, the important thing to remember is to be consistent.

 

8.2.     Naming Specific Types of Data

 

 

8.3.     The Power of Naming Conventions

 

·         Any naming convention is better than none at all.

 

8.4.     Informal Naming Conventions

 

Here are some guidelines for creating a language-independent naming convention: 

 

8.5.     Hungarian Notation

 

This naming convention consists of naming the variable in three parts:

 

This convention is widely used in C.

 

The main disadvantage with this is that the variables will never have abstract names.

 

8.6.     Short Names

 

When using short names, follow these guidelines:

 

It is noted that some programmers use phonetic names (before = b4), but I would not recommend this.

 

8.7.     Kinds of Names to avoid

 

 

8.8.     Summary

 

 

8.9.     Forwarding Actions

 

 

 

9.       General Issues in Using Variables

 

9.1.     Scope

 

The key is to minimize the scope of variables. If variables are global, the program is likely to be easier to write, but if they are not, the program will be easier to read.

 

Ensure that variable references are kept together.

 

9.2.     Persistence

 

 

9.3.     Binding Time

 

There are three types of data binding.

 

9.3.1.  Code Time Binding

 

This refers to hard-coded variables that are assigned values in the source code.

 

9.3.2.  Compiler Time Binding

 

This refers to variables that are assigned values from constants.

 

9.3.3.  Run Time Binding

 

This occurs when the program is running, and variables are assigned dynamic values.

 

9.4.     Relationship between Data Structures and Control Structures

 

 

9.5.     Use each Variable for one purpose only

 

 

9.6.     Global Variables

 

Global variables are tricky. They can be very useful, but also extremely risky to use.

 

9.6.1.  Common Problems

 

9.6.2.  Reasons to use Global Data

 

9.6.3.  How to Reduce the Risks of Using Global Data

 

9.6.4.  Use Access Routines instead of Global Data

 

Advantages are:

 

Advantages:

  

9.7.     Summary

 

 

9.8.     Forwarding Actions

 

None.

 

 

10.     Fundamental Data Types

 

10.1.  General Numbers

 

General things to remember:

 

 

10.2.  Integers

 

 

10.3.  Floating Point Numbers

 

 

10.4.  Characters and Strings

 

Use Named constants.

 

10.5.  Booleans

 

  

10.6.  Enumerations

 

 

10.7.  Named Constants

 

 

10.8.  Arrays

 

 

10.9.  Summary

 

 

10.10.            Forwarding Actions

 

 

  

11.     Organizing Straight Line Code.

 

11.1.  Statements that must be in a specific order

 

 

11.2.  Statements who's order does not matter

 

 

 

12.     Using Conditions

 

12.1.  IF statements

 

 

12.2.  IF chains

 

 

12.3.  CASE statements

 

 

12.4.  Tips

 

 

 

13. Controlling Loops.

 

13.1.  Select the kind of Loop to use.

 

 

When to use WHILE:

 

When to use EXIT DO:

 

When to use FOR/NEXT:

  

13.2.  Controlling the Loop.

 

When building a loop code block, use the same principles for building a routine.

Simplify the code.

 

Entry points:

 

Process the Middle of the Loop:

 

Exiting the Loop:

 

Checking End Points:

 

Using Loop Variables:

 

How long should a Loop be?

  

13.3.  Creating Loops form the inside out.

 

  

13.4.  Correspondence between loops and arrays

 

Use FOR EACH or other language functions where possible.

 

 

14.     Unusual Control Structures

 

14.1.  GOTO

 

  

14.2.  RETURN/EXIT

 

  

14.3.  Recursion

 

 

Tips:

 

 

15.     General Control Issues

 

15.1.  Boolean Expressions

 

 

Simplifying Boolean Expressions:

 

Formatting Boolean Expressions positively:

 

15.2.  Compound Statements

 

 

15.3.  NULL Statements

 

 

15.4.  Taming Dangerously Deep Nesting

 

If there is dangerous nesting, restructure the tests:

 

15.5.  The Power of Structured Programming

 

Have one entry point and one exit point.

 

Use three components:

  

15.6.  Control Structures and Complexity

 

Good design reduces complexity.

 

How to reduce complexity:

 

Measure complexity by using a count:

 

Evaluate Complexity:

 

Consider other ways of measuring complexity:

 

 

16.     Layout and Style

 

16.1.  Fundamentals

 

 

Objectives of good layout:

 

Layout preferences are largely subjective. Discuss styles with colleagues to come up with a standard.

 

16.2.  Layout Techniques

 

16.2.1. White Space

 

 

16.2.2.          Parentheses

 

 

16.3.  Layout Styles

 

16.3.1. Pure Blocks

 

These are where indentation is easily used to highlight the blocks:

 

IF ----- THEN

 ------

 ------

 ------

END IF

 

The IF appears leftmost, the conditional code is indented and the end of the condition is un-indented. Clear layout.

 

16.3.2. End-line Layout

 

This is a slightly different method:

 

IF ---- BEGIN

-----

-----

-----

END

 

This example shows what would happen if a language does not support explicit 'END IFs'.

Where the previous example clearly showed the bounds of the conditional code, this example shows the start of the conditional code, but does not clearly show the end.

It is possible to have a large number of statements within the conditional section. If some are separated by blank lines, then this method could be confusing.

Nested conditions and loops become confusing

 

Pure blocks can be emulated:

 

IF ---- BEGIN

 -----

 -----

 -----

END

 

16.3.3. BEGIN-END Block Boundaries

 

A substitute for pure blocks is to substitute BEGIN-END pairs as the boundaries of blocks:

 

IF ---- THEN

 BEGIN

 -----

 -----

 END

 

16.4.  Laying Out Control Structures

 

16.4.1.          Fine Points of Formatting Control Structures

 

Avoid Un-indented BEGIN-ENDs.

Avoid Double Indentation:

 

IF --------- THEN

 BEGIN

 -----

 -----

 END

END IF

 

16.4.2.          Other Considerations.

 

 

16.5.  Laying Out Individual Statements

 

16.5.1.          Statement Length

 

Typically, 80 characters is said to be the longest length, for the following reasons:

Also:

 

16.5.2. Using spaces for clarity

 

 

16.5.3.          Aligning Related Statements

 

By aligning the '=' clause of a number of similar or related assignments, their relationship is implied.

 

16.5.4.          Format Continuation Lines

 

 

16.5.5. Use Only One Statement Per Line

 

Don't use multiple statements per line, in languages that support it.

 

16.5.6.          Laying Out Data Declarations

 

 

16.6.  Laying Out Comments.

 

 

16.7.  Laying Out Routines.

 

·         Use standard indentation for routine arguments.

 

16.8.  Laying Out File, Modules and Programs.

 

 

16.9.  Summary.

 

The purpose of layout is to make code reading easier. The reader may be another person, or yourself re-reading code after a long period of time. Code that is easy to read is easy to understand, and generally makes life easier for the reader.

 

16.10.            Forwarding Actions

 

Establish layout guidelines and encourage adherence to them.

 

 

17.     Self-Documenting Code.

 

17.1.  External Documentation.

 

These are common types of documentation:

 

 

17.2.  Programming Styles as Documentation.

 

By using good Naming Standards and Commenting, then code listings can be a useful form of documentation.

 

17.3.  Commenting.

 

This provides a higher level of abstraction, to the point where just reading the comments should tell the reader what the routine accomplishes, without the need to understand the source code.

 

17.3.1.          Types of Comments

 

 

17.3.2.          Commenting Efficiency

 

Follow these guidelines to make commenting efficient:

 

17.4.  Commenting Techniques.

 

17.4.1.          Individual Lines.

 

Avoid Self-Indulgent Comments. Anything that is not pertinent is better off not being there.

 

Using End-line Comments:

 

When to use single line comments:

 

17.4.2.          Commenting Paragraphs

 

Use these guidelines when commenting blocks of code:

 

Most importantly, don't spend time commenting bad code. Rewrite it.

 

17.4.3.          Commenting Data Declarations

 

 

17.4.4.          Commenting Control Structures

 

 

17.4.5. Commenting Routines

 

 

17.4.6.          Commenting Files, Modules and Programs

 

General guidelines:

 

17.4.7.          Using the "Book" Paradigm for commenting

 

As with a book, commented code can include the following equivalents:

 

17.5.  Summary

 

Commenting Code is very useful as documentation. By reading the comments in a code listing, the purpose and functionality of the routines can be identified abstractly.

 

17.6.  Forwarding Actions

 

Agree on a commenting methodology.

 

 

18.     Programming Tools

 

18.1.  Design Tools

 

The main types of design tools available are:

 

18.2.  Source Code Tools

 

Old days of computer programming consisted of writing code in very basic text editors, and then submitting the source code to compilers, and then going through linking stages and eventually arriving at the end with a packaged executable application. Nowadays, the coding process is easier.

 

18.2.1.          Editing

 

Editors now provide a wide range of useful features:

 

18.2.2.          Browsing

 

Browsers are utilities that can look through multiple files, and find references to a particular variable, or function call. Examples of these are:

 

18.2.3.          Analyzing Code Quality

 

Syntax and Semantic checkers.

 

These look for common mistakes, such as detecting:

            IF (X = Y)

Rather than

            If (X == Y)

 

Metrics Reporters.

 

These produce a large amount of information to measure aspects of a program. From number of variable declarations, right through to which programmers produce the most errors.

 

18.2.4.          Restructuring Source Code

 

Two main types of tools are available:

 

18.2.5.          Version Control

 

Any software development that is being worked on by more than one person should be using a version control system. Even if not, Version control provides advantages:

 

18.2.6.          Data Dictionaries

 

These are databases of all variables, file definitions and other data relevant to a program.

 

18.3.  Executable Code Tools

 

18.3.1.          Code Creation.

 

 

18.3.2.          Debugging.

 

 

18.3.3.          Testing.

 

 

18.3.4.          Code Tuning.

 

 

18.4.  Tool-Orientated environments.

 

UNIX. This is a programmer's operating system. It has lots of development tools.

 

CASE. These are promoted as being helpful for the development process, however, any one tool will never support the complete development lifecycle.

 

APSE. Ada-Programming-Support-Environment. Consists of:

 

18.5.  Building your own tools.

 

Ask the average programmer that if he could do a job in 5 hours or write a tool in 4 3/4 hours that could perform the job in 15 minutes. They would typically opt for the tool. It is better to write tools once that can perform repeated jobs. There are several kinds of tools that are generally written:

 

18.6.  Summary

 

 

18.7.  Forwarding Actions

 

CASPIAN makes use of development tools. I would be interested in seeing the output from a Disassembler, but this is purely an academic interest, and not of any practical use at the moment.

 

 

19.     How Program size Affects Construction

 

19.1.  Effect of Project Size on Development Activities

 

·         Where there are larger teams, there are more communication paths. This means that there are more possibilities for communications errors.

·         It is best to streamline communications, such as by using documents.

·         In smaller projects, the construction takes up a larger percentage of the time than in a larger project. Larger projects are managed more formally.

 

19.2.  Effect of Project Size on Errors

 

It is usually the case that the larger a project, in terms of lines of code, the greater the error density.

 

19.3.  Effect of Project Size on Productivity

 

 

 

20.     Managing Construction

 

20.1.  Encouraging Good Coding.

 

20.1.1.          Considering in Setting Standards.

 

Don't! Programmers usually resent standards. Instead, set guidelines or suggestions.

 

20.1.2.          Techniques.

 

 

20.2.  Configuration Management

 

20.2.1.          What is configuration Management?

 

Change control. If changes aren't managed well enough, then problems will occur.

 

20.2.2.          Software Design Changes

 

Use the following methods:

 

 

20.2.3.          Software Code changes

 

  

20.3.  Estimating a Construction Schedule

 

20.3.1.          Approaches

 

 

20.3.2.          Establish Objectives

 

 

20.3.3.          Influences on Schedule

 

 

20.3.4.          Estimation vs. Control

 

Once a project is estimated, you must be able to keep up with it.

 

20.3.5.          What to do if you are behind

 

There are several options:

 

21.     Software Metrics.

 

The key is that any measurement is better than none at all. Not measuring means not knowing what is going on.

 

21.1.  Treating Programmers as people

 

It is important to remember that programmers are not simply tools. Treating them well results in better performance. Programmers do not just spend their time coding.

 

21.2.  Variations in performance and quality

 

Individual Performance. Experience does not always mean better performance and productivity.

Team Variation. It has been shown that good programmers work better together, than a mixture of good and bad.

 

21.3.  Religious Issues

 

Programmers develop their own style, and trying to force compliance with certain areas could incite resentment.

Here are typical religious areas:

 

If you need to control programmers, consider the following:

 

21.4.  Physical Environment

 

This affects productivity as well. If people are uncomfortable or interrupted, the performance will be lower.

  

If the manager is being awkward, use the following approaches:

 

It is best to try to educate the manager.

 

21.5.  Summary

 

Any measurement is better than none at all.

 

22.     The Software Quality Landscape

 

22.1.  Characteristics of Software Quality

 

System quality:

 

Programming Quality:

 

22.2.  Techniques for improving Software Quality

 

 

Setting Objectives.

 

22.3.  Relative Effectiveness of techniques

 

22.3.1.          Percentage of Errors found

 

There are several methods to identify defects:

 

It is very important to note that each of these methods can detect a certain number of defects. However, by combining any two methods, the number of defects found is increased dramatically. Hence, it is best to combine several methods of checking.

 

22.3.2.          Cost of finding defects

 

 

22.3.3.          Cost of fixing defects

 

 

22.4.  When to do a QA

 

Ensure that QA is done at all stages of construction.

 

22.5.  General Principle of Software Quality

 

Improving Quality reduces cost.

 

22.6.  Summary

 

 

22.7.  Forwarding Actions

 

Establish quality standards.

 

23.     Reviews

 

23.1.  The role of reviews

 

23.1.1.          Reviews complement other QA techniques

 

 

23.1.2.          Reviews remove corporate structure.

 

This helps to guide young programmers with senior programmers experience.

 

23.1.3.          Reviews assess Quality and Progress.

 

These assess from a managerial level where things are up to.

Also, from a technical point of view, the way things are being done can be assessed.

 

23.1.4.          Reviews also apply before construction.

 

Use reviews when designing and planning as well.

 

23.2.  Inspections.

 

These are different from reviews in several ways:

 

Inspections can typically expect to find up to 60% of defects.

 

23.2.1.          Roles During Inspections

 

 

23.2.2.          Procedure for Inspections

 

 

23.3.  Other kinds of reviews

 

23.3.1.          Walkthroughs.

 

 

These are more informal than inspections, and can be preferable. However the inspection typically pays of better, due to the formality.

 

23.3.2.          Code Reading.

 

The author gives about 2-4000 lines of code to two separate people. These three then meet and discuss the errors. With his kind of review, most of the errors are found during the preparation, rather than in the meeting.

 

23.3.3.          "Dog and Pony shows"

 

These are reviews that are for the benefit of management. These are not technical.

 

 

24.     Unit Testing

 

24.1.  The Role of Unit Testing

 

The intention is to break the software. Testing cannot prove the absence of errors.

Testing is not meant to improve the quality of software. It is used to measure it.

You must expect to find errors. If you are convinced that errors do not exist, then you may consciously or subconsciously avoid weak areas.

 

24.2.  Testing During Construction

 

Testing in this stage should consist of:

 

24.3.  The Testing Bag of Tricks

 

24.3.1.          Incomplete Testing

 

This means that the test cases are selected so that they don't produce duplicate results. This is done to make the most use of the time available.

Test likely errors. Decide what is most likely to produce errors, and test those areas.

 

24.3.2.          Structured Basis Testing

 

This is like "White Box" testing. That is to say, the test cases are selected in order to test every part of the logic. As the logic is known to the tester, all logical paths through the system can be tested.

 

24.3.3.          Data Flow Testing

 

Data and variables can be defined as being in the following states:

 

Check combinations of these states to see if for example, a variable is being assigned a value before it is defined.

 

24.3.4.          Equivalence Partitioning

 

If two test cases are going to produce exactly the same results, then only one of these is necessary.

 

24.3.5.          Error Guessing

 

Use experience to guess at which areas are likely to contain errors. This is not advised for very inexperienced programmers.

 

24.3.6.          Boundary Analysis

 

Write test cases that test the boundaries of variables. That is to say, test the maximum and minimum allowed values. Test disallowed values as well, and see what the results are. These tests identify 'off by one' errors.

 

24.3.7.          Classes of Bad Data

 

Bad data can be defined in the following ways:

 

These can be used to test error control.

 

24.3.8.          Classes of Good Data

 

These are nice values that test the normal functionality:

 

24.3.9.          Use test cases that allow easy manual checks.

 

If arithmetic operations are being performed, use cases that are easy to manually calculate as a check.

 

24.4.  Typical Errors

 

24.4.1.          Which routines contain the most errors?

 

Errors are not evenly distributed throughout the code. They usually collect together in certain places.

 

24.4.2. Errors by Classification

 

 

24.4.3.          Proportion of Errors Resulting from faulty construction

 

Implementation Errors constitute up to 40% of all errors.

Construction Errors are costly to fix.

 

24.4.4.          How many errors should you expect to find

 

 

24.4.5.          Testing itself

 

 

24.5.  Test Support Tools

 

24.5.1.          Scaffolding

 

This can be creating a low-level routine that does not do much at all, but is used to test a high level routine. This allows you to take certain functionality for granted.

 

24.5.2.          Results Comparators

 

These are tools that compare text output files to see if they are identical.

 

24.5.3.          Test Data Generators

 

 

24.5.4. Coverage Monitors

 

These can show what code is executed, and highlighting the most used code.

 

24.5.5. Symbolic Debuggers

 

These allow a compiled program to be stepped through.

 

24.5.6.          System Perturbers

 

These are tools such as:

 

24.5.7.          Error Databases.

 

These are information repositories that are maintained by testers. Use them as knowledge bases.

 

24.6.  Improving Testing

 

24.7.  Planning to test

 

It is important to allow for testing when the project is being planned.

 

24.7.1. Re-testing

 

This means that when code is changed, re run the old test cases as well as running new ones.

 

24.8.  Keeping Test Records

 

By precisely recording testing results and errors found, it can be seen whether or not changes damage the project.

 

24.9.  Summary

 

 

24.10.            Forwarding Actions

 

 

 

25.     Debugging

 

3.1 Overview of Issues

 

Bugs. The original term "Bug" was used when the first large-scale digital computer was first used. An error was traced to a moth in the circuitry.

 

25.0.1.          Role of Debugging

 

This is a last resort. There should be no bugs in a properly designed, coded and tested system.

 

25.0.2.          Variations in Debugging Performance

 

Variations can be measured by time spent, number of errors found, and number of errors caused. Not everybody can debug well.

 

25.0.3.          Errors as Opportunities

 

Opportunities can arise such as:

 

25.0.4.          An Ineffective approach

 

The "Devil's Guide to Debugging" is as follows:

 

25.0.5.          Debugging by superstition

 

The wrong attitude to take is the "Not my Fault" position. It is better to take responsibility for an error, until you can prove that it is someone else's fault.

 

25.1.  Finding an Error

 

25.1.1.          Use a Scientific Method

 

1.      Gather data and get repeatable results.

2.      Form a hypothesis.

3.      Experiment to prove the hypothesis.

4.      Prove or disprove the hypothesis.

5.      Repeat until a hypothesis is proven.

 

Alternatively:

1.      Stabilize the error.

2.      Locate the source of the error.

3.      Fix the error.

4.      Test the fix.

5.      Look for similar errors.

 

25.1.2.          Tips on Finding Errors

 

 

25.1.3.          Syntax Errors

 

 

25.2.  Fixing an Error

 

 

25.3.  Psychological Considerations

 

Paris in the spring.

 

Did you notice the second "the" in the previous sentence? The lesson here is that you can see what you expect to see, not what really is.

 

25.4.  Debugging Tools

 

 

25.5.  Summary

 

  

 

26.     System Integration

 

26.1.  Importance of the Integration Method

 

It is important to integrate in the right order. Systems can become unstable if components are added in the wrong order.

The system should be self-supporting at each stage of integration.

Integration is not just about testing.

 

26.2.  Phased vs. Incremental Integration.

 

26.2.1.          Phased Integration

 

Phased Integration happens like so:

1.            Develop all the routines

2.            Unit Test all the routines.

3.            Add all of the routines to the system.

4.            Test and debug the whole system.

 

There are inherent problems with this approach, especially the fact that if errors arise, it is hard to identify which routine(s) contain the errors.

 

26.2.2.          Incremental Integration

 

Incremental Integration happens differently:

1.            Develop the minimal system in order to function.

2.            Develop and test a routine.

3.            Add the one routine to the system.

 

26.2.3.          Benefits of Incremental Integration

 

 

26.3.  Incremental Integration strategies

 

26.3.1.          Top Down Integration

 

 

26.3.2.          Bottom Up Integration

 

 

26.3.3.          Sandwich Integration

 

This method means that the low-level routines and the top level ones are written first.

Afterwards, the in-between routines are written.

 

26.3.4.          Risk Orientated Integration

 

This method is where the hard part of the system is written first, the part that contains the most risks.

 

26.3.5.          Feature Orientated Integration

 

This is similar to the top down method, but instead, the system is written in paths.

 

26.4.  Evolutionary Delivery

 

26.4.1.          General Approach

 

Evolutionary Delivery is all about delivering in stages, not waiting until the whole system is written.

This method allows for changes along the way, not all at one at the end.

 

26.4.2.Benefits

 

 

26.4.2.          Relationship of Evolutionary Delivery to Prototyping

 

Prototyping is an exploratory exercise.

 

26.4.3.          Limitations

 

There are drawbacks:

 

26.5.  Summary

 

Incremental Integration is a much better approach than phased integration.

 

26.6.  Forwarding Actions

 

Use the accretion method for integration, as mentioned much earlier.

 

27.     Code Tuning Strategies

 

27.1.  Performance Overview

 

27.1.1.          Quality Characteristics and Performance

 

Performance does not always mean speed. There are other qualities to consider.

 

27.1.2.          Performance and Code Tuning

 

Think about efficiency from these viewpoints:

 

27.2.  Introduction to Code Tuning

 

27.2.1.          Old Wives' Tales

 

There are many false beliefs about performance:

 

27.2.2.          The Pareto Principle

 

80% of the system can be done with 20% of the effort. This is the same with completed systems. Some areas are used more than others. It is these hotspots that you should concentrate on improving.

 

27.2.3.          Measurement.

 

It is not possible to simply guess how efficient code is it has to be measured.

Measurements must be precise.

Sometimes optimizations don't make code any faster, but make the code harder to read.

 

27.2.4.          Compiler Optimizations

 

Compiler settings can allow the compilers to optimize simple code.

 

27.2.5.          When to use Code Tuning

 

Only optimize code if it is absolutely necessary, and do so at the end of the project.

 

27.2.6.          Iteration

 

If a piece of code is optimized by a small amount, but is used again and again in the program, the cumulative effect will be grater.

 

27.3.  Common Sources of Inefficiency

 

 

27.4.  Summary of Approach to Code Tuning

 

1.            Use highly modular designs that are easy to understand.

2.            Find the hotspots in the code.

3.            Find the cause of the weak performance, is code tuning required?

4.            Tune the bottleneck. If there is no improvement in performance, then undo the change.

5.            Repeat from step 2.

 

27.5.  Summary

 

Only tune code when it is absolutely necessary.

 

 

28.     Code Tuning Techniques

 

28.1.  Loops

 

 

28.2.  Logic

 

 

28.3.  Data Transformation

 

 

28.4.  Expressions.

 

 

28.5.  Routines

 

Rewrite the code in-line.

 

28.6.  Re-Code in Assembler

 

Sometimes, the only way to dramatically improve performance is to rewrite the program in assembly code.

Follow these steps:

1.            Write the program in a high level language.

2.            Test the program and ensure that it is correct.

3.            Identify the hotspots.

4.            Re-code the hotspots in assembler.

 

This approach works well if the program follows a modular design.

 

 

29.     Software Evolution

 

29.1.  Guidelines

 

These will help to improve the quality of the software during its evolution:

 

The philosophy is to use your experience to improve the software.

 

29.2.  Making New Routines

 

 

 

30.     Themes in Software Craftsmanship

 

30.1.  Conquer Complexity

 

30.1.1.          Ways to reduce complexity

 

Divide the system into subsystems.

Move complex tests into Boolean functions.

 

30.1.2.          Hierarchies and Complexity

 

It is natural for the brain to see hierarchies, it allow you to not worry about all levels at once.

 

30.1.3.          Abstraction and Complexity

 

Use abstract naming in order to achieve this.

 

30.1.4.          Pick your Process

 

Define your own working methods and stick to them.

 

30.2.  Write programs for people first, and computers second

 

Readability has positive effects on these aspects of a program:

 

30.3.  Focus your attention with the help of conventions

 

The conventions will have been defined in order to produce quality. Adhering to these will help to produce high quality.

 

30.4.  Programming in terms of the problem domain

 

Making use of abstraction can do this. Not dealing with technical solutions.

 

30.5.  Watch for "Falling Rocks"

 

Look for anything that is unstable. Look for low quality routines and rewrite them.

 

30.6.  Iterate

 

Iterate early on, by using prototypes.

This method costs less.

 

30.7.  "Thou Shalt Render Religion and Software Asunder"

 

30.7.1.          Software Oracles

 

These are people who claim that their way of doing things is better than any other. Don't believe that anything is better until it is proven to be so.

 

30.7.2.          Eclecticism

 

These are people who cling to a single way of doing things. A good programmer will build up an intellectual toolbox of different methodologies.

 

30.7.3.          Experimentation

 

It is worth experimenting with new ideas all the way through the development process. This is necessary in order to expand. If you are not willing to change your beliefs after an experiment, then the experiment is pointless.

The important thing is to not be afraid of making mistakes.

 

 

© John Mann, 2000