Skip to content

SOLID Principles Interview Questions and Answers

SOLID is one of the most common topics in interviews for C# and ASP.NET Core developers with 2 or more years of experience, and it is almost always asked for senior and lead roles. Interviewers rarely want only the definitions. They want to hear where you used each principle in a real project. The answers below give both. If you are new to the topic, first read the SOLID Principles series.

Basic Questions

1. What are SOLID principles?

SOLID is a set of five object-oriented design principles that make code easier to maintain, extend and test:

  • S: Single Responsibility Principle
  • O: Open/Closed Principle
  • L: Liskov Substitution Principle
  • I: Interface Segregation Principle
  • D: Dependency Inversion Principle

They were promoted by Robert C. Martin (Uncle Bob), and the acronym was coined by Michael Feathers.

2. Why should we follow SOLID principles?

To avoid code that is rigid (hard to change), fragile (breaks in unexpected places), immobile (hard to reuse) and hard to test. SOLID code gives loose coupling, high cohesion, easier unit testing, safer changes and parallel work for teams with fewer merge conflicts.

3. What is the Single Responsibility Principle?

A class should have only one reason to change. In practice, one class does one job for one "actor" (one group of stakeholders). Project example: "Our OrderService validated, calculated GST, saved to the database and sent emails. We split it into OrderValidator, PriceCalculator, OrderRepository and EmailNotifier, and OrderService only coordinates them."

4. What is the Open/Closed Principle?

Classes should be open for extension but closed for modification: new behaviour is added with new code, not by editing tested code. Project example: "Payment modes were a switch statement. We created IPaymentGateway with one class per gateway and injected IEnumerable<IPaymentGateway>. Adding a wallet was one new class and one line in Program.cs."

5. What is the Liskov Substitution Principle?

Objects of a derived class must be usable wherever the base class is expected, without breaking the program. A child must not throw unexpected exceptions, ignore calls or change the meaning of the parent's methods. Project example: "Cash on Delivery inherited a Refund() method it couldn't support and threw NotSupportedException. We moved refunds into an IRefundable interface implemented only by card and UPI payments."

6. What is the Interface Segregation Principle?

Clients should not be forced to depend on methods they don't use; prefer small, role-specific interfaces. Project example: "Our product repository had read, write, bulk import and report methods. The read-only cache had to throw NotImplementedException for most of them. We split it into IProductReader, IProductWriter and IProductReports."

7. What is the Dependency Inversion Principle?

High-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details. Project example: "Our services depend on IOrderRepository and IEmailSender, injected through the constructor and registered in Program.cs. That let us move from SMTP to SendGrid by changing one line, and unit test services with Moq."

Intermediate Questions

8. What is the difference between DIP, DI and IoC?

TermTypeMeaning
DIPPrincipleDepend on abstractions, not concrete classes.
IoCGeneral ideaControl of object creation and flow is given to a framework or container.
DITechniqueDependencies are passed in from outside (constructor, property, method injection).
DI containerToolCreates and injects objects automatically, e.g. IServiceCollection in ASP.NET Core.

DI is the most common way to achieve DIP, but they are not the same thing.

9. Is SRP the same as "a class should have only one method"?

No. A class can have many methods if they all change for the same reason. A PriceCalculator with methods for subtotal, discount and tax follows SRP, because all of them change when pricing rules change.

10. How do you identify an SRP violation in a code review?

  • The class description needs the word "and".
  • Names like Manager, Helper, Utility, CommonService.
  • A large constructor with many unrelated dependencies, or a class with hundreds of lines.
  • Mixed concerns: SQL, HTTP, email and business rules in one class.
  • Different features keep editing the same file.

11. Does OCP mean we can never modify an existing class?

No. Bug fixes and refactoring are always allowed. OCP means that adding a new variation of behaviour (a new payment type, export format, discount rule) should not require editing the existing, tested classes.

12. Which design patterns help to implement OCP?

Strategy (the most common), Decorator (add behaviour such as caching or logging around a class), Template Method, Chain of Responsibility (ASP.NET Core middleware is a chain), Observer, and Factory when combined with DI.

13. Why is the Rectangle-Square example an LSP violation?

If Square inherits from Rectangle and overrides the width/height setters to keep sides equal, code like r.Width = 5; r.Height = 4; expects area 20 but gets 16 for a square. The square changes behaviour the rectangle promised, so it can't be substituted.

14. What are the signs of an LSP violation?

  • Overridden methods throwing NotImplementedException or NotSupportedException.
  • Empty overrides that silently do nothing.
  • Type checks in calling code: if (obj is SpecialType).
  • Base-class tests failing for a derived class.
  • A child that requires stricter input or returns weaker results than the parent.

15. Can you give an example of an LSP violation in the .NET framework?

ReadOnlyCollection<T> implements ICollection<T> but throws NotSupportedException from Add. Similarly, arrays implement IList<T> but throw on Add. .NET later added IReadOnlyCollection<T> and IReadOnlyList<T> to model read-only collections properly.

16. What is the relationship between LSP and ISP?

They often appear together. A fat interface (ISP violation) forces classes to implement methods they can't support, so they throw exceptions, which makes them non-substitutable (LSP violation). Splitting the interface fixes both.

17. How do you register one class for multiple segregated interfaces in ASP.NET Core?

builder.Services.AddScoped<ProductRepository>();
builder.Services.AddScoped<IProductReader>(sp => sp.GetRequiredService<ProductRepository>());
builder.Services.AddScoped<IProductWriter>(sp => sp.GetRequiredService<ProductRepository>());

