SOLID PrinciplesDependency Inversion PrincipleHard⏱️ ~3 min

DIP in Interviews: Common Questions and Patterns

Interviewers assess DIP understanding through design problems that require identifying appropriate abstraction points and justifying architectural decisions. Here are frequent scenarios and how to approach them systematically.

Common Interview Question Patterns:
Pattern 1: "Design a notification service that supports email and SMS."
Trap: Creating EmailService and SMSService as siblings without abstraction.
Solution: Define INotificationChannel interface with send(recipient, message). NotificationService depends on this abstraction, not concrete channels. This enables adding push notifications later without modifying core service logic.

Pattern 2: "Your library system directly uses MySQL. How would you refactor to support PostgreSQL?"
Trap: Creating database-specific subclasses of business entities.
Solution: Extract IBookRepository interface defining domain operations like findAvailableBooks() and borrowBook(bookId, memberId). Business logic (LibraryService) depends only on this interface. Create MySQLBookRepository and PostgresBookRepository implementations.

Pattern 3: "Design a payment system for an e-commerce platform."
Trap: OrderService directly instantiating StripePaymentProcessor.
Solution: Define IPaymentProcessor with authorize(amount, paymentMethod) and capture(transactionId). OrderService receives processor via constructor injection. Support multiple gateways through strategy pattern or factory.
Machine Coding Considerations:
First, define interfaces before implementations. Start by writing IPaymentGateway based on what the high-level service needs, then implement concrete gateways. This ensures abstraction is driven by consumer needs.

Second, use dependency injection for wiring. High-level classes should receive dependencies through constructors, not create them internally. Show a simple injector or main method that assembles the object graph.

Third, keep interfaces focused. Avoid dumping all possible database operations into IRepository. Define narrow interfaces like IBookFinder and IBookUpdater if different parts of the system need different capabilities (Interface Segregation Principle).

Fourth, demonstrate testing. Create a MockPaymentGateway that succeeds or fails deterministically. Show how business logic can be tested without network calls or database setup.
Handling Follow-Up Questions:
"What if we need to call multiple payment gateways and use the first that succeeds?"
Introduce CompositePaymentGateway implementing IPaymentGateway. It holds a list of gateways and tries each in sequence. The high-level service still depends on the single abstraction, but the composite provides fallback logic.

"How do you handle gateway-specific configuration like API keys?"
Pass configuration objects to concrete implementations during construction. The interface should not expose configuration details. Use builder pattern or configuration classes injected alongside dependencies.

"Doesn't this make the codebase harder to navigate with all these interfaces?"
Acknowledge the trade-off. Explain that proper naming (IPaymentGateway vs PaymentGatewayInterface) and IDE navigation features mitigate this. The benefit is isolated changes and testability, which outweighs navigation cost in mature systems but may not be worth it in simple applications.

"When should you use abstract classes instead of interfaces?"
Use abstract classes when you have shared implementation among subclasses. If all payment gateways need common logging or retry logic, an abstract base class can provide that while still inverting dependencies. However, prefer interfaces when possible to allow multiple inheritance (in languages that support it for interfaces but not classes) and clearer contracts.
Interview Tip: Always explain your design decisions out loud. Say "I am defining IPaymentGateway first because the OrderService drives what operations are needed" rather than silently writing code. This demonstrates architectural thinking, not just coding ability.
Red Flags Interviewers Watch For:
First, interfaces that expose implementation details. If IDatabaseConnection has getJdbcConnection(), it leaks abstraction. Methods should use domain language.

Second, interfaces placed in the wrong module. If IUserRepository lives in the database package instead of the domain package, dependencies are not truly inverted.

Third, using new keyword in high-level modules. Instantiating new MySQLRepository() inside business logic defeats DIP. Use factories or injectors instead.

Fourth, over-engineering simple scenarios. Creating abstractions for trivial operations or unlikely-to-change dependencies shows lack of pragmatism.
💡 Key Takeaways
Identify appropriate abstraction points based on likelihood of change and testing needs
Define interfaces from the perspective of high-level consumers, not low-level implementations
Use dependency injection to wire concrete implementations at runtime
Demonstrate testing benefits by showing mock implementations
Handle follow-up questions by extending design through composition or decoration
Avoid leaking implementation details through interface method signatures
📌 Examples
1Notification service supporting email, SMS, and future channels through INotificationChannel
2Library system refactored from MySQL-specific code to database-agnostic with IBookRepository
3Payment system supporting multiple gateways through IPaymentProcessor with fallback logic
4Mock implementations for testing business logic without external service dependencies
← Back to Dependency Inversion Principle Overview