Direction of dependencies & The Stable Dependencies Principle (SDP)

Direction of dependencies & The Stable Dependencies Principle (SDP)

Keeping your codebase clean and easy to maintain is crucial in software development. One important principle that helps achieve this is the Stable Dependencies Principle (SDP). Let's dive into what this principle is and how it can benefit your projects.

What is the Stable Dependencies Principle (SDP)?

The Stable Dependencies Principle states that:

"Depend in the direction of stability."

In simple terms, more stable components (parts of your code that don't change often) should not depend on less stable components (parts of your code that change frequently).

Why is SDP Important?

  1. Increased Reliability: Stable components at the core of your system make it more reliable because they change less often, reducing the risk of bugs.
  2. Easier Maintenance: Components that don't change much provide a solid foundation. This makes it easier to update and fix other parts of the system without worrying about breaking stable components.
  3. Better Scalability: Systems designed with stable dependencies are easier to scale. You can build on a stable foundation without frequent breaking changes.

How to Measure Stability

To understand stability, look at two things:

  • Afferent Couplings (Ca): How many components depend on a given component (How many dependents)?
  • Efferent Couplings (Ce): How many components does the given component depend on (How many dependencies)?

A component is more stable if it has fewer dependencies (lower Ce) and more dependents (higher Ca).

Relationship Between SDP and Dependency Inversion Principle (DIP)

The Dependency Inversion Principle (DIP) and SDP often work together. DIP suggests that high-level modules should not depend on low-level modules, but both should depend on abstractions (interfaces). This helps in creating stable dependencies.

Applying SDP with DIP

Let's look at an example to see how SDP and DIP can work together.

Problematic Example

Here, a stable component (CoreSystem) depends on a less stable component (ExperimentalFeature):

// Less stable component
public class ExperimentalFeature
{
    public void Execute()
    {
        // Experimental code that changes often
    }
}

// More stable component
public class CoreSystem
{
    private ExperimentalFeature _feature = new ExperimentalFeature();

    public void Run()
    {
        _feature.Execute();
    }
}        

This setup violates SDP because the stable CoreSystem depends on the unstable ExperimentalFeature.

Improved Example with DIP

We can fix this by using an interface to invert the dependency:

// Abstraction
public interface IFeature
{
    void Execute();
}

// Less stable component implementing the abstraction
public class ExperimentalFeature : IFeature
{
    public void Execute()
    {
        // Experimental code that changes often
    }
}

// More stable component depending on the abstraction
public class CoreSystem
{
    private readonly IFeature _feature;

    public CoreSystem(IFeature feature)
    {
        _feature = feature;
    }

    public void Run()
    {
        _feature.Execute();
    }
}

// Usage
public class Program
{
    public static void Main()
    {
        IFeature feature = new ExperimentalFeature();
        CoreSystem coreSystem = new CoreSystem(feature);
        coreSystem.Run();
    }
}        

Benefits of This Approach

  1. Follows SDP: The stable CoreSystem now depends on the stable interface (IFeature), not directly on the less stable ExperimentalFeature.
  2. Follows DIP: The high-level CoreSystem depends on the abstraction (IFeature), not the low-level ExperimentalFeature.
  3. Flexible and Extensible: You can easily introduce new features by implementing the IFeature interface without changing the CoreSystem.

"Is SDP the same as DIP?"

While the Stable Dependencies Principle (SDP) and the Dependency Inversion Principle (DIP) are related and often complement each other, they are distinct principles with different focuses. SDP is a general and broader principle, and DIP is its specific form applicable where the level of modules is known; SDP can be more helpful when we don’t know which module is higher level, but we know which is more stable. This will mostly happen in code, which does not directly represent user behaviors. I will write more about this in the next article to keep this one sane and short, But to get a hint at how stability will help use defined dependency, look at three components In a configuration management system, we might have the following components:

  1. ConfigurationReader: Reads configurations from different sources (files, databases, environment variables).
  2. ConfigurationValidator: Validates the configurations to ensure they meet certain criteria.
  3. ConfigurationCache: Caches configurations for quick access. Let's clarify their differences and how they work together:

Can you try to determine which one is at a higher level and which one is at a lower level? It’s difficult, but it will become clear if we look at stability.

Lets clarify how both of these work together:

Stable Dependencies Principle (SDP)

Definition:

  • The Stable Dependencies Principle states that a component should depend on components that are more stable than itself.
  • Stability, in this context, means how likely a component is to change. A stable component is one that changes infrequently.

Focus:

  • SDP is about organizing dependencies in a way that more stable components are at the core of the system, ensuring that frequent changes in less stable components do not ripple through the entire system.

Dependency Inversion Principle (DIP)

Definition:

  • The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces).
  • Abstractions should not depend on details. Details should depend on abstractions.

