Skip to main content

CS 3100: Program Design and Implementation II

Quiz Review 2

Meme with triumphant Winnie the Pooh: 'Me getting an A+ on a blood test I didn't study for'

©2026 Ellen Spertus, CC-BY-SA

Poll: How much of the practice quiz did you do?

A. None yet

B. I've skimmed it

C. A few problems

D. Many problems

E. All problems

Poll Everywhere QR Code or Logo

Poll: How hard do you think it is?

Poll image
Poll Everywhere QR Code or Logo

Click below the faces if you haven't tried it yet.

Question 1: Critical Requirements Error

A team discovers a critical requirements error after the system has been deployed to production. According to the cost-of-change model discussed in class, how does the cost of fixing this error compare to catching it during requirements analysis?

  1. The cost is roughly the same regardless of when it is discovered
  2. The cost is actually lower in production because the team better understands the system and can make more targeted fixes
  3. The cost grows exponentially — fixing in production can be 100x more expensive than fixing during requirements
  4. The cost is approximately 2x higher in production

Catching Problems Late is Expensive

Lecture 1: Systematic Program Design and Implementation Process

  • Bug in requirements = cheap to fix
  • Bug in production = expensive

Lecture 9: Getting requirements wrong is the most expensive mistake in software engineering.

Lecture 12: Domain Models are Expensive to Change

Lecture 18: Architectural decisions are the ones that are expensive to change.

Lecture 24: Usability

  • The best way to know if users can use your software is to watch them use it
  • But by the time you have working software, changes are expensive

Poll: What's Not Expensive to Change Late?

Poll Everywhere QR Code or Logo

Text espertus to 22333 if the
URL isn't working for you.

https://pollev.com/espertus

Question 1 Answer

A team discovers a critical requirements error after the system has been deployed to production. According to the cost-of-change model discussed in class, how does the cost of fixing this error compare to catching it during requirements analysis?

  1. The cost is roughly the same regardless of when it is discovered
  2. The cost is actually lower in production because the team better understands the system and can make more targeted fixes
  3. The cost grows exponentially — fixing in production can be 100x more expensive than fixing during requirements
  4. The cost is approximately 2x higher in production

Question 2: Stakeholder Conflicts

A product manager, a UX designer, and a backend engineer are all stakeholders for a recipe-sharing feature in CookYourBooks. The product manager wants maximum social engagement, the UX designer wants a simple interface, and the engineer wants to minimize API calls. Which requirements analysis principle does this scenario illustrate?

  1. Requirements should be gathered only from the person who is paying for the project
  2. Technical stakeholders should override non-technical stakeholders
  3. The best approach is to implement all stakeholder requests independently
  4. Different stakeholders have conflicting values, and requirements analysis must surface and negotiate these conflicts

"It depends"

We can't maximize every metric so need to make trade-offs:

  • class design with SOLID principles [Lecture 8]
  • requirements analysis [Lecture 9]
  • object creation patterns [Lecture 17]
  • architectural qualities [Lectures 19, 21]
  • usability measures [Lecture 24, not on quiz]

Good, fast, cheap: Choose two

Top two panels of a workchronicles.com cartoon, in which someone explains to a client: 'We can be good. fast. cheap but you can only pick tw...'
Bottom two panels of a workchronicles.com cartoon, in which the client seizes 'GOOD', 'FAST', and 'CHEAP' and says: 'Let's get started?', causing the original speaker to put their head in their hands.

Question 2 Answer

A product manager, a UX designer, and a backend engineer are all stakeholders for a recipe-sharing feature in CookYourBooks. The product manager wants maximum social engagement, the UX designer wants a simple interface, and the engineer wants to minimize API calls. Which requirements analysis principle does this scenario illustrate?

  1. Requirements should be gathered only from the person who is paying for the project
  2. Technical stakeholders should override non-technical stakeholders
  3. The best approach is to implement all stakeholder requests independently
  4. Different stakeholders have conflicting values, and requirements analysis must surface and negotiate these conflicts

Question 3: Responsibility Assignment Pattern

In the Pawtograder domain model, the RegradeRequest class has a method canEscalate() that checks whether the request has responses, whether it is already resolved, and whether 24 hours have passed since the last response. This is an example of which responsibility assignment pattern?

  1. Information Expert — RegradeRequest has the data needed to answer the question
  2. Creator — RegradeRequest creates the escalation
  3. Singleton — there can only be one active regrade request
  4. Controller — RegradeRequest handles external system events and coordinates responses across the application

Responsibility Assignment Determines Code Structure

Now we translate our validated domain model into working code.

The critical question: Which classes own which behaviors?

We'll use three key heuristics:

  1. Information Expert — Who has the data needed?
  2. Creator — Who should create new objects?
  3. Controller — Who coordinates complex operations?


Lecture 12: Domain Modeling

Assign Behavior to the Class That Has the Data

Assign responsibility to the class that has the information needed to fulfill it.

Example: Who should determine if a regrade can be escalated?

❌ Ignoring the heuristic:

