<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shivam Kumar</title>
    <description>The latest articles on DEV Community by Shivam Kumar (@c_vam07).</description>
    <link>https://dev.to/c_vam07</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4173850%2F2ef7cf7e-403e-446d-b47f-94bca6c0c691.jpg</url>
      <title>DEV Community: Shivam Kumar</title>
      <link>https://dev.to/c_vam07</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/c_vam07"/>
    <language>en</language>
    <item>
      <title>Why SOLID Principles Changed the Way I Write Code</title>
      <dc:creator>Shivam Kumar</dc:creator>
      <pubDate>Sat, 24 Oct 2026 05:50:02 +0000</pubDate>
      <link>https://dev.to/c_vam07/why-solid-principles-changed-the-way-i-write-code-40d0</link>
      <guid>https://dev.to/c_vam07/why-solid-principles-changed-the-way-i-write-code-40d0</guid>
      <description>&lt;p&gt;Every developer remembers the excitement of writing their first application. Seeing a program finally run after hours of debugging is incredibly satisfying.&lt;/p&gt;

&lt;p&gt;But after building a few real projects, I noticed something interesting.&lt;/p&gt;

&lt;p&gt;Writing code that &lt;strong&gt;works today&lt;/strong&gt; is easy.&lt;/p&gt;

&lt;p&gt;Writing code that &lt;strong&gt;still works six months later after dozens of new features&lt;/strong&gt; is the real challenge.&lt;/p&gt;

&lt;p&gt;As projects grow, small design mistakes become expensive. A simple feature request suddenly requires changes in multiple files. Fixing one bug unexpectedly breaks another module. Testing becomes frustrating because everything depends on everything else.&lt;/p&gt;

&lt;p&gt;That's exactly why the &lt;strong&gt;SOLID Design Principles&lt;/strong&gt; exist.&lt;/p&gt;

&lt;p&gt;They aren't rules to make your code more complicated. They're guidelines that help you build software that's easier to maintain, extend, and scale.&lt;/p&gt;

&lt;p&gt;Here's what I learned about each principle.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Good Code Eventually Becomes Bad Code
&lt;/h1&gt;

&lt;p&gt;Most applications don't start out messy.&lt;/p&gt;

&lt;p&gt;Imagine you're building an online shopping application.&lt;/p&gt;

&lt;p&gt;At first, it only needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add products to a cart&lt;/li&gt;
&lt;li&gt;Calculate totals&lt;/li&gt;
&lt;li&gt;Generate invoices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation is straightforward.&lt;/p&gt;

&lt;p&gt;But after a few months, new requirements start arriving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support multiple payment gateways.&lt;/li&gt;
&lt;li&gt;Store data in different databases.&lt;/li&gt;
&lt;li&gt;Generate PDF invoices.&lt;/li&gt;
&lt;li&gt;Send email receipts.&lt;/li&gt;
&lt;li&gt;Add discount strategies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything was built inside a few large classes, every new feature means modifying existing code.&lt;/p&gt;

&lt;p&gt;Eventually, your project reaches a point where making one small change feels risky because you're never sure what else might break.&lt;/p&gt;

&lt;p&gt;The problem isn't bad programming.&lt;/p&gt;

&lt;p&gt;The problem is poor design.&lt;/p&gt;

&lt;p&gt;SOLID provides a way to avoid that situation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg3ke3zg8n62mfpzwjqj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg3ke3zg8n62mfpzwjqj5.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Single Responsibility Principle (SRP)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every class should have one responsibility and one reason to change.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One lesson I've learned is that classes often become "do everything" classes without us realizing it.&lt;/p&gt;

&lt;p&gt;Consider a &lt;code&gt;ShoppingCart&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;At first, it calculates the total amount.&lt;/p&gt;

&lt;p&gt;Later, it starts generating invoices.&lt;/p&gt;

&lt;p&gt;Then it stores invoices in the database.&lt;/p&gt;

&lt;p&gt;Soon it even sends confirmation emails.&lt;/p&gt;

&lt;p&gt;Now one class is responsible for shopping, billing, storage, and communication.&lt;/p&gt;

&lt;p&gt;If the invoice format changes, the shopping logic changes.&lt;/p&gt;

&lt;p&gt;If the database changes, the shopping logic changes again.&lt;/p&gt;

&lt;p&gt;That's a clear sign that the class has too many responsibilities.&lt;/p&gt;

&lt;p&gt;A better design is to split responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ShoppingCart&lt;/strong&gt; → manages cart operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;InvoicePrinter&lt;/strong&gt; → formats and prints invoices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;InvoiceRepository&lt;/strong&gt; → saves invoices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EmailService&lt;/strong&gt; → sends notifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each component becomes smaller, easier to test, and easier to maintain.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Open/Closed Principle (OCP)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Software should be open for extension but closed for modification.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the biggest sources of bugs is modifying code that already works.&lt;/p&gt;

&lt;p&gt;Imagine your application currently saves invoices in MySQL.&lt;/p&gt;

&lt;p&gt;Months later, your company decides to migrate to MongoDB.&lt;/p&gt;