Focus:

  • DIP focuses on decoupling high-level and low-level modules through the use of abstractions, which makes the system more flexible and easier to change.

How SDP and DIP Work Together

SDP and DIP often work together to create a robust and maintainable system:

  • SDP ensures that your dependencies point towards more stable components, reducing the risk of changes in less stable components affecting the entire system.
  • DIP ensures that both high-level and low-level modules depend on abstractions, not on concrete implementations, which decouples the modules and makes the system more flexible.

By following both principles, you can achieve a system where:

  • Stable components form the backbone of the system.
  • High-level modules are not tightly coupled to low-level modules, promoting flexibility and ease of maintenance.

Conclusion

The Stable Dependencies Principle (SDP) is a key guideline for making your codebase more reliable, maintainable, and scalable. By ensuring that stable components depend only on other stable components and by using interfaces to invert dependencies, you create a robust foundation for your software. Start applying SDP and DIP in your projects and see the benefits for yourself!

Ali Rafique Muhammad

Software Architect | Problem solver | Crafting Clean, Scalable Solutions | Advocate for Continuous Learning

9 个月

?? Read the substack article here: https://lnkd.in/dfCsSr9C

回复

要查看或添加评论,请登录

Ali Rafique Muhammad的更多文章

  • The Boy Who Cried Wolf: Addressing False Positives in Testing

    The Boy Who Cried Wolf: Addressing False Positives in Testing

    False positives in tests are like the boy who cried wolf. They create noise that reduces confidence in testing efforts,…

    1 条评论
  • Discovering the Null Object Pattern in Software Design

    Discovering the Null Object Pattern in Software Design

    What is the Null Object Pattern? Null Object Pattern: To design robust solutions, this design pattern involves creating…

    4 条评论
  • Decoupling Tests from Implementation Details

    Decoupling Tests from Implementation Details

    One of the key principles of effective testing is to decouple your tests from the implementation details of the system…

  • From Simple to Messy: Why Your Codebase Needs Regular Maintenance

    From Simple to Messy: Why Your Codebase Needs Regular Maintenance

    Ever wonder why your once simple codebase now looks like a tangled mess? The answer lies in continuous maintenance and…

    5 条评论
  • Single Level of Abstraction (SLA) Principle

    Single Level of Abstraction (SLA) Principle

    The Single Level of Abstraction principle asserts that a function or method should operate at a single level of…

    4 条评论
  • Understanding Multi-Tier Caching

    Understanding Multi-Tier Caching

    In high-performance applications, getting data quickly is crucial. Multi-tier caching is a method that speeds up data…

    5 条评论
  • LSP: A Guard Against Inheritance Bugs

    LSP: A Guard Against Inheritance Bugs

    The Liskov Substitution Principle aims to manage the cost of flexibility. While inheritance allows for extending…

  • Preventing Accidental modification of data

    Preventing Accidental modification of data

    Encapsulation prevents the accidental modification of data. In the software industry, many problems, such as bugs and…

  • Partial Classes and Methods (C#)

    Partial Classes and Methods (C#)

    It is possible to split the definition of a class or a struct, an interface or a method over two or more source files…

    2 条评论
  • Triggers in database

    Triggers in database

    its very useful to write triggers in databases.Its like automatically managing database when some even occurs.

社区洞察

其他会员也浏览了