Code smells and Anti-patterns

A code smell is a surface indication that usually corresponds to a deeper problem in system - Martin Fowler

  • Long Method
  • Large Class
  • Data Clumps
  • Long Parameter List
  • Primitive Obsession

Bloaters

  • Switch statements
  • Refused bequest
  • Alternative classes w/ Different interfaces
  • Temporary field

Tool Abusers

  • Divergent change
  • Shotgun surgery
  • Parallel inheritance hierarchies

Change Preventers

  • Lazy class
  • Speculative Generality
  • Data class
  • Duplicated code

Dispensables

  • Feature envy
  • Inappropriate intimacy
  • Message chains
  • Middle man

Couplers

Refactoring

Changing the structure of the code without changing its behaviour

Why Refactoring?

Refactoring is not

  • Changing a language or a framework
  • Changing supporting services like from Active Directory to Azure AD
  • Add logging to the code

Refactoring is

  • Reducing code duplication
  • Combining methods into Class
  • Reducing long methods
  • Reducing long list of parameters

Why Refactoring?

  • Incremental delivery
  • Incremental feature development
  • Constant change in the code-base

Wherever there is a constant change in the code-base, refactoring is inevitable

Code smell

Code without smells

Refactoring

Common Code Smells

Long Methods

function calculateWeeklyStats() {
  let startDate = new Date();
  let endDate = new Date();
  let numberOfDaysToAdd = 6;
  endDate.setDate(startDate.getDate() + numberOfDaysToAdd);
  
  // convert dates to UTC Timestamp (in milliseconds)
  let startDateFormatted = startDate.valueOf();
  let endDateFormatted = endDate.valueOf();

  return statsRepository.where(startDateFormatted, endDateFormatted);
}

Long Methods

function calculateWeeklyStats() {
  let startDate = new Date();
  let endDate = new Date();
  endDate.setDate(startDate.getDate() + 6);
  return calculateStats(startDate, endDate);
}

function calculateMonthlyStats() {
  let startDate = new Date();
  let endDate = new Date();
  endDate.setDate(startDate.getDate + 30);
  return calcualteStats(startDate, endDate);
}

function calculateStats(startDate, endDate) {
  return statsRepository.where(startDate.valueOf(), endDate.valueOf);
}

Long Class

function calculateWeeklyStats() {
  let startDate = new Date();
  let endDate = new Date();
  endDate.setDate(startDate.getDate() + 6);
  return calculateStats(startDate, endDate);
}

function calculateMonthlyStats() {
  let startDate = new Date();
  let endDate = new Date();
  endDate.setDate(startDate.getDate + 30);
  return calcualteStats(startDate, endDate);
}

function calculateYearlyStats() {
  let startDate = new Date();
  let endDate = new Date();
  endDate.setDate(startDate.getDate + 365);
  return calcualteStats(startDate, endDate);
}

Long Class

function calculateWeeklyStats() {
  let startDate = new Date();
  let endDate = startDate.addDays(6);
  return calculateStats(startDate, endDate);
}

function calculateMonthlyStats() {
  let startDate = new Date();
  let endDate = startDate.addDays(30);
  return calcualteStats(startDate, endDate);
}

function calculateYearlyStats() {
  let startDate = new Date();
  let endDate = startDate.addDays(365);
  return calcualteStats(startDate, endDate);
}

// add to Date Prototype

Date.prototype.addDays(numberOfDays){
  let clonedDate = new Date(this.getTime());
  clonedDate.setDate(this.getDate() + numberOfDays);
  return clonedDate;
}

Primitive Obsession

public class Bank {
    private double currency;

    public double convertToUSD(){
        return this.currency * 3200;
    }
    
    public double convertToGBP(){
    	return this.currency * 6400;
    }
}

Primitive Obsession

public class Currency {
	private double value;
    
    public Currency(double value) {
    	this.value = value;
    }
    
    public convertToUSD() {
    	return this.value * 3200;
    }
}


public class Bank {
    private Currency currency;
    
    ...
}

Alternative Classes with Different Interfaces

class AuthenticationService {
  
  function isLoggedIn() {
    return localStorage.getItem('loggedIn');
  }

}


class SessionService {
  function shouldRedirectToLogin() {
    return localStorage.getItem('loggedIn');
  }
}

Temporary Field

class AuthenticationService {
  
  constructor() {
    this.allowRedirect = false;
  }
  
  function isLoggedIn() {
    return localStorage.getItem('loggedIn');
  }
  
  function verifyToken() {
    ...
  }
    
  function isAdminUser() {
    if(isLoggedIn) {
      return this.allowRedirect;
    } else {
      return true;
    }
  }
}

Duplicate Code

function add(data) {
  salesRepository.add(data);
  let date = new Date();
  console.log(`Operation: Add $(date)`);
}

function edit(data) {
  salesRepository.edit(data);
  let date = new Date();
  console.log(`Operation: Edit $(date)`);
}

function delete(data) {
  salesRepository.delete(data);
  let date = new Date();
  console.log(`Operation: Delete $(date)`);
}

Duplicate Code

function log(op) {
  console.log(`Operation: $(op) $(new Date())`)
}


function add(data) {
  salesRepository.add(data);
  log('add');
}

function edit(data) {
  salesRepository.edit(data);
  log('edit');
}

function delete(data) {
  salesRepository.delete(data);
  log('delete');
}

Speculative Generality

Antipatterns

Spaghetti Code

One liners

// One liners

5 > b ? 3<b ? console.log(4) : console.log(2) : console.log(6);

// Multi-liner but readable
if ( 5 > b) {
  if ( 3 < b) {
    console.log(4)
  } else {
	console.log(2)
  }
} else {
  console.log(6)
}

Premature Optimization

Globals

Happy Path Driven Development

Using "latest"

Code Styling

Impure Functions

Throwing without a cause

Logging without a cause

Use tools such as ESLint, StyleCop, SonarQube

"Refactoring - Improving the Design of existing code" by Martin Fowler 

Gracias

Code smells and Anti-patterns

By Mohammad Umair Khan

Code smells and Anti-patterns

  • 412