Software Design Smells



Abstract

In this article I’d like to write about Software Design Smells.

In the SOLID – The Principles of Object Oriented Design article I posted, I wrote extensively about how we could prevent these design smells, by designing our solution with respect to the SOLID principles.

It’s important to understand the meaning of software design smells, and the negative affect on our application, to emphasize the importance of the SOLID principles.




Introduction

Software Design Smells are symptoms of bad design and violation of software patterns and object-oriented design principles that our codebase would experience, if we’ll not take measures to prevent them.


There are 7 well known 'Design Smells' that could have negative effects on our solution codebase:

            1.      Rigidity - The software is difficult to changes. (every change impact depended modules)
            2.      Fragility - The software breaks in many places when a single change is made.
            3.      Immobility - Components can't be reused.
            4.      Viscosity
  1.      Software Viscosity - Preserving the existing complicated design, or Hacking it.
  2.      Environment Viscosity - The development environment is very slow and inefficient.
            5.      Needless Complexity - Over Engineering & Over Design.
            6.      Needless Repetition - Duplicate code that could be avoided by abstraction.
            7.      Opacity - Writing modules that are not clear and hard to understand.




Rigidity

The software is difficult to changes

Every new requirement, even a small single change in a particular module, could cause additional (sometimes complex) changes in other dependent modules.

This indicates on a rigid design and naturally effects on the implementation-time.

In addition, since we couldn't fully anticipate this change implication, regressions issues might occur.




Fragility

The software breaks in many places when a single change is made

When modifying a single area in our code, other areas in the code, with no 'visible (logic) relation' to this change, were harmed or broken.

So, in such cases, fixing one issue could cause more problems and even regression issues.

The design is easy to break.




Immobility

Components can't be reused

When trying to reuse a common component (module) in several areas in the code (or in other systems), it's too tightly coupled to the original code (system), and almost not possible, since it includes lots of unnecessary effort and meaningful risks.

This inevitably creates 'code duplication' in our business logic (BL), which naturally effects on the maintainability of the code and especially on new BL requirement.

The design is hard to reuse.




Viscosity:

There are 2 levels of viscosity:


1.     Software Viscosity – Modifying the code while preserving the existing complicated design, or by 'Hacks', meaning by introducing new 'design' which is NOT compliant to the existing design.

Many changes in the code could be made in more than one way, if the easiest way is without preserving the existing design, then this solution is considered high viscosity software, since developers sometimes tend to do the 'wrong thing' and thus breaking the design and causing other issues, such as 'Spaghetti Code'.


2.     Environment Viscosity – The development environment is very slow and inefficient.

Developers spend many hours in the development environment.

If an environment is slow, meaning:
§  Compile-time is very long
§  Unit tests projects runs inefficiently
§  Source control check-ins are long and complex
§  Long deployment\build processes
§  Other bad designs that affect the environment, e.g.: performance testing tools.

Then sometimes developers would search 'Hacks' and the easiest ways to complete their tasks. E.g. they won't perform full 'feature tests', in order to prevent spending many hours in a not 'friendly' testing environment.

Another e.g.: developers would write code in wrong modules in order to prevent long compile-time.




Needless Complexity

Over Engineering & Over Design

With the aim of maintaining good designs and implementing robust solutions (and to avoid software design smells), developers sometimes exaggerate with their designs while trying to be as generic as possible and to capture every extreme case that could appear in the future.

This is somewhat unfulfilling these concepts, since we always need to use common sense and good design judgment on which extreme cases to include and others to exclude.

Such extreme designs create unreachable code (unused elements) that causes the entire solution size to grow, and add complexity in the code maintenance.

Sometimes, we should be reminded that good robust designs should be flexible, lightweight, and easy to understand in order to provide trivial new features implementation.




Needless Repetition

Duplicate code that could be avoided by abstraction

Duplicate (mostly) business logic code appears in the system, that could be united using abstraction.

This could be the result of copy-paste, and probably lack of awareness to the many problems such poor design could cause.

This, of course, effects on the maintainability of our systems, bug fixing in many different areas in the code, and naturally features growth.




Opacity

Writing modules that are not clear and hard to understand

Developers could write 'self-explanatory' code that is easy to understand and explore, and as opposed, developers could write code that is very hard to understand and sometimes even doesn’t make sense.

Difficult to understand code requires a significant amount of time trying to implement new features, and could potentially cause undesired behavior and severe bugs.

Bad initial design could cause such effects over time with features changes and features growth.





Summary

As experienced developers, we know that in large scale systems (containing millions of lines of code), and as the application evolves with numerous new requirements (over the years), sometimes we are bound to get software design smells.

However, we could always take measures to minimize (and finally prevent) the design smells in our codebase, especially with respect to new requirements.

Few measures to take in order to prevent design smells:

1.      First and foremost, maintain the ‘set of mind’ to always remember the negative effect of software design smells, on our application, development-time, bugs, performance and on the entire product life-cycle.
2.      Educate the developers to practice the SOLID principles when designing new solutions.
3.      Design & Develop according to Design Patterns.
4.      Maintain other software engineering 'Key Design Principle', such as:
-        CQRS - Command & Query Responsibility Segregation pattern
-        DRY -  Don't repeat yourself
-        LoD - The Law of Demeter (a.k.a The Principle of Least Knowledge)
-        Rule of Three
-        The Null Object Pattern
-        The Monad Pattern - Maybe Monad
-        AOP - Aspect-Oriented Programming
-        Strangler Pattern - Recreate legacy system 
-        Composite Reuse Principle - Favor Composition over inheritance
-        etc.
5.      As part of development standard, always perform code reviews.
6.      Perform code inspections to core designs and important features, receive feedbacks.
7.      Encourage the developers to write proper code comments when relevant.
8.      Encourage the developers to log informative messages in critical code sections, regardless to the regular BL logging.
9.      Work according to code guidelines and common code styling. (use static code analysis tools)
10.  Create unit-testing projects with sufficient code coverage. (run automatically with every check-in, use report system for notifications)
11.  Create performance testing projects to maintain proper KPIs. (bad designs = low performance)


Remember… well-designed clear code provides better Maintainability, Scalability, Testability... and finally influence on the entire Product Life-Cycle.



Related articles:
           
Intro:

S.O.L.I.D





The End

Hope you enjoyed!
Appreciate your comments…

Yonatan Fedaeli


No comments: