Design Patterns (GoF and modern) Module 10
Anti-patterns and refactoring to patterns interview questions
Interview questions on anti-patterns and refactoring to patterns: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
What is a god object, and how do you start splitting one?
Answered in the lesson Anti-patterns: god objects and golden hammers
-
Fresher to mid
Why is a mutable default argument dangerous in a Python Builder, and what is the fix?
Answered in the lesson Language-specific traps when applying patterns
-
Fresher to mid
What is a code smell, and is every smell a problem that needs fixing?
A code smell is a symptom that is quick to see on the surface of the code and usually points to a deeper problem: the same switch in several functions, a function with many parameters, checks for a missing value everywhere. Kent Beck coined the term while helping Martin Fowler with Refactoring. A smell is a reason to look closer, not a verdict. A long method can be perfectly clear, and a switch that has had three cases for five years and is used in one place costs little. The test is whether the smell makes the next change harder: if new cases keep arriving or the same edit has to be made in several places, remove it with the smallest refactoring that works, and keep the tests passing at every step.
From the lesson Code smells that point to a pattern
-
Fresher to mid
What is a characterization test, and how does it differ from an ordinary unit test?
A characterization test records what existing code does today, right or wrong, so that a later change in behaviour shows up at once; Michael Feathers named the idea in Working Effectively with Legacy Code. An ordinary unit test states what the code should do, written from the requirement. A characterization test is written from the code: you call it with inputs, often many seeded ones plus the boundaries, and write down whatever it answers, including answers that look wrong, such as a negative total. Its job is to make a refactoring safe, not to judge the behaviour. Bugs it reveals are fixed later, as deliberate changes with their own tests, and once the code is understood, focused unit tests replace the recorded answers.
From the lesson Refactoring safely with characterization tests
-
Fresher to mid
What does it mean to refactor toward a pattern, and when would you refactor away from one?
Refactoring toward a pattern means changing existing code in small steps that keep its behaviour, until the pattern's structure appears, for example moving the branches of a conditional into strategy objects once a new rule keeps being requested. Joshua Kerievsky's Refactoring to Patterns treats patterns this way, as destinations reached when the code needs them, not designs drawn up front. Refactoring away from a pattern is the reverse: when the variation the pattern served is gone, such as a factory that only ever makes one class or a Singleton that only makes tests share state, you take the structure out, again in small tested steps. Both directions are decided by evidence in the code's history, and both keep every answer the same at each step.
From the lesson Refactoring toward, and away from, patterns
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to staff
A 3,000-line OrderService is changed by most pull requests and its test suite takes nine minutes. The team cannot stop feature work for a rewrite. How do you split it, and how do you show that nothing broke along the way?
- What it tests
- Choosing where to start from change history, protecting behaviour with characterization tests, extracting behind a facade in small steps, and keeping feature work flowing.
- First step
- Find the hotspots before touching any code: count the commits that changed each method, multiply by its length, and rank. Then group the fields by the job they serve and list the methods that use several groups. The first extraction is the group that changes most, behind the existing public methods.
- Follow-ups
-
- Two teams change the class every week. How do you avoid merge pain during the split?
- There are no tests at all. What do you write first?
- How do you know when to stop splitting?
From the lesson Anti-patterns: god objects and golden hammers
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A pull request replaces the constructor parameters of twenty classes with calls to a global ServiceLocator, to make wiring easier. Do you approve it? What would you ask for or change?
- What it tests
- Telling a trade-off from an anti-pattern: hidden dependencies, coupling to the locator, tests that share a registry, and the difference between application code and a library.
- First step
- Ask what problem the change solves. If constructors grew long, that is a design signal, not a wiring problem. Then name what the locator costs here: a class's signature no longer shows what it needs, and every class now depends on the locator itself.
- Follow-ups
-
- The classes are part of a library that other teams use.
- The author says tests can still register fakes in the locator.
- Some constructors really do take nine parameters.
From the lesson Anti-patterns: god objects and golden hammers
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A Java price cache finds entries by product id and compares the boxed ids with ==. Every test passes, yet in production some products are always cache misses. What is happening, and how do you fix it so that it cannot come back?
- What it tests
- The boxing cache range, reference equality against value equality, why the tests missed it, and turning a one-off fix into a rule that a build checks.
- First step
- Look at which ids miss. If every id above 127 misses and the small ones hit, the comparison is checking identity: the Java Language Specification guarantees one boxed object per value only from -128 to 127, and test ids are usually small.
- Follow-ups
-
- Why did the tests not catch it?
- Would a HashMap with Integer keys have the same bug?
- How do you stop the same mistake elsewhere in the code base?
From the lesson Language-specific traps when applying patterns
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A team ports a report framework from Java to C++. In Java the base constructor calls an overridable title() hook. Explain what that design does in each language, why neither compiler stops it, and how you would redesign it.
- What it tests
- Construction order, dispatch during construction in two languages, and a redesign that removes the base class's need for its subclass while it is being built.
- First step
- Walk through construction. In Java the fields start at their defaults, then the base constructor runs and dispatches to the override, which sees null fields. In C++ the object is only a base object until the derived constructor starts, so the base version runs.
- Follow-ups
-
- What if title() is pure virtual in the C++ version?
- Is calling a private or final method from a constructor safe?
- Where does a factory method fit?
From the lesson Language-specific traps when applying patterns
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A Rust desktop app keeps its observers as Rc<RefCell<Observer>> in each document, and every observer holds an Rc back to its document. Memory grows each time a document window is closed. Explain why, how you would confirm it, and how you would fix it.
- What it tests
- Reference counting and cycles, choosing which side holds a Weak pointer, and what notification does when an observer is gone.
- First step
- Draw the pointers. The document owns its observers and each observer owns the document, so when the window drops its handle, both counts stay at 1 or more and Drop never runs. Confirm it with Rc::strong_count, or with a Drop implementation that logs.
- Follow-ups
-
- Which side should hold the Weak pointer, and why?
- What should notify do when upgrade() returns None?
- Would Arc and Mutex change anything?
From the lesson Language-specific traps when applying patterns
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A pricing module has the same switch on the customer's tier, standard, gold or partner, in five functions. Would you replace it with polymorphism, a lookup table, Strategy or State? Walk through how you decide.
- What it tests
- Telling the four options apart by what varies and how often, using the change history as evidence, and choosing the smallest change that removes the duplication.
- First step
- Ask what the switch decides and how it changes. If each tier only supplies numbers, a table is enough. If tiers carry behaviour, polymorphism or Strategy fit; State only if a customer's tier changes as a result of what the customer does.
- Follow-ups
-
- Sales wants to add a tier every month, from an admin screen.
- One tier needs a completely different discount calculation.
- How do you do the change without breaking the five callers?
From the lesson Code smells that point to a pattern
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
In a review, a colleague has replaced a four-line chain of if statements in a request handler with a Chain of Responsibility of four handler classes. The tests still pass. What do you say, and what would change your mind?
- What it tests
- Weighing a pattern's cost against the variation it serves, giving review feedback with evidence, and knowing the conditions under which the pattern pays.
- First step
- Measure the change first: lines, classes and how a reader follows one request. Then ask what variation the chain serves. If every route runs the same four checks in the same order, the if chain is the smallest correct code.
- Follow-ups
-
- Different routes need different checks.
- One check has to add the signed-in user to the request.
- The colleague says it is the standard design for middleware.
From the lesson Code smells that point to a pattern
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A code base checks for a missing discount plan in twelve places, with a slightly different default in some of them. Compare a default value at the boundary, a Null Object and an Option type, choose one, and say how you would make the change.
- What it tests
- Recognising inconsistent special cases as the real bug, choosing between data and behaviour for the missing case, and refactoring twelve call sites safely.
- First step
- First list the twelve checks and what each one does when the plan is missing: the differing defaults are a bug or a hidden rule, and the team must decide which before any refactoring. Then choose by whether a missing plan only changes values or also behaviour.
- Follow-ups
-
- Two of the twelve places really do need a different default.
- The language has an Option type in its standard library.
- How do you stop a thirteenth check from appearing?
From the lesson Code smells that point to a pattern
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
You have to change a 400-line pricing function that has no tests, reads the current date and a global settings object, and has a bug report open against it. Describe your first hour, step by step.
- What it tests
- Ordering the work safely: seams before tests, characterization before refactoring, and keeping the bug fix separate from the structural change.
- First step
- Do not touch the logic yet. First make the function callable from a test: add seams for the date and the settings, such as parameters with defaults, so existing callers do not change. Then record what it answers for many inputs, including the case from the bug report.
- Follow-ups
-
- The function prints to a log and sends an email when a price changes.
- How many recorded cases are enough?
- When do you fix the reported bug?
From the lesson Refactoring safely with characterization tests
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Your team's snapshot tests change on almost every pull request, and people approve the new snapshots without reading them. What has gone wrong, and how do you make the tests useful again?
- What it tests
- Knowing what makes snapshots unstable or too broad, restoring review of intended changes, and knowing when to replace a snapshot with focused tests.
- First step
- Look at what changes in a typical update. If it is timestamps, random ids or unordered output, the snapshot records noise; if it is whole pages of markup, it records far more than callers depend on. Either way, approving has become a reflex, so the test detects nothing.
- Follow-ups
-
- The snapshot holds a whole rendered page.
- How should a change to a snapshot be reviewed?
- When should a snapshot test be deleted?
From the lesson Refactoring safely with characterization tests
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to staff
A service has 95 % line coverage, but a mutation testing run kills only 60 % of the mutants. What does that tell you, what do you do next, and how would you use mutation testing in a team without slowing everyone down?
- What it tests
- Understanding what coverage and mutation scores measure, triaging surviving mutants, recognising equivalent mutants, and fitting a slow tool into a team's workflow.
- First step
- Coverage says which lines ran; the mutation score says how many deliberate changes the tests noticed. So most lines run without their results being checked closely. Start with the surviving mutants in the code that matters most and changes most.
- Follow-ups
-
- Some surviving mutants cannot be killed by any test.
- The full mutation run takes 40 minutes.
- Should the team set a minimum mutation score?
From the lesson Refactoring safely with characterization tests
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A test suite passes on developers' machines but fails in CI about one run in five, always in tests that send notifications through a Singleton. Explain the likely cause, and plan the refactoring away from the Singleton without one big change.
- What it tests
- Recognising shared state as the cause of order-dependent tests, and planning Inline Singleton as small steps that never break callers.
- First step
- Suspect test order: CI often runs tests in a different order, or in parallel. Run the failing tests alone and then after each of their neighbours; if one order fails, a test changes the Singleton's state and another depends on it.
- Follow-ups
-
- Fifty call sites use the Singleton. Where do you start?
- Can you just reset the Singleton between tests?
- How do you know when to delete instance()?
From the lesson Refactoring toward, and away from, patterns
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
You are moving three pricing flags, gift wrap, insurance and express delivery, into decorators. Insurance is a percentage of the total. What can go wrong with the order of the decorators, and how do you prove that the refactoring changed no price?
- What it tests
- Understanding that decorators compose in order, choosing a characterization strategy that covers every combination, and keeping the order in one place.
- First step
- Write down which extras depend on the total so far: insurance does, so whatever it wraps decides its base. Then pin the old function's answers for every combination of the three options, eight per basket, before moving anything.
- Follow-ups
-
- A fourth extra arrives, a discount that must apply before insurance.
- Where should the order of the decorators be decided?
- Is a Decorator still right with ten extras?
From the lesson Refactoring toward, and away from, patterns
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A Java 25 code base has a Visitor over six AST node types with nine visitors. A colleague proposes replacing it with sealed interfaces and switch expressions. What do you weigh, and how would you migrate safely if you agree?
- What it tests
- Weighing Visitor against exhaustive pattern matching, knowing what the compiler checks in each design, and planning an incremental migration.
- First step
- Ask whether the node set is closed: if only this code base adds node types, a sealed interface lets the compiler check every switch, which is what the visitor interface did. If plugins add nodes, Visitor, or another open design, stays.
- Follow-ups
-
- Some visitors carry state while they walk the tree.
- One node type is defined in another module.
- How do you migrate nine visitors without a big change?
From the lesson Refactoring toward, and away from, patterns
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
All Design Patterns (GoF and modern) interview questions · The Design Patterns (GoF and modern) track