Internal Product Attributes
Measuring Internal Product Attributes
Section titled “Measuring Internal Product Attributes”Internal product attributes are characteristics measurable from the software artifact itself (code, design documents, etc.) without executing it or considering its external environment. Measuring these attributes helps monitor and control the product during development and maintenance, often serving as leading indicators for external quality attributes like reliability or maintainability.
Key Internal Attributes: Size and Structure
Section titled “Key Internal Attributes: Size and Structure”Two fundamental internal attributes are size and structure. Size often correlates with development effort, while structure heavily influences maintainability, testability, and understandability.
Measuring Size
Section titled “Measuring Size”Software size is a multifaceted concept, often described through:
-
Length: The physical extent of the software artifact (e.g., lines of code, number of files/modules).
-
Functionality: The scope of features and capabilities provided to the user (e.g., Function Points, Story Points).
-
Complexity: The degree of intricacy in the software’s design and implementation. This itself has dimensions:
- Problem Complexity: Inherent difficulty of the problem being solved.
- Algorithmic Complexity: Efficiency of the chosen algorithms (e.g., Big O notation).
- Structural Complexity: Complexity arising from the code’s structure (e.g., dependencies, nesting levels, discussed further under Structure).
- Cognitive Complexity: Effort required for a human to understand the code.
Let’s examine how these aspects of size are measured:
Measuring Length
Section titled “Measuring Length”Length can be measured for various artifacts like specifications, designs, and code. Early measures (specification length) can sometimes predict later measures (code length).
Specification and Design Length
Section titled “Specification and Design Length”These documents often mix text with diagrams (e.g., UML diagrams, flowcharts, wireframes). Length can be measured by counting pages, words, or specific diagram elements (e.g., number of UML classes, use cases, activity nodes). Consistency requires defining clear counting rules for atomic elements within each notation used.
Code Length
Section titled “Code Length”The most traditional measure is Lines of Code (LOC). It’s crucial to define LOC clearly: Does it include comments, blank lines? Is it physical lines or logical statements? A common distinction is:
- Source Lines of Code (SLOC): Typically excludes comments and blank lines.
- Non-Comment Lines of Code (NCLOC): Similar to SLOC.
- Logical Lines of Code (LLOC): Counts executable statements, potentially spanning multiple physical lines.
While easy to automate, LOC has significant limitations: it varies greatly between languages, doesn’t reflect complexity or functionality accurately, and can be gamed. It’s often used alongside other metrics.
Other length measures include:
- Number of files, modules, classes, methods/functions.
- Number of bytes or characters (less common for code itself).
Halstead’s Software Science (Historical Context): Maurice Halstead proposed metrics based on counting operators (e.g., +, =, if) and operands (variables, constants). Key concepts included:
- μ1: Number of unique operators
- μ2: Number of unique operands
- N1: Total occurrences of operators
- N2: Total occurrences of operands
From these, Halstead derived measures like Program Length ($$N = N_{1}+ N_{2}$$), Vocabulary ($$\mu =\mu _{1}+\mu {2}$$), Volume ($$V = N\times {log{2}} \mu$$), Difficulty ($$D \approx (\mu_1 / 2) \times (N_2 / \mu_2)$$), and Effort ($$E = D \times V$$).
While influential historically for attempting a scientific basis for measurement, Halstead metrics are complex to calculate accurately (defining operators/operands consistently is hard) and are rarely used in modern practice compared to simpler complexity metrics.
Object-Oriented Metrics: Measures like Number of Classes (NOC), Number of Methods per Class, and Weighted Methods per Class (WMC) provide length/size insights in OO systems.
Measuring Functionality
Section titled “Measuring Functionality”Functionality measures aim to quantify the features delivered to the user, independent of implementation details. Common approaches include:
- Function Point Analysis (FPA): A standardized method (e.g., Albrecht’s, IFPUG, COSMIC) counting user-recognizable functions like inputs, outputs, inquiries, files, and interfaces, adjusted for complexity. More detail in the next chapter.
- Use Case Points (UCP): Based on counting actors and use cases in a UML model, adjusted for complexity and technical/environmental factors.
- Story Points (Agile): A relative measure used in Agile development to estimate the effort required for user stories, combining complexity, uncertainty, and volume. Not a direct measure of functionality size but related.
Functionality measures are generally considered better indicators of user-perceived size than LOC but can be more subjective and time-consuming to calculate.
Measuring Structure (Complexity)
Section titled “Measuring Structure (Complexity)”Structural metrics assess the complexity arising from the organization and interconnections within the software. High structural complexity often leads to difficulties in testing, understanding, and maintenance. Key metrics include:
- Cyclomatic Complexity (McCabe): Measures the number of linearly independent paths through a piece of code (e.g., a function or method). Calculated from a control flow graph (Nodes, Edges) as E - N + 2. Higher values indicate more complex branching logic, suggesting higher risk and testing effort needed. Commonly used by static analysis tools.
- Coupling Metrics: Measure the degree of interdependence between modules/classes (e.g., Coupling Between Objects - CBO in OO). High coupling makes changes ripple and reduces reusability.
- Cohesion Metrics: Measure how well the elements within a single module/class belong together (e.g., Lack of Cohesion in Methods - LCOM in OO). Low cohesion suggests the module/class has too many unrelated responsibilities and should potentially be split.
- Nesting Depth: Maximum level of nested control structures (if, for, while). Deep nesting makes code hard to read and follow.
- Cognitive Complexity: A more recent metric (promoted by tools like SonarQube) aiming to measure the mental effort required to understand code, penalizing structures that break linear flow or require mental stacking.
- Dependency Analysis: Mapping dependencies between modules/components to identify potential cycles or overly complex relationships.
Modern static analysis tools automatically calculate many of these structural metrics, providing valuable feedback to developers during the coding process.