Chapter 15 Flashcards — JUnit Internals

flashcards clean-code junit case-study refactoring

What is the “Boy Scout Rule” and which chapter case study exemplifies it?
?
Leave the code cleaner than you found it. Chapter 15 demonstrates it by taking the already-decent ComparisonCompactor class from JUnit, finding six smells, and fixing each without changing observable behavior. No rewrite required — just small, targeted improvements.

What smell is illustrated by field names like fExpected, fActual, and fContextLength?
?
Encoding in names (G20 / N6). The f prefix was a convention to signal “field” before IDE support existed. Modern environments make it redundant noise. Clean Code names fields by what they hold: expected, actual, contextLength.

What is the rule for choosing between “index” and “length” in a variable name?
?
Name variables by what they store. If the variable holds a count of characters (e.g., how many suffix characters match), name it suffixLength. If it holds a zero-based position into an array, name it suffixIndex. Using the wrong term forces readers to cross-reference usage to understand the variable’s semantics.

What is a negative conditional and why should it be avoided?
?
A negative conditional uses negation at the top of a guard — e.g., if (expected == null || actual == null || areStringsEqual()). Readers must negate the entire expression to understand the happy path. Convert to a positive named predicate: if (canBeCompacted()). Positive predicates scan left-to-right without mental reversal.

How should a method be named when its return value is a formatted artifact?
?
Name the method after what it returns, not what it does internally. compact() describes an internal action. formatCompactedComparison() describes the returned value — a formatted comparison message. Callers care about what they receive.

What is hidden temporal coupling and why is it dangerous?
?
Hidden temporal coupling occurs when method B silently depends on method A having been called first, but nothing in B’s signature communicates this. Example: findCommonSuffix() reads prefixLength from a shared field that findCommonPrefix() must have populated. If the call order is changed, the result is silently wrong. The dependency is invisible until it breaks.

How do you eliminate hidden temporal coupling?
?
Make the dependency explicit via parameters. Instead of a void findCommonSuffix() that reads a field, write int findCommonSuffix(int prefixLength). The caller must compute prefixLength first and pass it in. The compiler now enforces the correct ordering.

What is the difference between a method doing “two things” and a method doing “one thing at two levels of abstraction”?
?
Both are violations of the single responsibility principle for functions, but the levels-of-abstraction version is subtler. The original compact() mixed high-level orchestration (decide whether to compact, format the result) with low-level details (compute prefix, compute suffix). Extracting findCommonPrefix() and findCommonSuffix() leaves formatCompactedComparison() operating entirely at the orchestration level.

In the ComparisonCompactor, what are the two responsibilities that were incorrectly merged in the original compact() method?
?

  1. Deciding whether and how to compact the strings (finding common prefix/suffix, building compact forms). 2. Formatting the assertion failure message (calling Assert.format with the result). After refactoring, formatCompactedComparison handles formatting, and private helpers handle compaction.

What is variable shadowing and why is it an avoidable hazard?
?
Variable shadowing occurs when a local variable in a method has the same name as a field (or outer-scope variable). In the original compact(), local String expected shadowed the field fExpected. After removing f-prefixes from fields, the shadow would become a silent name collision. The fix: rename the local to compactExpected to make its purpose distinct.

Why does canBeCompacted() communicate intent better than the original inline condition?
?
canBeCompacted() encodes the business rule (“we can compact when expected and actual are non-null and differ”) as a named predicate. The inline condition if (fExpected == null || fActual == null || areStringsEqual()) requires the reader to build the rule from raw boolean logic. A named predicate is readable without comprehension overhead.

What is the general principle behind renaming compact() to formatCompactedComparison()?
?
Functions should be named from the caller’s perspective, not the implementer’s. The caller invokes this method to get a formatted comparison string — so the name should describe that artifact. compact is an implementation verb that leaks internal mechanics into the public API.

What is the Boy Scout Rule applied to code, and why is it more powerful than scheduled refactoring sprints?
?
The Boy Scout Rule: always leave the code cleaner than you found it. Applied incrementally, each developer improves one or two things per visit, so the codebase continuously improves rather than degrading between refactoring sprints. Sprints happen rarely; small improvements happen every commit.

In C++, what is the idiomatic naming convention for member variables that replaces the f or m_ prefix?
?
A trailing underscore (contextLength_, expected_). This avoids the visual encoding prefix while still distinguishing fields from local variables. It is widely used in Google’s C++ style guide and many open-source C++ projects.

How does Python’s @dataclass(frozen=True) improve a class like DiffCompactor?
?
@dataclass(frozen=True) makes the object immutable after construction — all fields become read-only. For a compactor that takes expected, actual, and contextLength at construction and never mutates them, frozen=True makes that contract explicit and enforces it. It also auto-generates __init__, __repr__, and __eq__ for free.

What smell category does the f-prefix fall under in Martin’s smell taxonomy?
?
Names — N6 (Avoid Encodings). Encoding type or scope information into names (Hungarian notation, f prefix, m_ prefix) adds visual noise without value when modern IDEs and type systems communicate that information through other means.

What smell category does hidden temporal coupling fall under?
?
General — G31 (Hidden Temporal Coupling). This smell is in the “General” category because it is a structural hazard in any language or paradigm, not specific to naming, comments, or test structure.

Why does findCommonSuffix(int prefixLength) returning an int signal a design improvement over void findCommonSuffix() writing to a field?
?
A return value communicates that the method computes a result. A void method that writes to a field communicates nothing about what it produces or what it requires. Returning int suffixLength and accepting int prefixLength makes both the input and output explicit in the signature, reducing the need to read the implementation body to understand data flow.

How does the structure of a method name mislead in compact() vs. formatCompactedComparison()?
?
compact() implies the method’s action is compaction — but the method actually returns a formatted assertion message. The name creates a mismatch between expectation and reality. formatCompactedComparison() aligns the name with the artifact returned, eliminating the surprise.

What general lesson does the ComparisonCompactor case study offer about open-source code quality?
?
Open-source code, even from well-maintained projects like JUnit, is not automatically clean code. Smells accumulate through small, reasonable decisions made under time pressure. The existence of smells is not a criticism of the original author — it is a reminder that all code benefits from periodic review and that writing clean code is an ongoing practice, not a one-time achievement.

What is the practical sequence for refactoring a method with multiple responsibilities?
?

  1. Identify the distinct responsibilities the method handles. 2. Extract each responsibility into a private method with a descriptive name. 3. Verify the public method becomes a short orchestration of those helpers. 4. Confirm the method name still accurately describes what it returns. If it does not, rename it. This process is described across Chapters 3, 14, and 15 of Clean Code.

When a conditional guard inverts naturally into a positive predicate, what two things must the new method provide?
?

  1. A positive name that states the enabling condition (canBeCompacted, isEligible, hasData) rather than a negative one. 2. A self-contained boolean expression that reads as a complete business rule — no implicit context required. The caller reads if (canBeCompacted()) and knows exactly what is being checked without entering the method body.

Total Cards: 22
Review Time: ~18 minutes
Priority: LOW
Last Updated: 2026-04-14