// Service must extract all data from request
class RegradeService {
boolean canEscalate(RegradeRequest req) {
List<RegradeResponse> responses =
req.getResponses(); // extract
RegradeStatus status =
req.getStatus(); // extract
if (responses.isEmpty()) return false;
if (status == RESOLVED) return false;
// ... more extraction and logic
}
}

Service becomes a "logic hog" that pulls data from passive objects.

✓ Following the heuristic:

// RegradeRequest knows its own history
class RegradeRequest {
private List<RegradeResponse> responses;
private RegradeStatus status;

boolean canEscalate() {
if (responses.isEmpty()) return false;
if (status == RESOLVED) return false;
// Data is RIGHT HERE—no extraction
return coolingOffPeriodPassed();
}
}

Data and behavior stay together. Self-contained.



Lecture 12: Domain Modeling

The Class That Knows Its Constraints Should Enforce Them

Who should check if a grader can review a regrade?

❌ Logic scattered in service:

class RegradeService {
boolean canGraderReview(Grader g, RegradeRequest r) {
// Pull data from grader
GraderType type = g.getType();
int activeCount = g.getActiveGradingCount();
int max = g.getMaxConcurrentGradings();

// Pull data from request
Grader original = r.getOriginalSession()
.getGrader();

// Service implements the logic
if (original.equals(g)) return false;
if (activeCount >= max) return false;
return true;
}
}

Service knows too much about Grader's internals.

✓ Grader is the expert on itself:

class Grader {
private GraderType type;
private int activeGradingCount;

boolean canReviewRegrade(RegradeRequest req) {
// Can't review own work
if (req.getOriginalSession()
.getGrader().equals(this)) {
return false;
}
// Check MY workload (I know it!)
if (activeGradingCount >= getMax()) {
return false;
}
return true;
}
}

Grader knows its own constraints. Easy to test.

Lecture 12: Domain Modeling

Containers Should Create What They Contain

Example: Who should create InlineComment objects?

Key point: This means where should the createComment() method live, not where it should be called from.

❌ Ignoring the heuristic:

// External service creates comments
class CommentService {
InlineComment createComment(
GradingSession session,
SourceFile file, int line, String text) {
InlineComment c = new InlineComment(
session, file, line, text, now());
session.getComments().add(c); // Reaches in!
return c;
}
}


Service reaches into GradingSession's internals. Session loses control of its own state.

✓ Following the heuristic:

// Container creates what it contains
class GradingSession {
private List<InlineComment> comments = ...;

InlineComment addComment(
SourceFile file, int line, String text) {
if (!isActive()) throw new ...;
InlineComment c = new InlineComment(
this, file, line, text, now());
comments.add(c);
return c;
}
}

GradingSession controls its own contents. Can enforce invariants (must be active).

Lecture 12: Domain Modeling

Question 3 Answer

In the Pawtograder domain model, the RegradeRequest class has a method canEscalate() that checks whether the request has responses, whether it is already resolved, and whether 24 hours have passed since the last response. This is an example of which responsibility assignment pattern?

  1. Information Expert — RegradeRequest has the data needed to answer the question
  2. Creator — RegradeRequest creates the escalation
  3. Singleton — there can only be one active regrade request
  4. Controller — RegradeRequest handles external system events and coordinates responses across the application

Question 4: Domain Modeling

// Version A: Technical-focused
public class SubmissionManager {
private Map<String, List<byte[]>> fileStorage = new HashMap<>();
private Map<String, Integer> versionCounters = new HashMap<>();
private Map<String, Map<String, Object>> gradeData = new HashMap<>();
}

// Version B: Domain-aligned
public class Submission {
private final Student student;
private final Assignment assignment;
private final List<SourceFile> files;
private GradingSession activeGradingSession;
}

What is the primary advantage of Version B over Version A?

  1. Version B uses less memory because it has fewer fields

  2. Version B has a smaller representational gap — its structure mirrors how stakeholders think about the domain

  3. Version B compiles faster because it avoids generic types

  4. Version B is required by the Java language specification for domain objects, which mandates named types over raw collections in business logic

🤯 You can use quizmanship to answer the question without reading the code!

Poll: What is this class about?

A. Slightly reducing memory usage

B. Creating understandable programs

C. Making compilation faster

D. Java syntax

Poll Everywhere QR Code or Logo

Text espertus to 22333 if the
URL isn't working for you.

https://pollev.com/espertus

Question 4 Answer

What is the primary advantage of Version B over Version A?

  1. Version B uses less memory because it has fewer fields
  2. Version B has a smaller representational gap — its structure mirrors how stakeholders think about the domain
  3. Version B compiles faster because it avoids generic types
  4. Version B is required by the Java language specification for domain objects, which mandates named types over raw collections in business logic

Question 4 Rewrite

Which version of the code has a smaller representational gap, with structure mirroring how stakeholders think about the domain?

// Version A: Technical-focused
public class SubmissionManager {
private Map<String, List<byte[]>> fileStorage = new HashMap<>();
private Map<String, Integer> versionCounters = new HashMap<>();
private Map<String, Map<String, Object>> gradeData = new HashMap<>();
}

