Junior design-pattern interviews ask "name the Gang of Four patterns." Senior interviews ask why you wrapped EF Core in a repository that adds nothing, why that Singleton is really global mutable state you can't test, and which pattern you'd delete because the abstraction costs more than it saves.
Patterns are a shared vocabulary for recurring design problems — but a senior knows they're a means to uphold SOLID, not a checklist to apply everywhere. Over-patterning is as much a smell as no patterns at all.
This is 30 questions across creational, structural, behavioral, and architectural/enterprise patterns — plus when NOT to use them. Each has a short answer, a deep dive with C# code, and the senior signal the interviewer is listening for. Every example is from Mattrx — a real multi-tenant platform on .NET 9, Clean Architecture + CQRS (MediatR), where patterns show up as production code, not textbook diagrams.
Block A — foundations & creational patterns
1. What is a design pattern — and when should you NOT use one?
A named, reusable solution to a recurring design problem — a shared vocabulary, not a library. Don't reach for one when the simple code is already clear; a pattern adds indirection, and indirection you don't need is overengineering. Apply a pattern when it removes real duplication or coupling, not to show you know it.
2. How do design patterns relate to SOLID?
Patterns are concrete ways to satisfy SOLID. Strategy/Decorator serve Open-Closed; DI serves Dependency Inversion; most push toward Single Responsibility. SOLID is the why; patterns are the how. If you can't name the principle a pattern upholds, you're cargo-culting it.
3. Singleton — pattern or anti-pattern?
Both. The intent (one shared instance) is fine; the classic static Instance property is usually an anti-pattern — global mutable state, a hidden dependency, hard to test. Prefer a DI container with singleton lifetime: one instance, but injected and swappable.
// Anti-pattern: global state, hidden dependency, can't substitute in tests.
public sealed class Config { public static Config Instance { get; } = new(); }
// Better: same single instance, but injected via an interface -> testable, explicit.
services.AddSingleton<IConfig, Config>();
4. Factory Method vs Abstract Factory?
Factory Method creates one product, deferring which concrete type to runtime. Abstract Factory creates families of related products behind one interface. Factory Method = one thing; Abstract Factory = a related set.
public sealed class NotifierFactory(IServiceProvider sp)
{
public INotifier For(Channel channel) => channel switch
{
Channel.Email => sp.GetRequiredService<EmailNotifier>(),
Channel.Sms => sp.GetRequiredService<SmsNotifier>(),
Channel.Push => sp.GetRequiredService<PushNotifier>(),
_ => throw new ArgumentOutOfRangeException(nameof(channel)),
};
}
5. When does the Builder pattern beat a constructor?
When an object has many optional parameters, needs step-by-step assembly, or should be immutable at the end. Builder avoids telescoping constructors. In C#, object initializers and records cover simple cases — reserve Builder for construction with real logic or ordering.
6. What is the Prototype pattern, and its modern form?
Create new objects by cloning an existing one instead of constructing from scratch. Modern languages give you this directly: structuredClone in JS, record with-expressions and copy constructors in C#.
7. Is Dependency Injection a design pattern?
Yes — the concrete technique for Inversion of Control: dependencies are supplied from outside (constructor injection) rather than created inside. Watch the anti-pattern cousin: Service Locator (pulling dependencies from a container inside the class) hides dependencies instead of declaring them.
8. When is an Object Pool worth it?
When object creation/destruction is expensive and objects are reused rapidly — connections, large buffers, threads. Pooling amortizes allocation and reduces GC pressure. Don't pool cheap objects; the bookkeeping costs more than the allocation it saves.
Block B — structural patterns
9. What problem does the Adapter pattern solve?
It wraps an incompatible interface so two things not designed to work together can. Write an adapter that implements the interface your code expects and translates calls to the foreign API — great for isolating a third-party SDK behind your own abstraction.
10. Decorator — what does it give you over inheritance?
It adds behavior by wrapping, not subclassing — compose responsibilities at runtime instead of exploding a class hierarchy. Same interface in and out, so decorators stack (caching + logging + retry) in any order. Open-Closed in action.
// Wrap ANY IKpiRepository with caching -> no subclass, no edit to the original.
public sealed class CachingKpiRepository(IKpiRepository inner, ICache cache) : IKpiRepository
{
public Task<Kpis> GetAsync(Guid tenantId, CancellationToken ct)
=> cache.GetOrSetAsync("kpis:" + tenantId,
() => inner.GetAsync(tenantId, ct), TimeSpan.FromSeconds(60));
}
11. What is the Facade pattern?
A simplified interface over a complex subsystem — one entry point that hides the coordination of several classes. An application service that orchestrates several domain operations is essentially a facade.
12. What are the uses of the Proxy pattern?
A stand-in with the same interface as the real object, controlling access: virtual proxy (lazy-load), protection proxy (access checks), caching proxy, or remote proxy. EF Core's lazy-loading proxies are a real example. Proxy controls access; Decorator adds behavior.
13. When do you use the Composite pattern?
For tree structures where you want to treat a single object and a group uniformly — a leaf and a branch implement the same interface, so client code recurses without special-casing. File systems, UI trees, nested menus.
14. What does the Bridge pattern decouple?
It separates an abstraction from its implementation so the two vary independently — instead of a combinatorial class explosion, you have two dimensions bridged by composition. "Prefer composition over inheritance," formalized.
15. What is the Flyweight pattern?
Share immutable common state across many objects to save memory — store the shared (intrinsic) state once and pass the varying (extrinsic) state in. .NET string interning is a flyweight. Classic for thousands of glyphs/particles/tiles.
16. Decorator vs Proxy vs Adapter — how do you tell them apart?
All three wrap another object, but the intent differs: Adapter changes the interface (make incompatible fit), Decorator keeps the interface and adds behavior, Proxy keeps the interface and controls access. Same structure, three different reasons — classify by intent.
Block C — behavioral patterns
17. What problem does the Strategy pattern solve?
It selects an algorithm at runtime by encapsulating each variant behind a common interface — eliminating a growing switch on "what kind." Add a behavior by adding a class, not editing the chooser (Open-Closed).
public interface IPricingStrategy { decimal Price(Usage u); }
// FlatPricing, TieredPricing, VolumePricing each implement it; resolved by plan key.
// The billing service picks a strategy -> no switch on plan type in the domain logic.
18. Observer — where does it show up in real code?
It defines a one-to-many dependency: when one object changes, its subscribers are notified. It's the basis of C# events, domain events, and reactive streams (IObservable). Publishers don't know their subscribers — that's the decoupling.
19. What is the Command pattern good for?
It encapsulates a request as an object — turning "do X" into a Command with its data. That enables queuing, logging, retry, and undo/redo (store the inverse). MediatR requests and message-queue commands are this pattern.
20. Template Method vs Strategy?
Template Method defines an algorithm's skeleton in a base class and lets subclasses override specific steps (inheritance, compile-time). Strategy injects the whole algorithm as a collaborator (composition, runtime). Favor Strategy unless the skeleton is truly fixed.
21. When do you use the State pattern?
When an object's behavior depends on its state and you'd otherwise have sprawling switch (status) blocks in every method. Each state becomes a class that knows its allowed transitions — a state machine as objects. It makes illegal transitions unrepresentable.
22. What everyday framework is Chain of Responsibility?
ASP.NET Core middleware. Each handler gets the request, does its part, and either passes to the next link or short-circuits. Pipelines, filters, and MediatR behaviors are all Chain of Responsibility.
// Each middleware is a link: do work, then call next() -> or short-circuit and don't.
app.Use(async (ctx, next) => { /* authenticate */ await next(); });
app.Use(async (ctx, next) => { /* resolve tenant */ await next(); });
23. What does the Mediator pattern decouple, and where do you use it?
It centralizes communication so components talk through a mediator instead of referencing each other directly — reducing many-to-many coupling to many-to-one. MediatR is the canonical .NET example.
// Controller depends on IMediator, not on the handler -> decoupled request flow (CQRS).
public record PublishCampaign(Guid Id) : IRequest<Result>;
public sealed class PublishCampaignHandler : IRequestHandler<PublishCampaign, Result> { /* ... */ }
await _mediator.Send(new PublishCampaign(id), ct);
24. What pattern powers foreach / IEnumerable?
Iterator. It provides sequential access to a collection's elements without exposing its internal structure. In C#, IEnumerable/IEnumerator (and yield return) are the pattern built into the language — enabling lazy, structure-agnostic traversal.
25. What is the Visitor pattern, and its trade-off?
It lets you add new operations over an object structure without modifying the classes — move the operation into a visitor and the elements "accept" it. Trade-off: easy to add operations, hard to add new element types (you must touch every visitor). The inverse of normal polymorphism.
Block D — architectural & enterprise patterns (and anti-patterns)
26. Repository & Unit of Work — and when are they redundant?
Repository abstracts data access; Unit of Work tracks changes and commits atomically. The catch on modern ORMs: EF Core's DbSet<T> is already a repository and DbContext is already a Unit of Work — a generic IRepository<T> on top often just hides EF and adds no value. Use a repository for a real domain-shaped interface, not to wrap EF one-to-one.
27. What is CQRS and when is it worth it?
Command Query Responsibility Segregation — separate the model that writes from the model that reads, because their needs differ (normalized writes vs denormalized fast reads). Worth it for read-heavy, complex domains; overkill for simple CRUD.
public record GetDashboard(Guid TenantId) : IRequest<DashboardDto>; // query -> read model
public record ArchiveCampaign(Guid Id) : IRequest; // command -> write model
28. How do you replace null and exceptions with patterns?
Null Object (a do-nothing implementation instead of null) removes null checks; the Result/Option pattern returns success-or-failure as a value instead of throwing for expected failures. Both make control flow explicit and push errors into the type system.
29. What are the design anti-patterns you watch for?
God object (one class doing everything), Singleton abuse (global state), anemic domain model (data bags + logic in services), service locator (hidden dependencies), and over-patterning (layers of indirection nobody needs). The subtle one is over-abstraction — unused flexibility is debt.
30. How do you actually choose a pattern?
Start from the problem and the change you expect, not the catalog. Identify what varies, isolate it behind an interface, and pick the pattern whose intent matches — or write the simple code if nothing varies yet. Refactor to patterns when duplication/coupling appears; don't design from patterns speculatively.
The through-line: every pattern buys flexibility with indirection. Apply one only when it absorbs a change you actually expect, name the SOLID principle it serves, and delete the abstraction that doesn't earn its keep. Patterns serve SOLID; they don't replace judgment.
This post was originally published on PrepStack. The full original has all the code and senior-signal notes.
Top comments (0)