Win a copy of Rust Web Development this week in the Other Languages forum!

T. Sharma

+ Follow
since Jul 30, 2013
Cows and Likes
Total received
In last 30 days
Total given
Total received
Received in last 30 days
Total given
Given in last 30 days
Forums and Threads
Scavenger Hunt
expand Ranch Hand Scavenger Hunt
expand Greenhorn Scavenger Hunt

Recent posts by T. Sharma

Stephan has already answered the question correctly. Still, here are my thoughts:

In the financial domain, if one has a large loan (say, a home loan), it is unlikely that the person could repay the whole loan in one instance; however, it is definitely easier for the person to repay the loan by paying EMIs (Equated Monthly Installments). In the same way, it is unlikely for a project team to repay huge technical debt all at once; however, repayment in the form of small installments is quite feasible and practical where a small portion of effort in each iteration is dedicated to refactoring.
The tools that detect and report design smells explicitly are:
Infusion - for C++ and Java code
Designite - for C# code

In fact, Designite uses the classification and catalog proposed in our book.

However, (some of the) design smells can be inferred by metrics also and there are tons of tools to generate metrics. This presentation on tools could be handy for you that provides an overview about various tools that could be used to identify and address technical debt.
To facilitate the discussion and help readers understand the scope of the book, I am putting down the relevant information about the book "Refactoring for Software Design Smells: Managing Technical Debt".

The sample text covering two design smells can be found here.

Information about the book:
The book is geared towards software developers and architects. The book presents 25 structural design smells, how they uncover mistakes made while designing, what design principles were overlooked or misapplied, and what principles need to be applied properly to address those smells through refactoring. Organized across common areas of software design, each smell is presented with diagrams and examples illustrating the poor design practices and the problems that result. The book describes how the overall quality of software can be improved significantly and technical debt can be reduced by finding and addressing smells in the design.

Companion website:
Amazon web page of the book:

Table of Contents
Foreword by Grady Booch
Foreword by Dr. Stéphane Ducasse

CHAPTER 1 Technical Debt
1.1 What is Technical Debt?
1.2 What Constitutes Technical Debt?
1.3 What is the Impact of Technical Debt?
1.4 What causes Technical Debt?
1.5 How to Manage Technical Debt?

CHAPTER 2 Design Smells
2.1 Why Care About Smells?
2.2 What Causes Smells?
2.3 How to Address Smells?
2.4 What Smells Are Covered in This Book?
2.5 A Classification of Design Smells

CHAPTER 3 Abstraction Smells
3.1 Missing Abstraction
3.2 Imperative Abstraction
3.3 Incomplete Abstraction
3.4 Multifaceted Abstraction
3.5 Unnecessary Abstraction
3.6 Unutilized Abstraction
3.7 Duplicate Abstraction

CHAPTER 4 Encapsulation Smells
4.1 Deficient Encapsulation
4.2 Leaky Encapsulation
4.3 Missing Encapsulation
4.4 Unexploited Encapsulation

CHAPTER 5 Modularization Smells
5.1 Broken Modularization
5.2 Insufficient Modularization
5.3 Cyclically-Dependent Modularization
5.4 Hub-like Modularization

CHAPTER 6 Hierarchy Smells
6.1 Missing Hierarchy
6.2 Unnecessary Hierarchy
6.3 Unfactored Hierarchy
6.4 Wide Hierarchy
6.5 Speculative Hierarchy
6.6 Deep Hierarchy
6.7 Rebellious Hierarchy
6.8 Broken Hierarchy
6.9 Multipath Hierarchy
6.10 Cyclic Hierarchy

CHAPTER 7 The Smell Ecosystem
7.1 The Role of Context
7.2 Interplay of Smells