// Version B: Domain-aligned
public class Submission {
private final Student student;
private final Assignment assignment;
private final List<SourceFile> files;
private GradingSession activeGradingSession;
}

The structure of the domain-aligned version (B) mirrors how stakeholders think about the domain.

Question 5: AI Collaboration

A developer uses an AI programming agent to generate a complete authentication module without reviewing the generated code or understanding how it works. According to the course's framework for AI collaboration, this is an example of:

  1. Effective use of AI to maximize productivity
  2. Pair programming with an AI partner
  3. The "vibe coding" trap — accepting AI output without applying domain knowledge to evaluate it
  4. The recommended approach for boilerplate code

The Fundamental Principle: Task Familiarity

2x2 quadrant diagram with Domain Expertise on X-axis and Task Complexity on Y-axis. Top-left (low expertise, high complexity) is red 'Danger Zone'. Top-right (high expertise, high complexity) is green 'Ideal for AI'. Bottom-left (low expertise, low complexity) is yellow 'Learning Opportunity'. Bottom-right (high expertise, low complexity) is green 'Efficient Use'.

Lecture 13: AI Coding Assistants

Fallacy 4: "The Network Is Secure"

Data crossing networks can be intercepted, modified, or spoofed. Every network boundary is a potential attack surface.

Pawtograder: Without the OIDC token, anyone could POST fake grades. Without HTTPS, a network observer could read or modify grades in transit.