This way both interfaces share the same instance within a request. Registering AddScoped<IProductReader, ProductRepository>() and AddScoped<IProductWriter, ProductRepository>() separately would create two different instances.

18. Where should interfaces live in a layered (Clean Architecture) solution?

In the Application/Core layer, next to the business logic that uses them, not in the Infrastructure layer that implements them. Infrastructure references Application, never the other way around. This is the "abstractions should not depend on details" part of DIP.

19. How does ASP.NET Core itself follow SOLID?

  • SRP: middleware, filters, model binders and validators each do one job.
  • OCP: the request pipeline is extended by adding middleware; logging is extended by adding providers.
  • LSP: any ILogger, IDistributedCache (memory, Redis, SQL Server) can replace another.
  • ISP: small interfaces like ILogger<T>, IOptions<T>, IHostedService.
  • DIP: the built-in DI container and constructor injection everywhere.

Advanced and Scenario-Based Questions

20. Can following SOLID be harmful?

Yes, if applied blindly. Too many tiny classes and single-implementation interfaces make the code hard to navigate ("over-engineering"). Use the principles where change or testing is expected. Good practice: apply OCP when the second or third variation appears, and create interfaces for things that cross a boundary (database, network, files, third-party APIs) or that need mocking.

21. Do we need an interface for every class?

No. Entities, DTOs, view models, simple helpers and pure functions usually don't need interfaces. Create interfaces for services you want to swap, mock in tests, or that talk to external systems.

22. Your controller action is 300 lines long. How would you refactor it using SOLID?

  1. Identify the responsibilities: validation, business rules, data access, external calls, mapping.
  2. Move validation to FluentValidation or a validator class (SRP).
  3. Move business logic into a service class, data access into a repository (SRP).
  4. Put external calls behind interfaces such as IPaymentGateway and IEmailSender (DIP).
  5. Replace switch statements on types with strategies (OCP).
  6. Inject everything through constructors and keep the controller responsible only for HTTP.
  7. Add unit tests for the service before and after refactoring to be safe.

23. A new requirement says: send SMS as well as email when an order is placed. How do you design it?

Create an INotificationChannel interface with SendAsync(Order order), implement EmailChannel and SmsChannel, register both, and inject IEnumerable<INotificationChannel> into the order service, which loops over them. Adding WhatsApp later is a new class plus one registration (OCP), the order service doesn't know about any provider (DIP), and each channel class has one job (SRP).

public class OrderNotifier
{
    private readonly IEnumerable<INotificationChannel> _channels;
    public OrderNotifier(IEnumerable<INotificationChannel> channels) => _channels = channels;

    public Task NotifyAsync(Order order) =>
        Task.WhenAll(_channels.Select(c => c.SendAsync(order)));
}

24. What is wrong with this code, according to SOLID?

public class ReportService
{
    public byte[] Export(List<Sale> sales, string format)
    {
        if (format == "PDF") return BuildPdf(sales);       // uses a PDF library
        if (format == "Excel") return BuildExcel(sales);   // uses an Excel library
        if (format == "CSV") return BuildCsv(sales);
        throw new ArgumentException("Unknown format");
    }
}

It violates OCP: every new format requires editing this class. It also mixes several responsibilities (SRP): PDF, Excel and CSV generation change for different reasons and need different libraries. Fix: an IReportExporter interface with a Format property and one class per format (PdfExporter, ExcelExporter, CsvExporter), resolved through DI or keyed services.

25. What is wrong with this code?

public class InvoiceService
{
    public void Generate(int orderId)
    {
        var repo = new SqlOrderRepository();
        var order = repo.GetById(orderId);
        var pdf = new PdfGenerator().Create(order);
        new SmtpEmailSender().Send(order.Email, pdf);
    }
}

It violates DIP: the business class creates concrete low-level classes with new, so it can't be unit tested and is tied to SQL Server, a specific PDF library and SMTP. Fix: inject IOrderRepository, IPdfGenerator and IEmailSender through the constructor and register the implementations in Program.cs.

26. What is the Service Locator anti-pattern and how is it related to DIP?

Service Locator means classes ask a global container for their dependencies, for example serviceProvider.GetService<IEmailSender>() inside methods. The class still depends on abstractions, but its dependencies are hidden; you can't see them in the constructor, and tests must configure a container. Prefer constructor injection, which makes dependencies explicit.

27. How do SOLID principles help in unit testing?

SRP gives small classes with focused behaviour that are easy to test. DIP and constructor injection let you replace databases and external services with mocks (Moq, NSubstitute). ISP keeps the mocks small. LSP ensures tests written against an interface are valid for every implementation.

28. Which SOLID principle do you think is the most important, and why?

There is no single right answer; interviewers want your reasoning. A common, well-argued answer: SRP and DIP. SRP keeps classes small and understandable, and DIP makes code testable and replaceable. Most OCP improvements become easy once those two are in place, because you can add new implementations of an existing abstraction.

Quick Revision Table

PrincipleOne-line meaningSmellTypical fix
SRPOne reason to changeGod class, "Manager", "Helper"Split into focused classes
OCPExtend without modifyingswitch on a type or modeInterface + Strategy + DI
LSPChild works wherever parent worksNotSupportedException in overrides, type checksBetter hierarchy, capability interfaces
ISPNo unused methods forced on clientsNotImplementedException, fat interfacesRole interfaces
DIPDepend on abstractionsnew of services inside business codeInterfaces + constructor injection