&lt;p&gt;If you have to edit the existing storage class, you're risking bugs in functionality that was already stable.&lt;/p&gt;

&lt;p&gt;Instead, create an abstraction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;InvoiceStorage
      ↑
 ├── MySQLStorage
 ├── MongoStorage
 └── PostgreSQLStorage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the application depends only on the common interface.&lt;/p&gt;

&lt;p&gt;Adding a new database means creating another implementation rather than changing existing code.&lt;/p&gt;

&lt;p&gt;This approach makes software much safer to evolve.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Liskov Substitution Principle (LSP)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every subclass should behave like the class it replaces.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This principle is less about inheritance and more about trust.&lt;/p&gt;

&lt;p&gt;Suppose you have a base class called &lt;code&gt;Account&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every account supports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine introducing a &lt;code&gt;FixedDepositAccount&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem?&lt;/p&gt;

&lt;p&gt;Fixed deposit accounts don't allow withdrawals.&lt;/p&gt;

&lt;p&gt;If someone writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FixedDepositAccount&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the application either throws an exception or behaves unexpectedly.&lt;/p&gt;

&lt;p&gt;Even though the code compiles, the design is broken.&lt;/p&gt;

&lt;p&gt;A better solution is to model the real-world behavior correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Account
      ↑
WithdrawableAccount
      ↑
 ├── SavingsAccount
 └── CurrentAccount

NonWithdrawableAccount
      ↑
FixedDepositAccount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every object behaves exactly as the caller expects.&lt;/p&gt;

&lt;p&gt;Good inheritance should never surprise the developer using it.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Interface Segregation Principle (ISP)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't force classes to implement methods they don't need.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Large interfaces often create unnecessary work.&lt;/p&gt;

&lt;p&gt;Imagine an interface like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Machine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with three methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;print()&lt;/li&gt;
&lt;li&gt;scan()&lt;/li&gt;
&lt;li&gt;fax()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now think about a basic home printer.&lt;/p&gt;

&lt;p&gt;It only prints.&lt;/p&gt;

&lt;p&gt;Why should it implement scanning and faxing?&lt;/p&gt;

&lt;p&gt;Instead, split the interface into smaller ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Printable&lt;/li&gt;
&lt;li&gt;Scannable&lt;/li&gt;
&lt;li&gt;Faxable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each device implements only the capabilities it actually supports.&lt;/p&gt;

&lt;p&gt;Smaller interfaces make code cleaner and easier to understand.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Dependency Inversion Principle (DIP)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Depend on abstractions, not concrete implementations.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Business logic shouldn't care which database or external service is being used.&lt;/p&gt;

&lt;p&gt;Without dependency inversion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderService
      ↓
MySQLDatabase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service becomes tightly coupled to MySQL.&lt;/p&gt;

&lt;p&gt;Changing databases means modifying business logic.&lt;/p&gt;

&lt;p&gt;A better architecture introduces an abstraction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderService
      ↓
Database
      ↑
 ├── MySQLDatabase
 ├── MongoDatabase
 └── PostgreSQLDatabase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the service communicates with a common interface.&lt;/p&gt;

&lt;p&gt;The implementation can change without affecting the business logic.&lt;/p&gt;

&lt;p&gt;This design also makes unit testing much easier because mock implementations can replace real databases during testing.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why These Principles Matter Together
&lt;/h1&gt;

&lt;p&gt;Each SOLID principle solves a different design problem, but together they create software that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to understand&lt;/li&gt;
&lt;li&gt;Easier to test&lt;/li&gt;
&lt;li&gt;Easier to extend&lt;/li&gt;
&lt;li&gt;Easier to debug&lt;/li&gt;
&lt;li&gt;Easier to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of building tightly connected classes, you build independent components that work together through well-defined contracts.&lt;/p&gt;

&lt;p&gt;That's what makes large applications manageable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Quick Cheat Sheet
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Main Idea&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S — Single Responsibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One class should have one responsibility.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;O — Open/Closed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extend behavior without modifying existing code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L — Liskov Substitution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Child classes should be usable wherever parent classes are expected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I — Interface Segregation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep interfaces small and focused.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;D — Dependency Inversion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Depend on abstractions rather than concrete implementations.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;The biggest lesson I took away from learning SOLID is that software design isn't about predicting every future requirement—it's about making future changes easier.&lt;/p&gt;

&lt;p&gt;No project stays the same forever. Requirements evolve, technologies change, and new features keep arriving. Code that isn't designed for change quickly becomes difficult to maintain.&lt;/p&gt;

&lt;p&gt;SOLID won't magically make every project perfect, but it provides a practical foundation for writing software that can grow without becoming fragile.&lt;/p&gt;

&lt;p&gt;If you're learning object-oriented programming or preparing for system design interviews, mastering these principles is one of the best investments you can make. They're not just interview topics—they're habits that improve the quality of every project you build.&lt;/p&gt;

</description>
      <category>solidprinciples</category>
      <category>cleancode</category>
    </item>
  </channel>
</rss>