(We'll dive deep on security later in this lecture.)



Lecture 20: Distributed Architecture

A Well-Maintained Library Beats DIY for Security

Is it safer to use a popular library or write your own? Almost always use the library — if it's well-maintained.

Why well-maintained libraries win:

  • Battle-tested by thousands of users
  • Security researchers scrutinize popular projects
  • Vulnerabilities get reported and patched
  • You benefit from specialists' expertise

When dependencies introduce risk:

  • Every dependency is an attack surface
  • Transitive dependencies multiply the risk
  • Unmaintained dependencies don't get patches
  • You're trusting code you've never read


Lecture 21: Open-Source Frameworks

Question 5 Answer

A developer uses an AI programming agent to generate a complete authentication module without reviewing the generated code or understanding how it works. According to the course's framework for AI collaboration, this is an example of:

  1. Effective use of AI to maximize productivity
  2. Pair programming with an AI partner
  3. The "vibe coding" trap — accepting AI output without applying domain knowledge to evaluate it
  4. The recommended approach for boilerplate code

Question 6: Debugging Approach

When debugging, a developer notices that a Recipe object has an unexpected null value for its instructions field. They form the hypothesis: "The instructions field is set to null in the constructor." They then set a breakpoint in the constructor and run the program. This approach is an example of:

  1. Rubber duck debugging
  2. Trial-and-error debugging
  3. Print-statement debugging
  4. The scientific method applied to debugging — observe, hypothesize, predict, test

Rubber-Duck Debugging

Sketchplanations comic defining rubber ducking as explaining your problem to a rubber duck to help solve it.
In the first panel, a developer sits at a laptop with a rubber duck on the desk and says 'I can't figure out what's up. I take each transaction then apply the filters in this order and ... hang on ...' In the second panel, the developer turns toward the duck with a smile and exclaims 'Of course! What was I thinking?! Thank you, rubber duck!'

Ad Hoc Techniques

  • Trial-and-error debugging
  • Print-statement debugging
Two-red button meme with choices "Adding lots of print statements" and "Scientific debugging"

The Scientific Method Applied to Debugging

  1. Observe: Notice unexpected behavior or test failure
  2. Hypothesize: Form a theory about the cause
  3. Predict: What evidence would support or refute this?
  4. Test: Gather evidence (debugger, logging, tests)
  5. Analyze: Does the evidence support the hypothesis?
  6. Iterate: Refine hypothesis or implement fix


Lecture 14: Program Understanding & Debugging

Question 6 Answer

When debugging, a developer notices that a Recipe object has an unexpected null value for its instructions field. They form the hypothesis: "The instructions field is set to null in the constructor." They then set a breakpoint in the constructor and run the program. This approach is an example of:

  1. Rubber duck debugging
  2. Trial-and-error debugging
  3. Print-statement debugging
  4. The scientific method applied to debugging — observe, hypothesize, predict, test

Question 7: Test Doubles

Consider this test for a ThermostatController:

@Test
public void activatesHeatingWhenBelowTarget() {
TemperatureSensor mockSensor = mock(TemperatureSensor.class);
HVACService mockHVAC = mock(HVACService.class);
NotificationService mockNotifier = mock(NotificationService.class);

when(mockSensor.readTemperature("livingRoom")).thenReturn(65.0);

ThermostatController controller = new ThermostatController(
mockSensor, mockHVAC, mockNotifier);
controller.adjustToTargetTemperature(72.0, "livingRoom");
verify(mockHVAC).setMode(HVACMode.HEATING, "livingRoom");
verify(mockHVAC).activate("livingRoom");
}

In this test, mockSensor is acting as a (A) stub, (B) mock, (C) integration test fixture, or (D) spy.

Test Doubles: Stand-Ins for Real Dependencies

Stubs return canned answers; fakes work simply; spies record calls.

Stubs

Return canned answers

Fakes

Simplified implementations

Spies

Record what happened

From simplest to most sophisticated

What about mocks? We'll get there.

Lecture 15: Test Doubles and Isolation

Stubs: Return Canned Answers

Ignore details you don't care about; return what you need.

class StubGitHubService implements GitHubService {
private final CodeSnapshot fixedCode;

public StubGitHubService(CodeSnapshot code) {
this.fixedCode = code;
}

@Override
public CodeSnapshot fetchCode(String repoUrl) {
return fixedCode; // Always returns the same code
}
}

Ignores the repo URL, always returns sample code — that's fine!



Lecture 15: Test Doubles and Isolation

Fakes: When You Need Real Behavior

When you need save-then-retrieve, use a working in-memory implementation.

class FakeUserRepository implements UserRepository {
private final Map<String, User> users = new HashMap<>();

@Override
public void save(User user) {
users.put(user.getId(), user);
}

@Override
public User findById(String id) {
return users.get(id);
}

@Override
public List<User> findAll() {
return new ArrayList<>(users.values());
}
}

A working implementation — just simpler than the real database



Lecture 15: Test Doubles and Isolation

Spies: Record What Happened (Decorator Pattern)

Wrap, record, delegate—verify interactions after the fact.

class SpyDatabase implements Database {
private final Database delegate; // Wraps a real implementation
private boolean saveGradeCalled = false;
private String savedStudentId = null;

public SpyDatabase(Database realDatabase) {
this.delegate = realDatabase; // Decorator pattern!
}

@Override
public void saveGrade(String studentId, int score) {
this.saveGradeCalled = true; // Record the call
this.savedStudentId = studentId;
delegate.saveGrade(studentId, score); // Delegate to real impl
}

// Query methods for tests
public boolean wasSaveGradeCalled() { return saveGradeCalled; }
public String getSavedStudentId() { return savedStudentId; }
}


Lecture 15: Test Doubles and Isolation

Where Do Mocks Fit In?

These describe the behavior of the test double:

  • stub: returns canned answer
  • fake: simplified implementation
  • spy: records how methods were called

Test doubles can be created

  • as ordinary classes (as shown on previous slides)
  • at run-time by a mocking framework, such as Mockito

Mockito: Create, Configure, Verify

mock(), when().thenReturn(), verify()—the three operations you'll use most.

mock(Class.class)Create a test double
when(...).thenReturn(...)Configure stub behavior
verify(mock).method(...)Check spy recordings
when(...).thenThrow(...)Simulate exceptions

Mockito uses reflection to generate implementations at runtime



Lecture 15: Test Doubles and Isolation

Question 7 Answer

@Test
public void activatesHeatingWhenBelowTarget() {
TemperatureSensor mockSensor = mock(TemperatureSensor.class);
HVACService mockHVAC = mock(HVACService.class);
NotificationService mockNotifier = mock(NotificationService.class);

when(mockSensor.readTemperature("livingRoom")).thenReturn(65.0);

ThermostatController controller = new ThermostatController(
mockSensor, mockHVAC, mockNotifier);

controller.adjustToTargetTemperature(72.0, "livingRoom");

verify(mockHVAC).setMode(HVACMode.HEATING, "livingRoom");
verify(mockHVAC).activate("livingRoom");
}

In this test, mockSensor is acting as a:
(A) stub — it returns a pre-configured value without real behavior
(B) Mock object verified with verify() — its primary role here is to assert method calls
(C) Integration test fixture — it connects to real hardware
(D) spy — it records method calls for later verification

Question 8: Test Double Tradeoffs

What is the primary risk of relying exclusively on test doubles (mocks and stubs) for all testing?

  1. Test doubles are slower than real implementations
  2. Test doubles violate the Open/Closed Principle
  3. Test doubles are not supported by modern testing frameworks, which require real object instances to correctly measure code coverage
  4. Test doubles can give false confidence — tests pass even when real components don't integrate correctly

Choices

Test doubles are slower than real implementations?

  • They're often faster, but we don't care about little speed improvements.

Test doubles violate the Open/Closed Principle?

  • They have nothing to do with classes being open for extension and closed for modification.

Test doubles are not supported by modern testing frameworks, which require real object instances to correctly measure code coverage?

  • Test doubles are real objects (just not production objects). Test frameworks support measuring code coverage but don't require it.

Test doubles can give false confidence — tests pass even when real components don't integrate correctly?

🎯

How Could a Test Double Give False Confidence?

@Test
public void thrusterFiresForCorrectDuration() {
// Create a double of Lockheed Martin's thruster.
ThrusterController mockThruster = mock(ThrusterController.class);

// Verify that the NavigationSystem calls the thruster's
// fire method with 4.45 Newtons.
NavigationSystem nav = new NavigationSystem(mockThruster);
nav.fireThruster(4.45);
verify(mockThruster).fire(4.45);
}

The test passes but the Mars Climate Orbiter fails. Why?

The Mars Climate Orbiter Disaster

Comic-style illustration of the Mars Climate Orbiter disaster. Top panel shows the spacecraft burning up in Mars' atmosphere while a green alien thinks 'They should have done integration testing.' Bottom left panel labeled 'Jet Propulsion Lab' shows horrified engineers with a whiteboard displaying 'Force: 4.45 N, kg·m/s²'. Bottom right panel labeled 'Lockheed Martin' shows equally distressed engineers with their whiteboard showing 'Force: 1 lbf, pound-force-seconds'.

Question 8 Answer

What is the primary risk of relying exclusively on test doubles (mocks and stubs) for all testing?

  1. Test doubles are slower than real implementations
  2. Test doubles violate the Open/Closed Principle
  3. Test doubles are not supported by modern testing frameworks, which require real object instances to correctly measure code coverage
  4. Test doubles can give false confidence — tests pass even when real components don't integrate correctly

Question 9: Hexagonal Architecture

// code omitted

In an IoT EnergyOptimizer, EnergyPricePort is a port and GridPriceApiAdapter (which calls a real pricing API) is an adapter. In Hexagonal Architecture, what is the main benefit of this separation?

  1. Adapters are faster than direct method calls
  2. The application core can be tested and reused without depending on any specific external system
  3. Ports automatically generate REST API endpoints
  4. The Java compiler requires interfaces for external dependencies involving I/O

Hexagonal Architecture (Ports and Adapters)

The solution is called Hexagonal Architecture, proposed by Alistair Cockburn (2005)

Hexagonal Architecture diagram for an IoT Smart Home. A central golden hexagon labeled 'Application Core / Domain Logic' contains 'EnergyOptimizer' with the rule 'if (price > threshold) reducePower()' and the caption 'Pure business rules. No technology.' Two ports sit on the hexagon's edges as electrical outlet sockets. On the left, EnergyPricePort has two adapters plugged in: GridPriceApiAdapter (blue, connecting to an Energy Grid API cloud) and StubPriceAdapter (green, labeled 'returns $0.35'). On the right, DeviceControlPort has ZigbeeDeviceAdapter (blue, connecting to a smart thermostat) and SpyDeviceControlAdapter (green, labeled 'records calls'). A callout reads 'Tests plug in here! Same ports, different adapters.' Blue adapters represent production; green adapters represent test doubles.

Lecture 16: Designing for Testability

The Three Layers

Core: Knows nothing about databases, APIs, or hardware—only the domain.

Ports: Interfaces that describe what the domain needs, not how to get it.

Adapters: Implementations of the interfaces to talk to specific technologies



Lecture 16: Designing for Testability

Now let's look at the code

public class EnergyOptimizer {
private final EnergyPricePort priceService;
private final DeviceControlPort deviceControl;
private final UserPreferencesPort preferences;

public EnergyOptimizer(EnergyPricePort priceService, DeviceControlPort deviceControl,
UserPreferencesPort preferences) {
this.priceService = priceService;
this.deviceControl = deviceControl;
this.preferences = preferences;
}
}

Question 9 Answer

In an IoT EnergyOptimizer, EnergyPricePort is a port and GridPriceApiAdapter (which calls a real pricing API) is an adapter. In Hexagonal Architecture, what is the main benefit of this separation?

  1. Adapters are faster than direct method calls
  2. The application core can be tested and reused without depending on any specific external system
  3. Ports automatically generate REST API endpoints
  4. The Java compiler requires interfaces for external dependencies involving I/O

Question 10: Testability Anti-Patterns

Which of the following is an anti-pattern that makes code difficult to test?

  1. Accepting dependencies through the constructor
  2. Defining behavior behind interfaces
  3. Using new to directly instantiate collaborators inside a method, hiding the dependency
  4. Separating domain logic from infrastructure code

What's an Anti-Pattern?

  • A pattern is a positive example that should be applied where appropriate.
  • An anti-pattern is a negative example that should be avoided.

Hardcoding Dependencies vs. Dependency Injection

// Hard-coded dependency
public class ImportService {


public ImportService() {

}

public void importRecipe(Path file) {
Recipe recipe = parseRecipe(file);
new LibraryServiceImpl().addRecipe(recipe);
}
}

Using new to directly instantiate collaborators inside a method, hiding the dependency

// Dependency injection
public class ImportService {
private final LibraryService libraryService;

public ImportService(LibraryService libraryService) {
this.libraryService = libraryService;
}

public void importRecipe(Path file) {
Recipe recipe = parseRecipe(file);
libraryService.addRecipe(recipe);
}
}

Accepting dependencies through the constructor

Question 10 Answer

Which of the following is an anti-pattern that makes code difficult to test?

  1. Accepting dependencies through the constructor
  2. Defining behavior behind interfaces
  3. Using new to directly instantiate collaborators inside a method, hiding the dependency
  4. Separating domain logic from infrastructure code

Question 11: Hidden Dependencies

// code omitted

In an ImportService, the method importRecipe() calls LibraryService.getInstance().addRecipe(recipe). What problem does the LibraryService.getInstance() call introduce?

  1. It makes ImportService slower by calling a static method
  2. It causes a NullPointerException at runtime
  3. It creates a hidden dependency that cannot be replaced for testing or reuse
  4. It violates the Single Responsibility Principle because ImportService now manages library instances

Consider the Code

public class ImportService {
public void importRecipe(Path file) {
Recipe recipe = parseRecipe(file);
LibraryService.getInstance().addRecipe(recipe);
}
}

This has exactly the same problem as in the previous question.

Hardcoding dependencies vs. dependency injection

// Hard-coded dependency
public class ImportService {


public ImportService() {

}

public void importRecipe(Path file) {
Recipe recipe = parseRecipe(file);
// two bad options, hardcoding dependencies
new LibraryServiceImpl().addRecipe(recipe);
LibraryService.getInstance().addRecipe(recipe);
}
}

Hardcoding dependency with new

Hardcoding dependency with static method call

// Dependency injection
public class ImportService {
private final LibraryService libraryService;

public ImportService(LibraryService libraryService) {
this.libraryService = libraryService;
}

public void importRecipe(Path file) {
Recipe recipe = parseRecipe(file);
// Use LibraryService injected through constructor
libraryService.addRecipe(recipe);

}
}

Accepting dependencies through the constructor

Question 11 Answer

In an ImportService, the method importRecipe() calls LibraryService.getInstance().addRecipe(recipe). What problem does the LibraryService.getInstance() call introduce?

  1. It makes ImportService slower by calling a static method
  2. It causes a NullPointerException at runtime
  3. It creates a hidden dependency that cannot be replaced for testing or reuse
  4. It violates the Single Responsibility Principle because ImportService now manages library instances

Question 12: Dependency Injection

A developer refactors ImportService so that LibraryService is passed in through the constructor rather than looked up via getInstance(). This refactoring applies which pattern?

  1. Strategy Pattern — the algorithm for importing is swappable
  2. Dependency Injection — the dependency is provided externally rather than looked up internally
  3. Service Locator — ImportService looks up its dependency from a central registry rather than receiving it from the outside
  4. Builder Pattern — the ImportService is constructed step by step

Question 12 Answer

A developer refactors ImportService so that LibraryService is passed in through the constructor rather than looked up via getInstance(). This refactoring applies which pattern?

  1. Strategy Pattern — the algorithm for importing is swappable
  2. Dependency Injection — the dependency is provided externally rather than looked up internally
  3. Service Locator — ImportService looks up its dependency from a central registry rather than receiving it from the outside
  4. Builder Pattern — the ImportService is constructed step by step

Question 13: Architecture Decision Records

What is an Architecture Decision Record (ADR)?

  1. A log file generated by the build system that records compilation decisions, dependency resolutions, and warnings for later auditing
  2. A UML diagram showing all classes in the system
  3. A performance benchmark comparing different architectural approaches
  4. A short document that captures the context, decision, and consequences of a significant architectural choice

Architecture Decision Records (ADRs)

Diagrams show what. ADRs capture why. An ADR documents: Context, Decision, and Consequences.

ADR-001: Centralized Feed Algorithm vs. Per-Server Algorithm

Context: We need to rank posts for each user's feed. We could rank centrally using global engagement signals, or let each server (or user) choose their own algorithm.

Decision: Chirp will rank feeds centrally using a shared EngagementStrategy. The RankingStrategy interface is retained to allow A/B testing of algorithms internally.

Consequences:

  • Consistency: All users get a coherent, optimized experience
  • Changeability: New algorithms can be tested without touching the Feed Service
  • Scalability: Ranking infrastructure can be optimized centrally
  • ⚠️ Autonomy: Users cannot opt out of the platform's ranking choices
  • ⚠️ Coupling: Feed quality is tied to the platform's data — if engagement data degrades, all feeds degrade
Lecture 18: Thinking Architecturally

ADR-001: Patron Service Boundaries

Context: The client used by the Patron needs to be able to make requests of a service layer that applies business logic to repositories. We were constrained to prioritize the Actor service boundary heuristic.

Decision: We will have these separate service interfaces supporting these commands:

  • HoldService
    • hold book
    • holds

[remainder omitted]

We separated HoldService from other services, such as AccountService because of the former's greater rate of change.

Consequences:

  • ✅ Decoupling: Changes to hold policy affect only HoldService and its implementations, not other services, such as AccountService.
  • ⚠️ Testability: We need to mock BookRepository to test the method isPermittedToPlaceHold(), even though that method does not reference BookRepository.

Lecture 23: Open Source Frameworks

Question 13 Answer

What is an Architecture Decision Record (ADR)?

  1. A log file generated by the build system that records compilation decisions, dependency resolutions, and warnings for later auditing
  2. A UML diagram showing all classes in the system
  3. A performance benchmark comparing different architectural approaches
  4. A short document that captures the context, decision, and consequences of a significant architectural choice

Question 14: Service Boundary Heuristics

When determining where to draw service boundaries in a system, which of the following is a valid heuristic discussed in class?

  1. Group code that changes together and for the same reasons; separate code that changes independently or for different actors
  2. Each service should contain exactly one class
  3. Service boundaries should follow alphabetical ordering of class names
  4. All database access should be in a single service to minimize connection pooling overhead and ensure a consistent data access layer across the system

Service Boundary Heuristics

  1. Actor: Things used by different actors should be separate.

  2. Rate of Change: Things that change at different speeds should be separate. [This is about code, not data.]

  3. Interface Segregation: Clients that need different things should get different interfaces. [Clients aren't people. They are classes, microservices, and GUIs.]

  4. Testability: Things that need independent testing should be separable.

Question 14 Answer

When determining where to draw service boundaries in a system, which of the following is a valid heuristic discussed in class?

  1. Group code that changes together and for the same reasons; separate code that changes independently or for different actors
  2. Each service should contain exactly one class
  3. Service boundaries should follow alphabetical ordering of class names
  4. All database access should be in a single service to minimize connection pooling overhead and ensure a consistent data access layer across the system

Question 15: How Team Structure Shapes Architecture

A company has three teams: a frontend team, a backend team, and a database team. They build a system with three layers: a presentation layer, an application layer, and a data access layer. According to Conway's Law, why is this architecture unsurprising?

  1. Three-layer architectures are the best design for web applications because they cleanly separate presentation, business logic, and data concerns
  2. The company chose the best architecture for performance reasons
  3. Organizations tend to produce system designs that mirror their own communication structures
  4. The Java language specification requires layered architectures

Conway's Law: Teams Shape Architecture

Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations.
Melvin Conway, 1967

Translation: Your software's architecture will mirror your team's structure—whether you plan for it or not.

Example: Three teams working on one app → three separate services that need integration


Lecture 22: Teams and Collaboration

Question 15 Answer

A company has three teams: a frontend team, a backend team, and a database team. They build a system with three layers: a presentation layer, an application layer, and a data access layer. According to Conway's Law, why is this architecture unsurprising?

  1. Three-layer architectures are the best design for web applications because they cleanly separate presentation, business logic, and data concerns
  2. The company chose the best architecture for performance reasons
  3. Organizations tend to produce system designs that mirror their own communication structures
  4. The Java language specification requires layered architectures

Question 16: Fallacies of Distributed Computing

"The network is reliable" is the first of the Eight Fallacies of Distributed Computing. A developer writes code that calls a remote recipe-sharing API without any error handling. When the network is temporarily unavailable, the application crashes. Which design strategy would address this fallacy?

  1. Increase the server's RAM to handle more requests, since network failures are often caused by servers running out of memory under load
  2. Implement timeouts, retries with exponential backoff, and graceful degradation
  3. Switch from HTTP to a faster protocol
  4. Cache all data locally and never use the network

The Fallacies of Distributed Computing

Eight warning signs arranged in a grid, each crossing out a wrong assumption about distributed systems: The network is reliable, Latency is zero, Bandwidth is infinite, The network is secure, Topology doesn't change, There is one administrator, Transport cost is zero, The network is homogeneous. Clean flat illustration style with warning colors.
Lecture 20: Distributed Architecture: Networks, Microservices, and Security

Question 16 Answer

"The network is reliable" is the first of the Eight Fallacies of Distributed Computing. A developer writes code that calls a remote recipe-sharing API without any error handling. When the network is temporarily unavailable, the application crashes. Which design strategy would address this fallacy?

  1. Increase the server's RAM to handle more requests, since network failures are often caused by servers running out of memory under load
  2. Implement timeouts, retries with exponential backoff, and graceful degradation
  3. Switch from HTTP to a faster protocol
  4. Cache all data locally and never use the network

Question 17: CIA Triad

In the CIA Triad for security, what do Confidentiality, Integrity, and Availability mean?

  1. Confidentiality means data is only accessible to authorized parties; Integrity means data is not tampered with; Availability means the system is accessible when needed
  2. Confidentiality means fast response times; Integrity means code compiles; Availability means 24/7 customer support
  3. Confidentiality means using encryption everywhere; Integrity means using checksums; Availability means using load balancers
  4. These are three types of software testing methodologies

Security Decisions Trade Off Confidentiality, Integrity, and Availability

The CIA Security Triad: an equilateral triangle with Confidentiality (padlock), Integrity (shield checkmark), and Availability (clock) at each vertex. Threat labels point inward: Data Breach threatens Confidentiality, Tampering threatens Integrity, Denial of Service threatens Availability. Center: CIA TRIAD.

Question 17 Answer

In the CIA Triad for security, what do Confidentiality, Integrity, and Availability mean?

  1. Confidentiality means data is only accessible to authorized parties; Integrity means data is not tampered with; Availability means the system is accessible when needed
  2. Confidentiality means fast response times; Integrity means code compiles; Availability means 24/7 customer support
  3. Confidentiality means using encryption everywhere; Integrity means using checksums; Availability means using load balancers
  4. These are three types of software testing methodologies

Question 18: Graceful Degradation

A CookYourBooks developer adds a feature that calls a remote nutrition API. During testing, the API occasionally takes 30 seconds to respond. The developer adds a 5-second timeout and returns cached nutrition data when the API is slow, along with a warning that the data may not be up-to-date. This design applies which distributed systems concept?

  1. Horizontal scaling — adding more servers to handle the load
  2. The CIA Triad — ensuring confidentiality of nutrition data
  3. Conway's Law — the team structure determines the API design
  4. Graceful degradation — providing reduced but functional service when a dependency is unavailable

Vertical and Horizontal Scaling (Servers)

How does the system handle growth in load, data, or users?

Vertical Scaling

Image showing (from left to right) a small computer, a large arrow pointing up, a big computer

Horizontal Scaling

Image showing (from left to right) a single computer, a large arrow pointing right, two identical computers

Lecture 19: Architectural Styles

Vertical and Horizontal Scaling (People)

Left: an exhausted lone 'superhero' programmer juggling architecture, UX, testing, deployment, and support tickets simultaneously, dropping most of them. Right: a calm team of 5, each handling one area expertly. A red X crosses out '10x' over the hero; a green checkmark appears over the team.
Lecture 22: Teams and Collaboration

Question 18 Answer

A CookYourBooks developer adds a feature that calls a remote nutrition API. During testing, the API occasionally takes 30 seconds to respond. The developer adds a 5-second timeout and returns cached nutrition data when the API is slow, along with a warning that the data may not be up-to-date. This design applies which distributed systems concept?

  1. Horizontal scaling — adding more servers to handle the load
  2. The CIA Triad — ensuring confidentiality of nutrition data
  3. Conway's Law — the team structure determines the API design
  4. Graceful degradation — providing reduced but functional service when a dependency is unavailable

Question 19: Brooks' Law

According to Brooks' Law, what happens when you add more developers to a late software project?

  1. The project gets even later because communication overhead grows quadratically with team size
  2. The project gets back on schedule because there are more people working
  3. The project speed is unaffected because individual productivity stays the same and new developers can immediately work on independent tasks
  4. The project finishes faster but with lower quality code

Brooks' Law

Four panels showing communication paths growing quadratically: 2 people with 1 path (clean), 4 people with 6 paths (busy), 8 people with 28 paths (tangled web), 16 people with 120 paths (total chaos). Formula: n(n-1)/2. Brooks quote: Adding manpower to a late software project makes it later.

Question 19 Answer

According to Brooks' Law, what happens when you add more developers to a late software project?

  1. The project gets even later because communication overhead grows quadratically with team size
  2. The project gets back on schedule because there are more people working
  3. The project speed is unaffected because individual productivity stays the same and new developers can immediately work on independent tasks
  4. The project finishes faster but with lower quality code

Question 20: Open Source Licensing

A startup is considering including open-source libraries in their proprietary (closed-source) commercial product. Should the specific licenses of the libraries affect their decision?

  1. No, they should make the decision purely based on the library quality
  2. No, all open-source licenses forbid inclusion in commercial code, so they cannot use any of them
  3. Yes, some open-source libraries require derivative works to also share their source code
  4. Yes, some open-source licenses provide quality guarantees, which will make their product more reliable

💡Be skeptical of answers that say "all", "always", or "never".

Two Philosophies: Maximize Adoption vs. Protect the Commons

Two maker spaces in a shared warehouse. Left bench (BSD/MIT): blueprints freely available, someone takes one into a closed Proprietary Product door. Right bench (GPL): four workshop rules posted (use any tool, take apart machines, copy blueprints, share improvements) with a sign reading 'Free as in free speech, not as in free beer' and a Stallman-like figure gesturing proudly. Both benches are busy and productive.
Lecture 23: Open Source Frameworks

Question 20 Answer

A startup is considering including open-source libraries in their proprietary (closed-source) commercial product. Should the specific licenses of the libraries affect their decision?

  1. No, they should make the decision purely based on the library quality
  2. No, all open-source licenses forbid inclusion in commercial code, so they cannot use any of them
  3. Yes, some open-source libraries require derivative works to also share their source code
  4. Yes, some open-source licenses provide quality guarantees, which will make their product more reliable

Announcement

ASNUO is hosting a peer-led midterm review session for CS3100 on Monday (March 16) from 8–10 PM in GSB 101. The session is drop-in style, and students from any section are welcome. Snacks and energy drinks(!) will be provided.

Conclusion

Quizmanship

  • Remember what we are emphasizing in this class: scalable software, not:
    • optimizing performance
    • Java details
  • Be skeptical of answers with "always", "all", or "never".

Topics

  • We've reviewed many important topics from Lecture 9 to Lecture 24.
  • There are other important topics I did not review, including (but not limited to)
    • Domain Modeling
    • Fallacies of Distributed Computing
    • REST APIs
    • Teams & Collaboration
  • I am happy to do so in the remaining time or to go through Prof. Bell's slides.

Bonus Slide

Frank and Ernest comic strip in which Frank gives Ernest an electricity quiz.
Question 1: 'What do we use to carry electricity?' Ernest answers 'why... err...' [wire] (correct).
Question 2: 'What is the name for a unit of energy?' Ernest answers 'What! [Watt]' (correct).
Question 3: 'Can you tell me the term for when current passes through your body?'
Ernest answers 'Well, that would be a shock!' — which is both the correct answer and his reaction to getting a perfect score.