02 - Software Construction
Class: CSCE-331
Notes:
1.1 What is Software Construction?
Definition and Metaphor
- Common Usage: Outside of software, "construction" refers to the hands-on process of building something (e.g., a house or skyscraper).
- Software Context: In software, construction focuses on coding and debugging but also encompasses detailed design, unit testing, integration testing, and other related activities.
- Creativity vs. Translation: Construction is not purely mechanical. While often called "coding," this term implies a simple, mechanical translation of design into language; however, construction actually requires substantial creativity and judgment.
- Terminology: The book uses the terms "construction" and "programming" interchangeably.
The Place of Construction in Software Development Software development involves numerous distinct activities. Construction is the central activity, often sandwiched between requirements/architecture and system testing,.
The activities in the software development process include:
- Problem definition
- Requirements development
- Construction planning
- Software architecture (high-level design)
- Detailed design (Part of construction)
- Coding and debugging (Part of construction)
- Unit testing (Part of construction)
- Integration testing (Part of construction)
- Integration (Part of construction)
- System testing
- Corrective maintenance,
Specific Construction Tasks Construction is not a single action but a collection of specific tasks, including:
- Verifying groundwork: Ensuring prerequisites have been met so construction can succeed.
- Designing: Creating and writing classes and routines.
- Planning testing: Determining how code will be tested.
- Data creation: Creating and naming variables and named constants.
- Logic implementation: Selecting control structures and organizing blocks of statements.
- Verification: Unit testing, integration testing, and debugging code.
- Refinement: Polishing code via formatting and commenting.
- Collaboration: Reviewing other team members' code and designs.
- Integration: Combining separately created software components.
- Optimization: Tuning code for speed and resource usage.
What Construction is NOT While construction is central, it is distinct from other major software development phases. Important non-construction activities include:
- Management
- Requirements development
- Software architecture
- User-interface design
- System testing
- Maintenance
Key Takeaway If software development were a dog, it would "nuzzle up to construction, wag its tail at design and testing, and bark at the other development activities". Construction is the hands-on implementation phase where the rubber meets the road.
Here are the comprehensive notes for Chapter 1.2: Why Is Software Construction Important?, continuing the format from the previous section.
1.2 Why Is Software Construction Important?
1. Construction comprises a large portion of software development
- Depending on the size of the project, construction typically consumes between 30 and 80 percent of the total time spent on a project.
- Because it consumes so much time, the quality and efficiency of construction inevitably have a substantial impact on the project's overall success.
2. Construction is the central activity
- Construction is positioned centrally in the software development life cycle.
- Requirements and architecture are performed before construction to ensure effective implementation.
- System testing is performed after construction to verify the correctness of the implementation.
3. Productivity varies significantly during construction
- Individual programmer productivity during construction can vary by a factor of 10 to 20.
- Applying the techniques used by the best programmers can significantly improve the productivity of any developer.
4. Source code is often the only accurate description of the software
- On many projects, requirements and design documents go out of date, but the source code is always up to date.
- Consequently, the source code must be of the highest possible quality. It acts as the detailed definition of the product.
- Consistent application of improvement techniques ensures the code is a detailed and informative description of the program rather than a messy "Rube Goldberg contraption".
5. Construction is the only activity guaranteed to be done
- While ideal projects include careful requirements, architecture, and testing, real-world projects often skip or abbreviate these steps due to time constraints.
- However, construction cannot be skipped; if there is a program, there must be construction.
- Because construction is the one activity that is always performed, improving construction practices is a guaranteed way to improve any software development effort, regardless of how abbreviated the rest of the process is.
3.1 Importance of Prerequisites
The Role of Preparation in Quality
- Quality Strategies: High-quality software requires attention to quality at the beginning, middle, and end of a project.
- At the End (Testing): Testing is often what people think of first, but it cannot detect flaws like building the wrong product.
- In the Middle (Construction): This is the focus of the book, but the groundwork is laid earlier.
- At the Beginning (Prerequisites): You must plan for a high-quality product. "If you start the process with designs for a Pontiac Aztek, you can test it all you want to, and it will never turn into a Rolls-Royce."
- Risk Reduction: The overarching goal of preparation is risk reduction. Good planning clears major risks (like poor requirements) out of the way so construction can proceed smoothly.
Barriers to Preparation Three common reasons programmers do not prepare adequately before coding:
- Lack of Expertise: Many developers lack training in upstream activities (requirements, planning, architecture) and therefore do not know how to perform them effectively.
- The "WISCA" Syndrome: "Why Isn't Sam Coding Anything?" There is often pressure from management to see immediate coding, as planning is not perceived as "work".
- The Urge to Code: Programmers often prefer the immediate gratification of coding over the abstract work of planning.
The Argument for Prerequisites When justifying preparation time to management, three appeals are effective:
- Appeal to Logic: It is cheaper to figure out what you want to build before you build it than to build the wrong thing, throw it away, and start over.
- Appeal to Analogy: You wouldn't start building a house without blueprints. In software, the "chain" is: Architect
Designer Coder. If the early links are weak, the construction fails. - Appeal to Data: It is significantly cheaper to fix defects early.
- The Cost of Rework: Purging an error by the beginning of construction is 10 to 100 times less expensive than fixing it during system testing or after release.
- The "Software Food Chain": Defects introduced early (requirements/architecture) tend to stay in the system longer and cause more damage than coding errors.
Cost of Fixing Defects (Data) The cost to fix a defect rises dramatically the later it is detected:
- Requirements Defect:
- Fixed during Requirements phase: 1x cost.
- Fixed during System Test: 10x cost.
- Fixed Post-Release: 10–100x cost.
- Time Spent: The average project spends about 50% of its time on debugging and rework. Fixing defects upstream can cut development schedules by a factor of two or more.
Applicability to Modern/Iterative Projects
- Misconception: Some believe upstream activities (architecture/design) aren't needed in iterative or agile projects.
- Reality: Prerequisites apply to all projects, though the timing differs.
- Sequential: Prerequisites are done largely up-front.
- Iterative: Prerequisites are done just-in-time for each iteration. You must identify critical requirements and architecture for the piece you are constructing before you construct it.
The "Boss-Readiness" Test To determine if a project is ready for construction, avoid self-fulfilling prophecies like "We'd better start coding right away because we're going to have a lot of debugging to do." Instead, aim for the state where: "We’ve investigated requirements and design so much that I can’t think of any major problems we’ll run into during coding or debugging.".
3.3 Problem-definition Prerequisite
Concept and Terminology
- The "Box" Metaphor: Before thinking "outside the box," you must first find the box. The problem definition identifies the boundary of constraints and conditions.
- Alternative Names: This activity is also known as "product vision," "vision statement," "mission statement," or "product definition."
What is a Problem Definition?
- Definition: It is a clear statement of what the problem is without reference to possible solutions.,
- Tone: It should sound like a problem, not a solution.
- Good Example: "We can't keep up with orders for the Gigatron."
- Bad Example: "We need to optimize our automated data-entry system to keep up with orders." (This sounds like a solution).
- Timing: The problem definition lays the foundation for the rest of the process and must occur before detailed requirements work.
The User’s Point of View
- Language: The definition should be written in the user's language from the user's perspective, not in technical computer terms.
- The Trap of the Programmer Mindset: If the problem is defined in computer terms, programmers tend to look for computer solutions, even if a non-software solution is better.
- Example: If the problem is simply "We need a report on annual profit," the best solution might be a secretary with a calculator rather than developing a complex software extension to aggregate quarterly reports.
- Exception: It is appropriate to state the problem in technical terms only when the problem lies specifically with the computer or tools (e.g., "compile times are too slow").
The Penalty of Neglect
- Aiming: Without a good problem definition, you might aim at the wrong target.
- Double-Barreled Penalty: Failing to define the problem results in wasting time solving the wrong problem while simultaneously failing to solve the right problem.
3.4 Requirements Prerequisite
Definition and Terminology
- Definition: Requirements describe in detail what a software system is supposed to do. They are the first step toward a solution.
- Alternative Names: This activity is variously known as "requirements development," "requirements analysis," "specification," "functional spec," or simply "spec".
Why Have Official Requirements? Explicit requirements are crucial for several reasons:
- User-Driven Functionality: They ensure the user, not the programmer, drives the system's functionality. Without explicit requirements, programmers often guess at what users want.
- Conflict Resolution: They help avoid arguments by deciding the scope of the system before programming begins. Disagreements can be resolved by reviewing the written agreement.
- Minimizing Changes: Fixing a requirements error during the requirements phase is significantly cheaper than fixing it later.
- Cost Data: An error detected during coding is 5–10 times more expensive to fix than one detected during requirements. If detected post-release, it is 10–100 times more expensive.
- Impact: Specifying requirements adequately is a key to project success, potentially more important than effective construction techniques.
The Myth of Stable Requirements
- The Reality of Change: Stable requirements are the "holy grail" of development, but they are rarely achieved. On average, a project experiences about 25% change in requirements during development.
- Why Changes Occur: Customers often cannot reliably describe what they need until they see the software. The development process itself helps users understand their own needs better, leading to changes.
- Rigidity vs. Responsiveness: A plan to follow requirements rigidly is often a plan not to respond to the customer.
Handling Requirements Changes During Construction Since change is inevitable, use these strategies to minimize the negative impact:
- Assess Quality: Use a checklist to evaluate the quality of your requirements. If they are not good enough, stop and fix them before proceeding.
- Communicate Costs: Make sure everyone knows the cost of changes. When a client requests a change, offer a revised schedule and cost estimate. This often converts "must haves" into "nice to haves".
- Establish Change-Control: Set up a formal procedure (e.g., a change-control board) to review and approve changes. This keeps you happy (changes happen at specific times) and the customer happy (their input is handled).
- Use Flexible Approaches: Utilize development approaches that accommodate change, such as evolutionary prototyping or evolutionary delivery (building in short cycles).
- Focus on Business Value: Evaluate changes based on the business case. Many "features" disappear when evaluated against their actual incremental business value.
- Dump the Project: If requirements are exceptionally bad or volatile, and other strategies fail, consider cancelling the project.
Requirements Checklist Highlights When evaluating requirements, consider these key areas:
- Functional Requirements: Are all inputs, outputs, and external interfaces (hardware, software, communication) specified? Are user tasks and data clearly defined?
- Nonfunctional (Quality) Requirements: Are expected response times, security levels, reliability (consequences of failure), and maintainability specified?
- Quality of Requirements: Are they written in user language? Do they avoid conflicts? Are they at a consistent level of detail? Are they testable?
- Completeness: Are areas of incompleteness specified? If all requirements are met, is the product acceptable?
4.1 Choice of Programming Language
The Impact of Language Choice The programming language selected for a project can significantly influence productivity and code quality.
- Familiarity: Programmers are more productive when using a language they know. Data suggests programmers working in a language they have used for three or more years are about 30% more productive than those with equivalent experience who are new to a language. Some studies have shown productivity gaps as wide as 3 times based on language experience.
- High-Level vs. Low-Level: Using high-level languages (like C++, Java, Visual Basic) generally results in higher productivity and reliability than using lower-level languages (like C or Assembly).
- Higher-level languages are more expressive; a single line of code accomplishes more.
- Using high-level languages can improve productivity, simplicity, and comprehensibility by factors of 5 to 15.
The Sapir-Whorf Hypothesis
- Concept: Drawn from linguistics, this hypothesis suggests that a person's ability to think a thought depends on knowing words capable of expressing it.
- Application to Software: The programming language limits or shapes how a programmer thinks about a solution.
- Example: Programmers familiar with Fortran but new to C++ often write code that compiles in C++ but is logically structured like Fortran, failing to utilize C++'s object-oriented capabilities.
- Implication: A good language relieves the brain of unnecessary work (like manual memory management or low-level logic), allowing the programmer to concentrate on advanced problems.
Language Expressiveness (Ratios) Different languages require different amounts of code to express the same functionality. Table 4-1 compares high-level languages to C code:
- C: 1 (Baseline)
- C++: 2.5
- Java: 2.5
- Visual Basic: 4.5
- Perl / Python / Smalltalk: 6 (A higher ratio means the language is more expressive; one line of code accomplishes more than one line of C).
Brief Descriptions of Common Languages
- Ada: A general-purpose, high-level language based on Pascal. Developed for the Department of Defense, it is excellent for real-time and embedded systems, emphasizing data abstraction and information hiding.
- Assembly: A low-level "second-generation" language where statements correspond 1-to-1 with machine instructions. It is specific to a particular processor and used only when pushing limits of execution speed or code size.
- C: A mid-level language associated with UNIX. It serves as a "portable assembly language," featuring pointers and weak typing,.
- C++: An object-oriented language based on C. It adds classes, polymorphism, exception handling, and templates to C, along with more robust type checking.
- C#: A general-purpose, object-oriented language developed by Microsoft, syntactically similar to C, C++, and Java.
- Cobol: An English-like language used primarily for business applications. It remains widely used (second only to Visual Basic in some counts).
- Fortran: The first high-level language, introducing variables and high-level loops. It is used primarily for scientific and engineering applications.
- Java: An object-oriented language similar to C++ designed to run on any platform via virtual machines. It is widely used for Web applications.
- JavaScript: An interpreted scripting language used primarily for client-side programming in Web pages.
- Perl: A string-handling language based on C and UNIX utilities, often used for system administration and report generation.
- PHP: An open-source scripting language used for server-side interactive functions and accessing databases.
- Python: An interpreted, interactive, object-oriented language commonly used for scripts and small Web applications.
- SQL: A declarative language (defining results rather than procedures) which is the standard for querying and updating relational databases.
- Visual Basic: A high-level, object-oriented, visual version of Basic developed by Microsoft. It is widely used for Windows applications and Web programs.
4.4 Selection of Major Construction Practices
The Necessity of Choice
- Preparation Strategy: Part of preparing for construction is consciously deciding which "good practices" to emphasize. You cannot (and should not) use every available practice on every project.
- Trade-offs: Some practices replace others. For example, some projects might rely on pair programming and test-first development, while others might rely on solo development combined with formal inspections.
Key Decisions Checklist You should explicitly decide whether to include or exclude specific practices in the following areas before construction begins:
1. Coding Practices
- Design Balance: Define how much design should be done up-front versus how much should be done at the keyboard while coding.
- Conventions: Establish coding conventions for names, comments, and layout.
- Architectural Implications: Define specific coding practices implied by the architecture, such as:
- Error condition handling.
- Security addressing.
- Class interface conventions.
- Standards for reused code.
- Performance considerations during coding.
- Technology Wave: Identify your location on the technology wave and adjust expectations. Determine how to "program into the language" rather than being limited by programming in it.
2. Teamwork
- Integration Procedure: Define the specific steps a programmer must take before checking code into the master source (e.g., must compile, must pass smoke test).
- Programming Mode: Decide if programmers will work in pairs, individually, or a combination of both.
3. Quality Assurance
- Test-First Development: Decide if programmers will write test cases before writing the code.
- Unit Testing: Verify if programmers will write unit tests for their code regardless of the order (first or last).
- Debugger Usage: Decide if programmers are expected to step through their code in a debugger before checking it in.
- Integration Testing: Determine if programmers must integration-test their code before check-in.
- Reviews: Decide if programmers will review or inspect each other's code.
4. Tools
- Revision Control: Select a revision control tool.
- Language/Compiler: Select the specific language version or compiler version.
- Frameworks: Select a framework (e.g., J2EE, Microsoft .NET) or explicitly decide not to use one.
- Standard Features: Decide whether to allow the use of nonstandard language features.
- Acquisition: Identify and acquire necessary tools, such as editors, refactoring tools, debuggers, test frameworks, and syntax checkers.