CHAPTER 8 Repaying Technical Debt in Practice
8.1 The Tools
8.2 The Process
8.3 The People
Appendix A
Appendix B
Appendix C
Appendix D
An industrial software keeps changing due to changing/new requirements and bug fixes. A working software with not so good design will not be maintainable very soon in such a context. The Cost of Change will keep increasing for such a software if it is not refactored regularly.
Refactoring and related concepts are fundamental to software development. There might be variations when it comes to concrete details but in general they are applicable to all sort of software development regardless of the platform, technology, and programming language.
It's non-trivial to point out one out of 25 design smells (discussed in our book). However, I can provide a set of most commonly occurring smells that I have seen in my experience:
- Multifaceted Abstraction (when an abstraction has more than one responsibility assigned to it)
- Deficient Encapsulation (when the declared accessibility of one or more members of an abstraction is more permissive than actually required)
- Insufficient Modularization (when an abstraction exists that has not been completely decomposed, and a further decomposition could reduce its size, implementation complexity, or both) - commonly referred as God Class.
- Rebellious Hierarchy (when a subtype rejects the methods provided by its supertype(s))
As Junilu indicated, the refactoring should be carried out continuously. It is relevant to mention the Boy Scout rule; the rule states that "Always leave the campground cleaner than you found it." In the same way, when you stop working on a file/class, you must leave the code cleaner than you got it.
Two cents from my side as well (in addition to Ganesh's answer):

The focus of the book is on "design smells" - what are design smells, causes of the smells, implications and effects of the smells, and examples with corresponding refactoring solutions. The aim of the book is to help developers understand design smells so that they avoid introducing them in their product and if smells occurred in the project they could identify the smells and refactor them.

Of course, working with a legacy product is not easy. However, not touching the legacy product due to the fear of breaking code is not a sustainable and effective way. Identifying and removing design smells (while employing change impact analysis and dense test-net) can reduce the design debt of the product significantly and improve the maintainability of the product.
There are various dimensions of technical debt; some of the prominent dimensions are:
  • Implementation debt (Examples- code duplication, static tool rules violations, and code smells)
  • Design and architecture debt (Examples - Design smells, design rules violations, and architectural rules violations)
  • Test debt (Examples - Lack of tests, inadequate test coverage, and improper test design)
  • Documentation debt (Examples - No documentation for important concerns, poor documentation, outdated documentation)

  • There are various tools available to measure different dimensions of technical debt for different programming languages. You can find a detailed presentation about tools related to technical debt here.

    In this book, when we refer technical debt, we mean "design debt" arising mainly due to design smells.
    Dear Ranchers,

    This presentation catalogs a few tools that are useful for identifying technical debt issues (pertaining to different dimensions of technical debt) and addressing them.

    I hope, it will be useful for you.
    Hi all,

    We recently presented a tutorial in ISEC (Indian Software Engineering Conference) - 15 titled "How to apply design principles in practice". The slide set of the tutorial is now made available for all; you may access the same here. For more information, you may refer the article also with the same title.

    I hope, it is useful for you.

    I would like to go to the original question. Unfortunately, it is not rare to see such cases (where tests are completely missing). To counter this, Michael Feathers proposed the "legacy code change algorithm" in his book (Working Effectively with Legacy code):
  • Identify change points
  • Find test points
  • Break dependencies
  • Write tests
  • Make changes and refactor

  • One should try to follow this to overcome the situation. It is to be realized that the situation cannot be changed completely overnight.

    A quote from the same source (that I like a lot): "Over time, tested areas of the code base surface like islands rising out of the ocean."

    meenakshi sundar wrote:Does the book cover areas of Code smells and how to identify them ,Any best practices?

    For smells, you may take a look at this book: "Refactoring for Software Design Smells: Managing Technical Debt"

    For best practices, take a look at resources page here. There are many presentations, papers, and documents that may interest you.

    [Disclaimer: I am one of the co-authors of the above-cited book.]
    Knowledge of UML does not make you a better designer. There are many UML painters who draw nice looking UML diagram but their design sucks.

    UML provides you an abstraction, a view of your design to help you comprehend the system. You need to learn many other things to be a good designer.
    Nice discussion. Here are my two cents:

    When we think about reusing existing code, there are basically two ways: inheritance and composition. Inheritance is the natural choice when the participating abstractions share a "is-a" relationship. In all other cases, composition should be used to avoid problems such as stack and vector problem (as indicated by Campbell; we have also included it in our book under "Broken Hierarchy" smell).

    It is relevant here to talk about what makes a "good" inheritance hierarchy. Following enabling techniques must be employed for a smell-free hierarchy:
    Apply meaningful classification: Classify types to create a hierarchical organization using the following two steps:
    - Identify commonalities and variations among types. The commonalities are captured in a new abstraction which is referred as a supertype (this process is known as generalization). The variations are encapsulated in subtypes that inherit common behavior from supertypes.
    - Classify the supertypes and subtypes in levels to create a hierarchy. The resulting hierarchy should have general types towards the root and the specialized types towards the leaves.

    Apply meaningful generalization: As described in the previous enabling technique, generalization is the process wherein common behavior and elements between the subtypes are identified and factored out in supertypes.

    Ensure substitutability: Ensure that the types in the hierarchy follow the Liskov’s Substitution Principle (LSP); in other words, a reference of a supertype can be substituted with objects of its subtypes without altering the behavior of the program. Adherence to this principle allows object-oriented programs to leverage dynamic polymorphism.

    Avoid redundant paths: An inheritance hierarchy allows us to explicitly express the logical relationship between related types. When there are redundant paths in an inheritance hierarchy, it unnecessarily complicates the hierarchy.

    Ensure proper ordering: The key benefit of a hierarchical organization stems from its ability to provide us with a means to address the complex relationships among types. Hence, it is critical to express these relationships in an orderly fashion and maintain them consistently within the hierarchy.

    I hope it is useful.

    Reference: The above enabling techniques and corresponding text is taken from "Refactoring for Software Design Smells: Managing Technical Debt" book.