Code Complete Book Review (5 of 7)

As I continue this review of Code Complete by Steve McConnell, we look at techniques to improve our code quality. As I read through, many of these problems are addressed by modern programming techniques including XP and Agile. These techniques have been around much longer and will persist after these movements have passed as well.

  • Code Improvements
    • The Software Quality Landscape
      • Characteristics of Software Quality: Software has both external and internal quality characteristics. External characteristics are characteristics that a user is aware of including Usability, Efficiency and Reliability. Programmers care about the internal characteristics of the software as well as the external ones. Some internal characteristics include Readability, Maintainability and Flexibility. Sometimes, these features correlate, increasing Integrity increases Reliability. In other cases, they work against each other, increasing efficiency can decrease maintainability.
      • Techniques for Improving Software Quality: The most important technique for improving software quality is by setting explicit quality objectives. Research was done on meeting objectives ("Goals and Performance in Computer Programming" (Weinberg and Schulman 1974). In every case the team focused on their objective and was ranked highly on their objective.
      • Relative Effectiveness of Quality Techniques: There are many techniques to improve code quality including
        1. Design Review
        2. Code Inspection
        3. Unit Testing
        4. Integration Testing
        5. Beta Testing

        Some of these techniques are more effective than others, but no technique has been found to detect more than 75% of all defects. If we want to increase the quality of our software products we need to use several of these techniques in tandem.

      • The General Principle of Software Quality: Debugging and rework account for 50% of development time. Studies done at IBM and NASA confirm that increased quality accounts for a lower defect rate, but not higher cost.
    • Collaborative Construction
      • Collaborative Construction covers a wide range of techniques to improve code quality. These techniques include pair programming, code reviews and formal inspection. In addition to sharing technical expertise, Collaborative construction techniques offer a chance to create and/or transmit corporate culture.
      • Pair Programming: Checklist for effective Pair Programming
      • Inspections were developed by Michael Fagan and used at IBM for several years before Fagan published the paper that made them public. A formal inspection has set roles including the author, moderator, reviewer and scribe. In addition, there is a standard process of Planning, Preparation, Inspection Meeting, Inspection Report, Rework and Follow up. Checklist for effective Formal Inspections
      • Other approaches include Walkthroughs and code reading. Walkthroughs are informal code inspections. Organizations that choose that approach find the benefits are lower than the formal code review with a similar cost. Code reading provides a reviewer with a print out of the code. The reader reviews the code and submits it back to the author for updates. No meeting is necessary.
    • Developer Testing
      • Role of Developer Testing in Software Quality: Testing can never improve the quality of a software product. It can only act as an indicator for the quality. Some ways developers can test their code include
        • Unit testing is the execution of a complete class, routine, or small program that has been written by a single programmer or team of programmers, which is tested in isolation from the more complete system.
        • Component testing is the execution of a class, package, small program, or other program element that involves the work of multiple programmers or programming teams, which is tested in isolation from the more complete system.
        • Integration testing is the combined execution of two or more classes, packages, components, or subsystems that have been created by multiple programmers or programming teams. This kind of testing typically starts as soon as there are two classes to test and continues until the entire system is complete.
        • Regression testing is the repetition of previously executed test cases for the purpose of finding defects in software that previously passed the same set of tests.
        • System testing is the execution of the software in its final configuration, including integration with other software and hardware systems. It tests for security, performance, resource loss, timing problems, and other issues that can’t be tested at lower levels of integration.
          • It is recommended that developers write tests before they write code. This doesn’t take any extra work, we are simply changing the sequence in which the work is performed. This has the added benefit of catching design and architecture flaws earlier.
          • When writing tests, developers need ton consider logic flow (if, then, while, switch, case and goto statements. Data flow is just as important and developers need to test with initialized and uninitialized data.
          • Checklist for writing test cases
        • Debugging
          • Structured Debugging is a required skill for developers who are going to keep a system in production for any length of time. The debugging process will look a lot like the scientific process.
            1. Stabilize the error
            2. Locate the source of the error (the "fault").
              1. Gather the data that produces the defect.
              2. Analyze the data that has been gathered, and form a hypothesis about the defect.
              3. Determine how to prove or disprove the hypothesis, either by testing the program or by examining the code.
              4. Prove or disprove the hypothesis by using the procedure identified above.
            3. Fix the defect.
            4. Test the fix.
            5. Look for similar errors.

            Another approach is called brute force debugging. this can be time-consuming, but it is guaranteed to find bugs. Some approaches include

            • Perform a full design and/or code review on the broken code.
            • Throw away the section of code and redesign/recode it from scratch.
            • Throw away the whole program and redesign/recode it from scratch.
            • Compile code with full debugging information.
            • Compile code at pickiest warning level and fix all the picky compiler warnings.
            • Strap on a unit test harness and test the new code in isolation.
            • Create an automated test suite and run it all night.
            • Step through a big loop in the debugger manually until you get to the error condition.
            • Instrument the code with print, display, or other logging statements.
            • Compile the code with a different compiler.
            • Compile and run the program in a different environment.
            • Link or run the code against special libraries or execution environments that produce warnings when code is used incorrectly.
            • Replicate the end-user’s full machine configuration.
            • Integrate new code in small pieces, fully testing each piece as it’s integrated.
          • Debugging Checklist
        • Refactoring
          • Many times we assume that a perfect project has an elegant design before a single line of code gets laid down. However, software is constantly evolving due to changing requirements, technology, and maintenance. We can take advantage of this to continually improve the design of our software. Some reasons to refactor are included.
          • Refactoring can happen at the following levels
            • Data level
            • Statement level
            • Routine Level
            • Class Interface Level
            • Class Implementation Level
            • System Level
          • Checklist of Refactorings
          • Checklist for Safe Refactoring
        • Code Tuning Strategies
          • Remember the Pareto Principle (80/20 rule) when making optimizations. Improvements in small areas of your code will make the biggest improvements in speed. If you have been keeping your design modular, you can swap out slow operations for faster ones.
          • System calls and I/O can be very expensive. Examine routines that use these and ensure that you need these operations, if you can use a simpler operation, you can save time and overhead.
          • When tuning your code the first thing to do is measure. This allows you to identify bottlenecks and compare different resolutions to choose the best.
          • Your code tuning workflow should look like this:
            1. Develop the software by using well-designed code that’s easy to understand and modify.
            2. If performance is poor,
              1. Save a working version of the code so that you can get back to the "last known good state."
              2. Measure the system to find hot spots.
              3. Determine whether the weak performance comes from inadequate design, data types, or algorithms and whether code tuning is appropriate. If code tuning isn’t appropriate, go back to step 1.
              4. Tune the bottleneck identified above
              5. Measure each improvement one at a time.
              6. If an improvement doesn’t improve the code, revert to the code saved earlier. (Typically, more than half the attempted tunings will produce only a negligible improvement in performance or degrade performance.)
            3. Repeat from step 2.
          • Code Tuning Checklist

This material is copied and/or adapted from the Code Complete 2 Website at This material is Copyright © 1993-2007 Steven C. McConnell. Permission is hereby given to copy, adapt, and distribute this material as long as this notice is included on all such materials and the materials are not sold, licensed, or otherwise distributed for commercial gain.

Leave a Reply

Name *
Email *