Chapter 11 Flashcards — Systems

flashcards clean-code systems dependency-injection architecture

What is the central thesis of Clean Code Chapter 11 about systems?
?
Construction (building the object graph at startup) and use (runtime business logic) are fundamentally different activities. Conflating them produces tight coupling, untestable classes, and hard-to-change architectures. The fix: separate them completely.

What does “separation of construction from use” mean in practice?
?
A class should receive its dependencies fully constructed — via constructor, setter, or method parameters — rather than calling new on them internally. All new calls belong in main, a factory, or a DI container.

Why is mixing new inside a business class harmful?
?
Three problems: (1) tight coupling — changing the concrete class requires changing the business class; (2) untestability — you cannot substitute a fake without changing production code; (3) SRP violation — the class now has two jobs: building its world and doing its work.

What is the “separation of main” idiom?
?
Move all object graph construction to main (or a bootstrap module). The application receives fully-constructed objects from main and has no knowledge of how they were assembled. The dependency arrow is one-way: main → application. The application can never reach back into main.

What is the Abstract Factory pattern and when does it appear in system design?
?
A factory interface that the application calls to create objects at runtime. The concrete factory (which knows how to construct things) is wired in main. Use it when the application must control when an object is created (e.g., one ShippingLabel per order) but should not know the construction details.

What are the three forms of Dependency Injection?
?
Constructor injection — dependencies passed at construction time; object is immediately usable; preferred for required deps. Setter injection — dependencies set after construction; allows optional deps and circular graphs. Method injection — dependency passed as a method argument; used when only one operation needs it.

Why is constructor injection preferred over setter injection?
?
Constructor injection makes all dependencies visible and required at construction. The object is always in a valid, fully usable state. With setter injection, the object can exist in an incomplete state — a method call before a required setter is called will crash at runtime rather than at construction.

How does a DI container (e.g., Spring) differ from manual DI?
?
A DI container reads annotations or configuration to automatically resolve and inject dependencies across the whole application. Manual DI wires the graph by hand in main. Containers are more convenient for large applications with many collaborators; manual DI is sufficient and often clearer for small/medium applications.

What is a cross-cutting concern? Give three examples.
?
A concern that applies across many classes but is not the primary responsibility of any of them. Examples: transaction management, audit logging, security/authorization, caching, retry logic, metrics collection. They cross module boundaries and would be duplicated if handled inline.

What problem does AOP (Aspect-Oriented Programming) solve?
?
AOP externalizes cross-cutting concerns into aspects — separate modules applied declaratively (e.g., @Transactional, @Cacheable). Without AOP, transaction management, logging, and security checks get copy-pasted into every service method, tangling the concern with business logic and making it hard to change independently.

How does Python’s decorator pattern serve as AOP for cross-cutting concerns?
?
A Python decorator wraps a function, running code before and after the original call. The decorated function stays clean (pure business logic). The decorator applies the cross-cutting concern (e.g., logging, timing, auth check) from outside — analogous to a Spring @Around advice.

How does the C++ Decorator structural pattern externalize a cross-cutting concern?
?
Create a wrapper class that implements the same interface as the wrapped object. The wrapper adds the cross-cutting behavior (logging, metrics) by calling before/after logic around the delegate’s method. The original class is untouched; the wrapper is injected in place of the original.

What is the difference between a DI container and a Service Locator?
?
A DI container pushes dependencies into classes at construction time — the class never looks anything up. A Service Locator is a global registry that classes query to find their dependencies — the class actively pulls its deps. Service Locator is a hidden dependency and makes classes harder to test; prefer DI containers.

What does “scaling up incrementally” mean in the context of system architecture?
?
Start with a simple architecture (layered monolith, single database). Invest in clean separation of concerns (DI, interfaces, small classes). As load and requirements grow, extend the architecture (extract services, add caching, introduce queues) guided by real measurements, not speculation. A clean architecture makes this possible without rewriting.

What architectural decisions should be deferred, and which should be made early?
?
Defer: sharding strategy, caching topology, async vs. sync processing, specific service boundaries — until you have real load data. Decide early: API contracts (hard to change once clients depend on them), data model fundamentals, technology choices that affect all teams. Use proofs of concept before committing to hard-to-reverse decisions.

