Design Principles Expert
What is a design principle, and how do principles differ from design patterns and hard rules?
Select the correct answer
A principle is a general guideline for decisions; patterns are reusable solutions and rules are strict requirements.
A principle is a testing convention; patterns are naming standards and rules are performance limits set by tools.
A principle is a concrete code template; patterns are abstract ideals and rules are language-specific syntax limits.
A principle is a strict requirement; patterns are optional styles and rules are loose suggestions for beginners.
How does 'divide and conquer' / decomposition serve as a foundational design principle for managing complexity?
Select the correct answer
It breaks a large problem into smaller independent parts that can be understood and solved separately.
It duplicates a problem across modules so each team can solve the same task and compare final results.
It merges many small problems into one large unit so a single algorithm can process everything at once.
It defers a hard problem until later so simpler features ship first and complexity is handled afterwards.
What does designing for 'readability' as a first-class principle mean, and why is code read far more often than it is written?
Select the correct answer
It optimizes code for human comprehension, since code is read far more often than it is written.
It optimizes code for reuse, since shared components are read far more often than one-off scripts are.
It optimizes code for compiler speed, since machines parse code far more often than humans ever review it.
It optimizes code for line count, since shorter files are read far more quickly than longer verbose ones.
How do violations of design principles contribute to technical debt over time?
Select the correct answer
Violations compound as coupling and duplication accumulate, making later changes slower, riskier, and more costly.
Violations improve short-term speed and reduce debt because skipping structure lets teams ship features faster.
Violations are isolated defects that linter tools remove automatically, so they rarely affect long-term maintenance.
Violations only slow initial delivery, but well-tested code afterward pays the debt back with no interest.
Why do we need design principles at all? What underlying problem, managing complexity, change, or coupling: are they all ultimately trying to solve?
Select the correct answer
They ultimately exist to eliminate all bugs so systems never require testing or review after they ship.
They ultimately exist to standardize syntax so teams write identical-looking code across every project they build.
They ultimately exist to enforce performance so systems run faster and consume fewer resources as they grow.
They ultimately exist to manage complexity so systems stay understandable and changeable as they grow.
What makes a module 'good'? What properties distinguish a well-designed module from a poorly designed one?
Select the correct answer
It has many public methods and global state, exposing internals so callers can tune behavior directly.
It has minimal comments and short names, hiding intent so only original authors can safely modify it.
It has high cohesion and low coupling, exposing a clear interface that hides its implementation details.
It has low cohesion and tight coupling, sharing data widely so other modules integrate with it faster.
How do you define 'simplicity' in software design, and why is simple code often harder to write than complex code?
Select the correct answer
It means using clever concise one-liners; simple code is hard because such tricks need advanced language features.
It means avoiding every abstraction layer; simple code is hard because concrete logic requires far more typing.
It means removing non-essential complexity; simple code is hard because clarity demands deep understanding and rework.
It means writing the fewest lines you can; simple code is hard because compact code always runs much slower.
What is the Single Responsibility Principle and how do you determine if a class has too many responsibilities? What is the 'one reason to change' rule?
Select the correct answer
A class should perform one action per method, ensuring every method changes for exactly one clear reason.
A class should have only one reason to change, meaning it answers to a single actor or responsibility.
A class should contain only one public method so that any change is always isolated to a single function.
A class should depend on only one other class, limiting the ways external changes can affect its behavior.
What does it mean for a class to have 'one reason to change'? Does this mean a class should only have one method?
Select the correct answer
It should never be modified after release, forcing all changes into brand-new subclasses.
It should change only when the database schema changes, ignoring other business rule updates.
It should answer to one actor or responsibility, which may still require several methods.
It should contain exactly one public method and delegate everything else to helper classes.
Why do we use SOLID principles? What specific problems in a codebase are they designed to solve?
Select the correct answer
To enforce a single coding style so every developer writes their functions the same way.
To reduce coupling and make code easier to maintain, change, and extend safely.
To ensure full test coverage by requiring one unit test per class and per public method.
To guarantee faster runtime performance by removing abstraction layers from hot paths.
Explain the Open/Closed Principle. How do you design a module to be open for extension but closed for modification without touching the existing source code?
Select the correct answer
Add new behavior through global configuration flags, since branching on flags avoids writing any new classes.
Add new behavior by copying whole modules, since duplicating existing source avoids breaking any current callers.
Add new behavior by directly editing existing classes, since modifying source keeps all logic in one place.
Add new behavior through new abstractions or subclasses rather than editing existing, tested source code.
Explain the Liskov Substitution Principle. Why is it a problem if a subclass cannot be used interchangeably with its parent class?
Select the correct answer
Subclasses must always call the parent's constructor to ensure the base state is properly initialized first.
Subclasses must not add any new methods beyond those already declared in the parent class's interface.
Subclasses must override every method of the parent so that behavior is fully replaced and stays consistent.
Subtypes must be substitutable for their base types without breaking the program's expected correctness.
What is the 'fat interface' problem, and how does the Interface Segregation Principle help in reducing the impact of changes on client code?
Select the correct answer
Interfaces expose private data fields; ISP hides them behind getters so clients cannot break encapsulation.
Classes implement too many interfaces; ISP merges them into one so clients keep a single clean dependency.
Interfaces grow too large in memory; ISP reduces runtime overhead by loading only the needed method tables.
Clients depend on methods they don't use; ISP splits interfaces so unrelated changes don't affect them.
How do the SOLID principles specifically improve the testability of a codebase?
Select the correct answer
Following SOLID removes the need for tests by guaranteeing correctness through strict type-safe contracts.
Combining responsibilities into large classes reduces the number of test files you must write and maintain.
Depending on abstractions lets you swap real dependencies for mocks, isolating the units under test.
Injecting concrete classes directly makes tests faster because no abstraction layer must be resolved first.
What is the difference between SRP and the Interface Segregation Principle? Why shouldn't we just have one large interface for all related actions?
Select the correct answer
SRP and ISP both mean one method per type, so a single interface would end up violating each principle equally.
SRP applies to interfaces while ISP applies to classes; both aim to reduce the number of methods per file.
SRP limits a class's responsibilities; ISP keeps interfaces small so clients avoid unused method dependencies.
SRP splits large interfaces; ISP splits large classes, together ensuring every module has exactly one caller.
What does it mean to 'program to an interface, not an implementation', and how does this principle enable polymorphism at the design level?
Select the correct answer
Prefer inheritance over composition so subclasses always inherit the parent's concrete behavior.
Expose every internal method publicly so callers can reach the implementation details directly.
Depend on abstractions describing behavior so any implementation can be substituted freely.
Write concrete classes first, then extract interfaces only after the code has fully stabilized.
Why is it better to have many specific interfaces rather than one general-purpose interface? How does this relate to decoupling?
Select the correct answer
Fewer interfaces mean fewer files, which makes the overall codebase simpler to navigate.
One large interface guarantees consistency, so every client shares the same set of methods.
Clients depend only on the methods they actually use, which lowers coupling between them.
Specific interfaces let a single class implement them all, increasing reuse across modules.
Explain the difference between Dependency Inversion (the principle) and Dependency Injection (the pattern). Why should high-level modules depend on abstractions rather than low-level details?
Select the correct answer
DIP inverts control flow at runtime; DI is a compiler feature resolving concrete types automatically at build time.
DIP and DI are identical concepts, since injecting dependencies is the only way to invert module dependencies.
DIP is a principle where modules depend on abstractions; DI is a technique supplying dependencies externally.
DIP means avoiding all dependencies; DI is a way to inject code into classes to reduce the number of imports.
If a codebase is 'Rigid' and 'Fragile', which design principles are likely being violated?
Select the correct answer
Only Interface Segregation, because fat interfaces are the single sole cause of tightly coupled modules.
Liskov Substitution and Interface Segregation, since subclasses replace parents and clients share interfaces.
Only Dependency Injection, because failing to inject dependencies is the one thing that makes code fragile.
Open/Closed and Dependency Inversion, since tight coupling makes changes ripple out and break code.
Why is the Interface Segregation Principle important for compiled languages vs. interpreted languages?
Select the correct answer
In interpreted languages, fat interfaces cause runtime type errors that compiled languages catch at build time.
In interpreted languages, segregation reduces parsing time, which compiled languages avoid via binary caching.
In compiled languages, interfaces are optional, so segregation only matters when using dynamic interpreted code.
In compiled languages, changing a fat interface forces recompilation of all clients, even unaffected ones.
In what scenarios might strictly following SOLID principles lead to over-engineering or unnecessary complexity?
Select the correct answer
In large systems where multiple teams must coordinate on shared modules and contracts.
In small or short-lived projects where extra abstraction adds cost without a clear payoff.
In performance-critical code where interface calls are always slower than direct method calls.
Whenever a class exposes public methods that happen to be called by more than one client.
Explain the Liskov Substitution Principle without using the word 'inheritance'. Why is it a violation if a subtype throws an exception for a method defined in the base type?
Select the correct answer
A base type must expose only abstract methods so each subtype can define its own behavior.
A subtype must be usable wherever the base type is; an unexpected exception breaks that.
A subtype may narrow accepted inputs and widen outputs while keeping the same method names.
Two types are interchangeable whenever they simply share the same set of public methods.
What is behavioral subtyping? Can you explain why a 'Square' inheriting from a 'Rectangle' is often cited as a violation of the Liskov Substitution Principle?
Select the correct answer
Subtypes must honor supertype contracts; Square breaks the independent width and height of Rectangle.
Subtypes must reuse constructors; Square breaks because it accepts a single dimension unlike Rectangle.
Subtypes must add new methods; Square fails because it removes the area method that Rectangle defines.
Subtypes must share field names; Square differs by storing one side while Rectangle stores two sides.
What is the difference between Coupling and Cohesion? Why is 'High Cohesion, Low Coupling' considered the gold standard of software design?
Select the correct answer
Coupling measures test coverage; cohesion measures how many comments the code contains.
Coupling is the number of classes; cohesion is the number of methods inside each class.
Coupling is inter-module dependence; cohesion is how focused one module's tasks are.
Coupling is compile-time linkage; cohesion is the runtime memory shared across modules.
What is the difference between coupling and cohesion? Which one is more dangerous to get wrong?
Select the correct answer
Coupling is dependency between modules, cohesion is focus within one; high coupling is more dangerous.
Coupling measures reuse and cohesion measures test scope; both are equally dangerous to get wrong here.
Coupling is focus within a module, cohesion is dependency between them; low cohesion is more dangerous.
Coupling and cohesion both measure module size, and getting cohesion wrong is far more dangerous overall.
How do you identify 'tight coupling' in a codebase, and what are the symptoms of a system that is too tightly coupled?
Select the correct answer
Each class holds a single responsibility, making the code easy to extend but slow to compile fully.
Duplicated logic appears across files, yet every module can still be deployed independently of others.
Modules communicate only through clean interfaces, so refactoring one rarely affects the others at all.
A small change ripples through many modules, and classes cannot be tested or reused in isolation.
Explain how high cohesion within a module improves the testability and maintainability of the software.
Select the correct answer
A focused single-purpose module has fewer dependencies, so its tests are simpler and changes stay local.
A module split across many files hides complexity, so maintainers can change one part without reading others.
A module doing many tasks shares more code, so fewer test cases are needed and refactoring is much faster.
A module with global state exposes internals, making integration tests easier and unit tests less important.
What is the difference between 'Fragility' and 'Immobility' in software design, and how does high coupling contribute to both?
Select the correct answer
Fragility is slow build times after edits; immobility is code that can never be deleted safely; loose coupling is the shared root cause.
Fragility is breakage in unexpected places after a change; immobility is the inability to reuse code elsewhere; high coupling drives both.
Fragility is the inability to reuse code elsewhere; immobility is breakage in unexpected places after a change; low cohesion drives both.
Fragility is many bugs shipped at release; immobility is a team's reluctance to refactor code; poor naming conventions cause both issues.
What is 'stamp coupling', and how does passing entire objects when only a field is needed hurt a design?
Select the correct answer
A module receives a whole object but uses one field, so it needlessly depends on the full structure and breaks when unrelated parts change.
A module shares a global object with others, so it silently reads stale values and produces wrong results when another module edits them.
A module copies an object before using it, so it wastes memory on duplicates and diverges when the original is later mutated elsewhere.
A module receives one field but needs the whole object, so it repeatedly re-queries the source and slows down when the data set grows large.
When is tight coupling actually acceptable or even preferred over loose coupling?
Select the correct answer
When unit testing needs mocking, tight coupling makes isolating each component far easier to achieve.
When code must be reused across many unrelated modules that each evolve at different rates over time.
When systems are large and distributed, tight coupling helps teams scale their work independently.
When components are tightly related and always change together, extra abstraction only adds complexity.
What is the difference between 'Data Coupling' and 'Control Coupling', and which one is more dangerous for long-term maintainability?
Select the correct answer
Data coupling passes plain values while control coupling passes flags steering logic; control is worse.
Data coupling passes flags while control coupling passes records; control coupling is the safer of them.
Data coupling passes objects while control coupling shares memory addresses; both are equally harmful.
Data coupling shares global state while control coupling passes parameters; data coupling is far worse.
What is 'Temporal Coupling,' and why is it harder to detect than standard data coupling?
Select the correct answer
Objects are created and destroyed too frequently, which the garbage collector struggles to detect at runtime.
Data passed between modules changes type over time, so the compiler cannot flag the mismatch early enough.
Calls must happen in a set order, yet nothing in the method signatures reveals that hidden sequence.
Two modules share the same clock or timer, so timing bugs only appear under heavy production load spikes.
In design terms, what is the difference between 'rigidity' and 'fragility' in a codebase?
Select the correct answer
Rigidity means the code breaks easily; fragility means the code resists any attempt to change it at all.
Rigidity means poor performance under load; fragility means poor readability for new developers reading it.
Rigidity means too many dependencies exist; fragility means too few tests exist to catch regressions early.
Rigidity means changes are hard to make; fragility means changes break unexpected, unrelated parts.
When does coupling become cohesion, at what level of abstraction do related components transition from being 'coupled' to being part of a 'cohesive' module?
Select the correct answer
When components communicate over a network, the latency forces them to become a cohesive unit together.
When components are written by one team, ownership alone turns their coupling into genuine cohesion instead.
When related components serve one responsibility and sit together inside a single module boundary.
When unrelated components share a database, they naturally merge into one cohesive layer of the system.
What is 'Orthogonality' in software design, and how does it reduce the 'ripple effect' when a bug is fixed or a feature is added?
Select the correct answer
Components are layered strictly, so a change to one always flows downward through every layer and updates the lower modules automatically.
Components are duplicated for safety, so a change to one leaves the copies untouched and preserves the original behaviour in other parts.
Components share a single global state, so a change to one is instantly reflected everywhere and stays consistent across the whole system.
Components are independent, so a change to one is isolated and does not propagate unwanted effects into unrelated parts of the system.
Can you walk through the spectrum of cohesion types, from functional down to coincidental cohesion, and explain why some are worse than others?
Select the correct answer
Sequential cohesion (output feeds input) is best; procedural cohesion (steps share data) is worst since it mixes many unrelated data flows.
Coincidental cohesion (elements serve one task) is best; functional (elements grouped arbitrarily) is worst since it has no meaningful relation.
Functional cohesion (elements serve one task) is best; coincidental (elements grouped arbitrarily) is worst since it has no meaningful relation.
Temporal cohesion (elements run at one time) is best; logical cohesion (elements share one task) is worst since it forces a strict ordering.
What is 'content coupling' and 'common coupling', and why are they considered the most harmful forms of coupling?
Select the correct answer
Content coupling is passing whole records around; common coupling is passing single flags as arguments; both create verbose, wasteful links.
Content coupling is one module altering another's internals; common coupling is modules sharing global data; both create hidden, fragile links.
Content coupling is modules sharing global data; common coupling is a module altering another's internals; both create loose, harmless links.
Content coupling is one module calling another's methods; common coupling is modules sharing an interface; both create clean, explicit links.
Why are cyclic dependencies between modules considered harmful, and what is the principle of acyclic dependencies?
Select the correct answer
Cycles let modules share too much global state; the principle requires the dependency graph to route all calls through a central hub.
Cycles make modules run more slowly at startup; the principle requires the dependency graph to be flattened into one single module.
Cycles prevent modules from ever being reused alone; the principle requires the dependency graph to be limited to just three layers.
Cycles force modules to change and be tested together; the principle requires the dependency graph to form a directed acyclic graph.
What is 'connascence' as a way of reasoning about coupling, and how does it give a more nuanced view than simply 'tight vs loose'?
Select the correct answer
It classifies coupling by kind, strength, degree, and locality, so you can compare and rank dependencies instead of judging them binary.
It classifies coupling by runtime cost alone, so you can profile the slowest links in a system instead of reasoning about their structure.
It classifies coupling only by direction and count, so you can total the arrows in a diagram instead of judging their real strength at all.
It classifies coupling by the language used, so you can pick the safest framework for a module instead of measuring dependencies directly.
What is the Law of Demeter (Principle of Least Knowledge)? Why is 'reaching through' an object to access its internal dependencies considered a design smell?
Select the correct answer
An object should only talk to objects of the same class; chaining through others hides internal structure and duplicates code in callers.
An object should only talk to its immediate collaborators; chaining through them exposes internal structure and couples callers to it.
An object should never hold references to others; storing them creates cycles and prevents the garbage collector from freeing the callers.
An object should minimise the number of its methods; calling too many of them wastes memory and slows down the callers at runtime badly.
Explain the 'Tell, Don't Ask' principle. How does it help preserve the encapsulation of an object's state?
Select the correct answer
Notify observers whenever state changes so dependent objects can pull the latest values on demand
Instruct an object to perform an action itself rather than querying its state and deciding externally
Expose public getters and setters for every field so collaborators can read and mutate state freely
Query an object for all its fields first, then let the caller apply the business rules on those values
What is the 'Law of Demeter' (or Principle of Least Knowledge), and how does 'not talking to strangers' help in reducing coupling?
Select the correct answer
An object should minimize the total number of public methods it exposes to keep its overall interface as small as possible
An object should never hold references to other objects and must obtain every collaborator through a global registry
An object should call only its own, its parameters', and its direct collaborators' methods, avoiding long chains
An object should communicate with distant modules only through events, never by calling methods on them directly at all
What does 'Tell, Don't Ask' mean? How does it help in moving logic closer to the data it operates on?
Select the correct answer
Split the object into a data holder and a service so the logic stays in a dedicated stateless helper
Read the object's data into the caller so the shared logic can be reused across many different callers
Cache the object's fields locally so repeated questions about its state avoid extra round trips each time
Send commands to the object owning the data so the decision logic lives beside the state it uses
What is the design goal of 'Information Hiding', and how does it protect a system from the 'ripple effect' of changes?
Select the correct answer
Duplicate shared state across modules so each keeps its own copy and remains isolated from other edits
Conceal volatile decisions behind stable interfaces so changes stay local and don't propagate to clients
Encrypt internal data so unauthorized modules cannot read it, which prevents malicious changes from spreading
Publish every internal detail up front so clients adapt early and no surprises ripple through later
Why do we need Abstraction at all? What is the difference between Abstraction and Information Hiding (Encapsulation)?
Select the correct answer
Abstraction is only for interfaces and base classes; hiding applies exclusively to concrete implementation classes
Abstraction models the essential idea to manage complexity; hiding conceals the details that realize it
Abstraction restricts access to private members; hiding groups related data and behavior into one unit
Abstraction improves runtime speed by removing indirection; hiding adds layers that slow the whole system down
What is a 'leaky abstraction', and how does it violate the principle of encapsulation?
Select the correct answer
An abstraction that leaks implementation details callers must know, undermining the hiding encapsulation promises.
An interface that hides too much behavior, so callers cannot access any of the useful underlying details.
A design where private fields are exposed via getters, letting external code freely modify their state.
A memory leak caused when an object retains references and prevents the garbage collector from reclaiming it.
What is the 'Feature Envy' code smell, and which principle guides you to move behavior closer to the data it uses?
Select the correct answer
A method with far too many parameters; the Single Responsibility principle splits it into separate concerns.
A class with too many public methods; the Interface Segregation principle splits it into smaller focused roles.
A method that duplicates logic elsewhere; the Don't Repeat Yourself principle extracts it into a shared helper.
A method obsessed with another class's data; the Information Expert principle moves behavior to that data.
What does 'Encapsulate what varies' mean as a design principle, and how do you identify the parts of a system most likely to change?
Select the correct answer
Freeze stable core logic into constants; find them by locating code that has never once been modified before.
Hide all internal fields behind getters and setters; find them by checking which variables are declared private.
Duplicate volatile code across modules for safety; find them by measuring which functions get called most often.
Isolate the parts likely to change behind stable interfaces; find them by spotting requirements and details that shift.
What is the Single Level of Abstraction Principle (SLAP), and how does mixing abstraction levels in one function hurt readability?
Select the correct answer
Each function should operate at one level of abstraction; mixing high- and low-level steps hurts readability.
Each function should have a single return point; mixing multiple exits scattered through the body hurts readability.
Each module should depend on one other layer; mixing calls across many layers in one place hurts readability.
Each class should expose only one public method; mixing several unrelated operations in one type hurts readability.
What is the difference between Encapsulation and Information Hiding, and why is hiding the reason for a design choice as important as hiding the data itself?
Select the correct answer
Encapsulation blocks all external access entirely while hiding merely renames fields so their meaning stays private
Encapsulation is a runtime security feature while hiding is only a compile-time restriction enforced by the language
Encapsulation exposes internal structure to subclasses while hiding prevents any inheritance of the concealed members
Encapsulation bundles data with behavior; hiding conceals decisions likely to change so clients don't depend on them
Explain the concept of 'Leaky Abstractions.' Why is it impossible to have a 'perfect' abstraction?
Select the correct answer
An abstraction leaks when underlying implementation details surface, because it cannot fully hide every real-world complexity
An abstraction leaks when memory is not released, because hidden resources gradually accumulate and eventually degrade performance
An abstraction leaks when it depends on another layer, because transitive dependencies always make a design impossible to test
An abstraction leaks when it exposes too few methods, because callers must then bypass it to finish any useful work
How do you decide the right level of abstraction for a component, and what happens when an abstraction is 'leaky'?
Select the correct answer
Pick the abstraction that requires the least code to write; a leak just indicates a performance bottleneck somewhere
Match abstraction to the client's needs and hide the rest; a leak forces clients to know the internals
Mirror the database schema as closely as you can; a leak means the underlying storage engine has been swapped out
Always choose the most generic abstraction possible; a leak simply means the component needs more public methods
Explain the conceptual difference between an 'is-a' relationship and a 'has-a' relationship at the design level.
Select the correct answer
'Is-a' models two identical classes merged; 'has-a' models a class copying fields from another at construction.
'Is-a' models an object holding a reference; 'has-a' models a class extending another to inherit its behavior.
'Is-a' models a subtype that is a kind of its parent; 'has-a' models one object owning another as a part.
'Is-a' models interface implementation only; 'has-a' models runtime creation of temporary objects in a method.
Why is 'composition over inheritance' a common design maxim, and what are the risks of deep inheritance hierarchies (the fragile base class problem)?
Select the correct answer
Inheritance runs faster at runtime; deep composition chains add indirection that slowly degrades overall performance.
Inheritance hides implementation better; deep composition exposes internal helper objects to unrelated calling code.
Composition gives flexible runtime behavior; deep inheritance couples subclasses so base changes silently break them.
Composition avoids all code reuse; deep inheritance forces subclasses to reimplement every inherited method again.
Why is deep inheritance often considered a code smell, and what is the 'Fragile Base Class' problem?
Select the correct answer
Changes to a base class can unexpectedly break distant subclasses that depend on its internal behavior.
Adding a new subclass forces recompiling every unrelated class living in the same package hierarchy.
Subclasses always run slower because each method call must traverse the entire chain of parent classes.
Base classes cannot define abstract methods, so subclasses are forced to duplicate shared logic everywhere.
What does it mean to 'favor composition over inheritance', and in what specific scenarios is inheritance still the superior choice?
Select the correct answer
Compose objects for flexibility; inheritance fits a true is-a relationship with stable, substitutable base behavior.
Compose objects for speed reasons; inheritance fits any deep hierarchy where subclasses override most parent methods.
Compose objects to save memory; inheritance fits any case where two classes happen to share a few methods.
Compose objects to hide fields; inheritance fits whenever you want to reuse code without writing wrapper types.
Why is it often said that inheritance breaks encapsulation? In what scenarios is composition a safer choice for code reuse?
Select the correct answer
Inheritance is slower at runtime than composition, so composition should be preferred whenever memory usage and raw execution performance are top concerns.
Inheritance forces every subclass to be public, so composition should be used to keep helper classes private and fully hidden from all external callers.
Inheritance prevents polymorphism between types, so composition is required whenever different objects must share one common interface at runtime.
Subclasses rely on the base class's internal implementation, so base changes can silently break them; composition reuses behavior via a stable interface.
Explain the 'Rule of Three' in the context of refactoring toward DRY. Why shouldn't we abstract logic the first time we see repetition?
Select the correct answer
Abstract only after three developers have independently written the same code, because that proves the logic is genuinely reusable across separate teams.
Refactor duplication the very first time it appears, since delaying until a third copy exists makes the eventual abstraction far harder to extract.
Wait until code repeats a third time before abstracting, because two occurrences may be coincidental and the true shared pattern isn't yet clear.
Always split shared logic into exactly three separate layers, since fewer layers leave code coupled and more than three adds needless indirection cost.
Explain the KISS principle. How does adding layers of abstraction often violate this, and what is the cost to the team?
Select the correct answer
Keep designs as simple as possible; needless abstraction layers add indirection that raises cognitive load and slows the team's understanding.
Keep dependencies few; abstraction layers violate this by importing libraries, and the cost is a larger deployment artifact and slower startup time.
Keep interfaces stable; abstraction layers violate this by changing method signatures, and the cost is that dependent modules break during releases.
Keep every class small; abstraction layers violate this by growing file sizes, and the cost is that builds and automated test suites run slower.
How does 'premature optimization is the root of all evil' function as a design principle, and how do you decide when optimization is actually warranted?
Select the correct answer
It requires that all performance work be delegated to compilers rather than any manual effort
It says you should optimize every hot path up front so later refactoring becomes unnecessary
It means optimization should be avoided entirely because clean code always outperforms tuned code
It warns against tuning before knowing bottlenecks; optimize once profiling proves a real need
What is the Single Source of Truth principle, and how does it differ from DRY?
Select the correct answer
Both are identical rules requiring that code and data never be repeated anywhere in a system
Each piece of data has one authoritative home; DRY targets duplicated logic rather than data
It centralizes all business logic in one class; DRY concerns only naming conventions used
Each module owns its own data copy; DRY instead mandates copying logic across every layer
Explain the 'You Ain't Gonna Need It' (YAGNI) principle. How do you balance designing for future extensibility vs. avoiding speculative generality?
Select the correct answer
Avoid writing any abstractions at all until the product ships, then rewrite everything from scratch once the real requirements finally become known.
Build every plausible future feature upfront, since retrofitting later is costlier; extensibility should always be prioritized over short-term simplicity.
Build features only when actually required, not on speculation; add extensibility once there is concrete evidence of a genuine, current need.
Design the most generic solution possible early, because flexible frameworks reduce long-term risk more than solving today's narrow, immediate problem.
What is the 'Don't Repeat Yourself' (DRY) principle, and when can it be taken too far? What is 'wrong abstraction' in the context of DRY?
Select the correct answer
Each piece of knowledge has one authoritative source; over-applied, it couples unrelated code that only looks similar into a wrong abstraction.
Identical-looking code must always be merged immediately; a wrong abstraction is one that was extracted too late after the third or fourth repetition.
Every line of code must appear only once anywhere; a wrong abstraction is any duplicated helper method that two different modules happen to call at once.
All shared logic belongs in a base class; a wrong abstraction is when composition is used instead of inheritance to avoid repeating common behavior.
How do you determine if a solution is 'KISS' or just 'under-engineered'?
Select the correct answer
KISS means the code has the fewest lines possible; under-engineered means it uses more lines than a senior developer would have written for it.
KISS means the solution passes every test today; under-engineered means it will eventually need refactoring once new features are requested later.
KISS solves the actual requirements with the least complexity; under-engineered omits handling of real, known requirements or important edge cases.
KISS means avoiding all design patterns entirely; under-engineered means adding patterns before the team has agreed they are truly necessary here.
How do you distinguish between 'essential complexity' and 'accidental complexity' when reviewing a design?
Select the correct answer
Essential complexity is any logic in the core domain layer; accidental complexity is any logic that lives in the infrastructure or persistence layers.
Essential complexity is code that cannot be unit tested easily; accidental complexity is code that has full test coverage but is still hard to read.
Essential complexity is complexity the customer explicitly requested; accidental complexity is any behavior the developers added without a written ticket.
Essential complexity is inherent to the problem itself; accidental complexity comes from implementation choices, tooling, or design and can be removed.
How do you balance the KISS principle with the need to adhere to more complex principles like SOLID or Design Patterns?
Select the correct answer
Ignore KISS whenever a design pattern applies, since patterns are proven industry standards and following them always outweighs keeping code simple.
Treat KISS and SOLID as mutually exclusive, choosing KISS for prototypes and switching entirely to SOLID once the project reaches production scale.
Always apply every SOLID principle and known pattern upfront, because a fully decoupled design is inherently simpler for teams to maintain over time.
Apply patterns and SOLID only when they reduce real complexity or solve a present problem; otherwise the simpler solution better honors KISS.
What is the 'Principle of Least Commitment' (deferring decisions), and how does it help keep designs flexible?
Select the correct answer
Always pick the simplest algorithm first and never revisit it regardless of new requirements
Delay binding choices until needed, keeping options open so late changes stay cheap and easy
Assign the smallest possible responsibility to each class so coupling between them drops sharply
Commit to every design decision early so the whole team shares one fixed and stable plan
How does 'Separation of Concerns' differ from the Single Responsibility Principle?
Select the correct answer
SoC partitions a system into distinct concerns; SRP says a class has one reason to change
They are interchangeable names for the same rule that every function should do one thing
SoC applies only to classes while SRP governs how whole subsystems are split into layers
SoC forbids any shared state whereas SRP simply requires methods to be kept short overall
How does the principle of Separation of Concerns help in managing cognitive load for developers working on a large codebase?
Select the correct answer
It reduces the total line count so developers simply have fewer characters left to read overall
It isolates concerns so a developer reasons about one module without holding the whole system
It forces every developer to memorize all module interactions before any change can be made
It merges related concerns into one large module so all context lives in a single readable file
What is Inversion of Control as a concept, and how does the 'Hollywood Principle' (don't call us, we'll call you) relate to it?
Select the correct answer
A framework controls flow and calls your code back, which is what the Hollywood Principle states
It means inverting class hierarchies, and the Hollywood Principle is about avoiding deep subclassing
It is another name for dependency injection only, unrelated to any callback or framework flow
Your code drives the framework by polling it, and the Hollywood Principle describes that polling
What is the GRASP 'Indirection' principle, and when does adding a layer of indirection help versus hurt a design?
Select the correct answer
Always add wrapper layers between objects since more indirection always lowers coupling everywhere
Insert a mediating object to reduce coupling; it hurts when the extra layer adds needless complexity
Assign responsibility to the class holding the most data, avoiding any mediator between components
Remove all intermediaries so objects talk directly, which keeps coupling low and code easy to trace
Explain the 'Information Expert' principle. How do you decide which class should be responsible for a specific piece of logic?
Select the correct answer
Assign the responsibility to the class nearest the user interface receiving the initial request
Assign the responsibility to the class that already holds the data needed to carry it out
Assign the responsibility to a dedicated manager class that coordinates all the other classes
Assign the responsibility to whichever class currently has the fewest methods defined on it
What are the GRASP principles (e.g., Information Expert, Pure Fabrication), and how do they supplement SOLID?
Select the correct answer
Rules that replace SOLID entirely by defining stricter constraints for every class in a large system
Patterns for assigning responsibilities to objects, guiding the fine-grained choices SOLID leaves open
Testing heuristics that verify SOLID compliance by measuring coupling metrics across all the modules
Deployment guidelines describing how responsibilities map to services, extending SOLID to the network
What is the GRASP 'Protected Variations' principle, and how does it relate to the Open/Closed Principle?
Select the correct answer
Freeze all released classes entirely so that no future variation to their code is ever permitted
Guard shared mutable state with locks so concurrent variations cannot corrupt the object's data
Split every class into two layers so that reads and writes vary entirely independently of each other
Wrap predicted points of instability behind a stable interface so change cannot ripple outward
What is the GRASP 'Controller' principle, and what problem does it solve in assigning responsibilities?
Select the correct answer
Assign every incoming request to the class with the most information about that particular event
Assign event handling to the widgets themselves so each screen fully manages its own business rules
Assign responsibilities to a single god object that centralizes and owns all application behaviour
Assign handling of system events to a use-case coordinator, keeping domain logic out of the UI
Explain the GRASP 'Creator' principle: how do you decide which class should be responsible for creating instances of another?
Select the correct answer
Let the class that most frequently calls the object's methods be the one responsible for creating it
Let whichever class defines the most abstract interface create the concrete implementing subclasses
Let a dedicated factory class create every object in the system to keep construction fully centralized
Let a class create instances it aggregates, contains, records, or has the data to initialize
Explain the principle of Command-Query Separation. Why should a method that changes state not return a value, and vice versa?
Select the correct answer
Every public method should combine a read and a write so callers make only one round trip to it
Queries change state and return data, while commands merely read state without altering anything at all
Commands and queries must both return a status code so callers can verify the operation succeeded
Commands change state and return nothing; queries return data and cause no side effects
What is the 'Fail-Fast' design principle, and why is it better for a system to crash early rather than continue in an unstable state?
Select the correct answer
Detect invalid conditions and stop immediately, preventing corrupted state and making the bug visible
Retry invalid conditions repeatedly and hope they resolve, keeping the service available to end users
Route invalid conditions to a backup instance, so the primary process can keep serving traffic normally
Suppress invalid conditions and keep running, deferring the failure until a scheduled maintenance window
What is defensive programming, and how does it relate to 'Design by Contract' (preconditions and postconditions)?
Select the correct answer
It is identical to Design by Contract, since both require every method to verify its own postconditions before returning any result to the caller.
It codes only the happy path and trusts all callers; Design by Contract adds the runtime checks that defensive programming deliberately leaves out.
It codes against invalid states and misuse; Design by Contract instead assumes preconditions hold, shifting that duty explicitly to the caller.
It replaces contracts entirely by throwing exceptions, whereas Design by Contract forbids exceptions and relies purely on validated return codes.
What is the 'Fail-Fast' principle, how does it differ from 'Defensive Programming', and when should you use one over the other?
Select the correct answer
Fail-fast applies only to compiled languages, while defensive programming applies only to interpreted ones
Fail-fast logs the error and proceeds, while defensive code always throws an exception to the top caller
Fail-fast silently retries the operation, while defensive code halts the whole program on any invalid input
Fail-fast surfaces errors immediately at their source, while defensive code tolerates bad input and continues
Why is idempotence considered a vital design principle for distributed systems and API reliability?
Select the correct answer
Repeating the same request produces the same result, so retries after failures cannot cause duplicate effects
Repeating the same request rolls back prior writes, so the database is guaranteed to stay fully consistent
Repeating the same request runs faster each time, so caching layers can serve the response without new work
Repeating the same request is blocked outright, so the server rejects any duplicate call within a time window
Explain the concepts of Preconditions, Postconditions, and Invariants, and how they help create a 'contract' between a caller and a callee.
Select the correct answer
Preconditions and postconditions are both the caller's duty, while invariants are optional runtime checks the callee may skip in release builds.
Preconditions describe return values, postconditions describe input arguments, and invariants are the exceptions a method is permitted to throw on error.
Preconditions are the caller's duty to satisfy before a call, postconditions are the callee's guarantee afterward, and invariants stay true throughout.
Preconditions are the callee's guarantee before a call, postconditions are the caller's duty afterward, and invariants hold only at startup time.
Explain Postel's Law (The Robustness Principle). When should you be 'liberal in what you accept'?
Select the correct answer
Be strict about both input and output at all times, since accepting loose input always propagates corrupt data through the system.
Be tolerant of varied or imperfect input at system boundaries, while sending strictly well-formed, conservative output to others.
Be tolerant of varied output formats you emit, while requiring strictly well-formed input from every caller inside the system.
Be liberal about accepting any input everywhere so validation logic can be removed entirely, keeping both sides of a call simpler.
What are the trade-offs of Defensive Programming? When does it lead to 'cluttered' code that is harder to maintain?
Select the correct answer
Excessive checks on trusted internal calls bury business logic in guard clauses, adding noise without catching real faults.
Validating untrusted external input at the boundary buries the logic in guards and rarely prevents any genuine defect from occurring.
Removing all checks makes code shorter, but the guard clauses it eliminates were the only thing documenting a method's real contract.
Adding checks improves performance so much that the extra lines are always justified, even for calls entirely within a trusted module.
What is a 'God Object' (or Blob), and why is it considered an anti-pattern in object-oriented design?
Select the correct answer
An interface implemented by every class; bad because it forces unrelated types to share unneeded method signatures.
A single class concentrating most logic and data; bad because it has low cohesion, high coupling, and resists testing.
A class exposing only static methods; bad because it prevents inheritance and makes mocking it in tests impossible.
A tiny class delegating to many helpers; bad because it adds needless indirection and hides behavior from readers.
What specific design choices make a piece of code 'hard to test,' and which principles (like DIP or ISP) directly address these issues?
Select the correct answer
Using dependency injection makes wiring implicit; the DIP fixes this by requiring classes to construct their own collaborators directly.
Pure functions with no side effects resist testing; the ISP fixes this by adding shared global state that assertions can then read.
Hard-coded concrete dependencies block substitution; the DIP fixes this by depending on abstractions injectable in tests.
Small focused interfaces block mocking; the ISP fixes this by merging them into one broad interface that tests can stub in a single place.
What design principle does a 'God Object' violate, and what is the standard strategy for refactoring it?
Select the correct answer
It violates the Liskov Substitution Principle; make it a base class others can freely substitute for, preserving all inherited behavior.
It violates the Single Responsibility Principle; extract its distinct responsibilities into smaller focused, collaborating classes.
It violates the Dependency Inversion Principle; introduce an interface it implements so callers depend only on that shared abstraction.
It violates the Open/Closed Principle; add subclasses that override its methods so behavior can be extended without any modification.
What are the benefits of 'programming to an interface, not an implementation', and how does this principle facilitate unit testing?
Select the correct answer
Callers depend on a concrete class, so the compiler inlines calls, and tests run faster by exercising the real production object each time.
Callers share one global implementation, reducing object count, and tests verify behavior directly against that single shared instance's state.
Callers know the exact type, enabling private fields to be read in assertions, so tests need no mocks and cover more real internal paths.
Callers depend on an abstraction, so implementations swap freely and tests inject mocks or fakes for the real collaborator.
What are some common 'code smells' (like Feature Envy or Shotgun Surgery) that indicate a violation of a specific design principle?
Select the correct answer
Feature Envy means duplicated code across modules, and Shotgun Surgery means one method grows too long and must be broken into helper functions.
Feature Envy means a class hides too much data, and Shotgun Surgery means a single class absorbs every responsibility that should be split up.
Feature Envy means an interface has too many methods, and Shotgun Surgery means a subclass cannot substitute cleanly for its declared base type.
Feature Envy shows misplaced responsibility, and Shotgun Surgery shows scattered concerns that one change forces across many classes.
What is 'Shotgun Surgery', and which design principle (SRP or Cohesion) is typically violated when this smell appears?
Select the correct answer
Duplicated code forces the same edit in many places; it violates DRY throughout the whole codebase repeatedly.
A single change forces many small edits across many classes; it violates cohesion because one responsibility is scattered.
One class doing many things forces edits when any concern changes; it violates SRP by concentrating responsibilities.
One change makes many classes change for differing reasons; it violates the Open/Closed Principle very broadly.
What are the code smells 'needless complexity' and 'opacity', and which design principles address them?
Select the correct answer
Needless complexity is deep inheritance (favor composition); opacity is dead code removed by regular refactoring passes.
Needless complexity is unused over-engineered abstraction (YAGNI/KISS); opacity is unclear code fixed by clarity.
Needless complexity is duplicated logic (DRY); opacity is tight coupling addressed by dependency inversion between modules.
Needless complexity is premature optimization (KISS); opacity is global state fixed by encapsulation and scoping.
What is 'viscosity' as a design smell, and how does it discourage developers from doing the right thing?
Select the correct answer
When builds and tests run slowly, developers avoid running them, letting defects quietly accumulate over time.
When preserving the design is harder than hacking, developers take the easy shortcut, gradually eroding the design.
When code is duplicated widely, developers copy still more rather than extract, spreading the same logic further.
When modules depend on concretions, developers add more direct dependencies instead of introducing abstractions.
What is the 'Boy Scout Rule', and how does it help a codebase preserve adherence to design principles as it evolves?
Select the correct answer
Always leave code cleaner than you found it; small continuous improvements prevent gradual design decay.
Rewrite each module fully before adding features; large upfront cleanups keep the design perpetually pristine.
Never modify code you did not originally write; limiting edits keeps the design stable and consistent.
Only refactor during dedicated sprints; batching cleanup avoids risky changes mixed into feature work.
How do you maintain design principle adherence in a team with varying levels of seniority?
Select the correct answer
Let senior developers write all core modules while juniors handle only isolated bug fixes and simple tests.
Rely on automated linters alone so style stays uniform and no human review time is ever really needed.
Document the principles once in a wiki and assume everyone reads and applies them without any follow-up.
Establish shared standards and enforce them through code reviews, pairing, and mentoring rather than titles.
When two design principles conflict, how do you decide which one to prioritize? Can you give an example of a tension like DRY versus decoupling?
Select the correct answer
Weigh context and cost; forcing DRY can couple modules, so some duplication may be better to stay decoupled.
Always prefer DRY; eliminating duplication is the top priority and decoupling should always yield to reuse.
Always prefer decoupling; independence matters most, so duplication is fine and DRY is a minor concern.
Follow a fixed ranking of principles; the higher-ranked one always wins regardless of the specific situation.
When is it acceptable to deliberately break a design principle, and how do you justify that pragmatic decision?
Select the correct answer
Whenever it speeds up initial delivery; justify it by the deadline since principles just slow teams down anyway.
When the concrete cost of following it outweighs the benefit; justify it by documenting the deliberate tradeoff.
Only when a senior developer approves; justify it by their authority rather than by any measured tradeoff involved.
Never break principles in production; justify strict adherence because exceptions almost always cause defects later.
What is the 'Principle of Least Astonishment,' and how do you apply it to API design?
Select the correct answer
APIs should expose every internal option so users can configure any behavior.
APIs should behave as users reasonably expect, so results never surprise them.
APIs should minimize the number of methods to keep the surface area small.
APIs should hide implementation by throwing exceptions on unexpected input.
What is the Principle of Least Privilege, and how does it apply to software design beyond just security?
Select the correct answer
Restrict privileges only at network boundaries where external attackers can reach.
Give each component only the access it needs, limiting coupling and impact of errors.
Give each component full access early so refactoring later becomes easier to manage.
Grant privileges based on developer seniority to keep codebase changes controlled.
Why is global mutable state considered an enemy of good design, and which principles does it violate?
Select the correct answer
It slows the program because global variables are stored far from the CPU cache.
It duplicates data across modules, breaking DRY and increasing binary size at runtime.
It hides dependencies and shared state, breaking encapsulation and hurting testability.
It prevents inheritance because globals cannot be overridden by derived subclasses.
Why is immutability considered a valuable design principle for reducing bugs and coupling?
Select the correct answer
Immutable objects can't change after creation, so they're safe to share and reason about.
Immutable objects run faster since the CPU can skip all bounds and null checks.
Immutable objects enforce inheritance so subclasses always keep the parent behavior.
Immutable objects use less memory because the compiler stores only a single copy.
When should you choose 'Convention over Configuration' in a framework or library design?
Select the correct answer
When the framework supports many languages with incompatible configuration formats.
When performance is critical and defaults must be tuned manually for each case.
When sensible defaults cover most cases, reducing boilerplate while allowing overrides.
When every project needs unique settings that must be declared explicitly upfront.
What are the design trade-offs of 'Convention over Configuration'? When does a convention become a 'magic' hindrance to new developers?
Select the correct answer
It reduces file count but raises memory use because defaults load extra dependencies.
It cuts boilerplate but turns to magic when hidden behavior is hard to discover or debug.
It improves runtime speed but slows compilation when too many defaults are applied.
It enforces consistency but breaks type safety whenever conventions are overridden.