What is a Domain-Specific Language (DSL) and what problem does it solve?
?
A DSL is a language (or API) designed for a specific domain (e.g., build scripts, test specs, HTTP routing). It solves the communication gap between domain language (what business experts say) and code (what computers understand). A good DSL lets domain concepts be expressed directly in code, reducing misunderstandings and improving readability.

What is the difference between an internal DSL and an external DSL?
?
An internal DSL is a fluent API in the host language (e.g., Java Criteria API, Python builder chains, C++ builder pattern). An external DSL is a separate language parsed independently (e.g., Gradle, Gherkin/Cucumber, SQL). Internal DSLs are easier to build; external DSLs have richer syntax control.

Give an example of a cross-cutting concern handled badly (inline) vs. handled well (externalized).
?
Badly: every service method opens a JDBC connection, sets autoCommit(false), manually commits in try, rolls back in catch, and closes in finally. This code is duplicated across 20 services. Well: @Transactional annotation on the method; Spring AOP handles the transaction lifecycle; the method body contains only business logic.

Why does a testable design naturally tend toward better architecture?
?
Writing tests forces you to construct objects in isolation. If a class uses new internally, you cannot substitute fakes — the test becomes an integration test. Testability pressure pushes you toward constructor injection, interface-based dependencies, and small focused classes — exactly the architecture patterns that make systems clean and extensible.

What is wrong with using new inside a class for a collaborator?
?
It creates a hardcoded dependency: the class is bound to one specific implementation forever. You cannot test with a fake, cannot swap implementations, and must change the class every time the collaborator changes. The class violates both OCP (open for extension) and SRP (it decides who its collaborators are).

In Spring, what is the difference between constructor injection and field injection (@Autowired on a field)?
?
Constructor injection is explicit — dependencies are visible in the signature, the object is immediately valid, and tests can inject fakes without a Spring context. Field injection is implicit — the field is private, the only way to set it in a test is via reflection or a Spring test context. Field injection hides dependencies and makes testing harder; prefer constructor injection.

What is the role of a Dependency Injection container at application startup?
?
The container reads the application configuration (annotations, XML, or code), resolves the dependency graph (determines which implementation satisfies each interface), instantiates classes in the correct order, injects dependencies, and hands back the fully-wired application object. The application code never calls new.

When is manual DI preferable to a DI container?
?
Manual DI (wiring in main) is preferable when: (1) the application is small with few collaborators; (2) you want zero framework magic — every dependency is explicit in code; (3) startup overhead matters (containers have reflection cost); (4) you want the entire object graph visible in one place.

What does “test-drive your architecture” mean in practice?
?
Build a simple end-to-end slice first (controller → service → repository) and write tests that cover it. Measure real performance. Identify real bottlenecks. Extend the architecture (caching, async, sharding) only where tests and measurements justify it. Avoid speculative complexity introduced before real problems exist.

What is the key signal that you need AOP or decorators rather than inline cross-cutting logic?
?
When the same non-business concern (e.g., audit log, transaction, cache check) appears in 3 or more class methods — copy-pasted or near-identical — it is time to externalize it. Duplication of a cross-cutting concern is the tell-tale sign; AOP or decorators eliminate the duplication at the source.

How does Python’s functools.wraps relate to clean cross-cutting concerns?
?
functools.wraps copies the wrapped function’s metadata (__name__, __doc__) onto the decorator wrapper. Without it, all decorated functions appear to have the same name in stack traces and introspection — hiding which function actually ran. It is a small but important detail for making decorators transparent and debuggable.

What is a Java dynamic proxy and how does it relate to AOP?
?
java.lang.reflect.Proxy creates an object at runtime that implements a given interface and routes all method calls through an InvocationHandler. Spring AOP uses proxies to intercept method calls and run aspect code (logging, transactions). The target object is wrapped by the proxy; callers see the proxy but their code is unchanged.

Total Cards: 27
Review Time: ~22 minutes
Priority: HIGH
Last Updated: 2026-04-14