HOW TO IMPROVE SOFTWARE CODE &
STRUCTURE?
GANESH SAMARTHYAM (KONFHUB)
ganesh@konfhub.com
“HIGH INTERNAL QUALITY REDUCES THE
COST OF FUTURE FEATURES, MEANING THAT
PUTTING THE TIME INTO WRITING GOOD
CODE ACTUALLY REDUCES COST”
Martin Fowler
WHY CARE?
https://martinfowler.com/articles/is-quality-worth-cost.html
THREE STEP APPROACH
AWARENESS -> ACCEPTANCE -> ACTION
Smells
Tech Debt
Quantification
Refactoring /
Restructuring
IMPROVING CODE
CODE SMELLS -> REFACTORING
class PollutantEntry {
String country;
String state;
String city;
String place;
LocalDateTime localDateTime;
Float average;
Float max;
Float min;
String pollutant;
public PollutantEntry(String country, String state, String city,
String place, LocalDateTime localDateTime,
Float average, Float max, Float min, String pollutant) {
this.country = country;
this.state = state;
this.city = city;
this.place = place;
this.localDateTime = localDateTime;
this.average = average;
this.max = max;
this.min = min;
this.pollutant = pollutant;
}
}
OH! THIS IS SMELLY:
DATA CLUMPS &
LONG PARAMETER LIST
IMPROVING CODE
CODE SMELLS -> REFACTORING
public class PollutantEntry {
private Location location;
private LocalDateTime lastUpdate;
private PollutionData pollutionData;
/* The Builder class corresponds to the PollutantEntry - it helps create a Pollutant entry
object given the location object, time of reading, and the actual pollution reading
*/
public static class Builder {
PollutantEntry pollutantEntry = new PollutantEntry();
public Builder() {
}
public Builder location(Location location) {
pollutantEntry.location = location;
return this;
}
public Builder lastUpdate(LocalDateTime lastUpdate) {
pollutantEntry.lastUpdate = lastUpdate;
return this;
}
public Builder pollutionData(PollutionData pollutionData) {
pollutantEntry.pollutionData = pollutionData;
return this;
}
public PollutantEntry build() {
return pollutantEntry;
}
}
}
INTRODUCE ABSTRACTIONS &
USE BUILDER PATTERN!
IMPROVING CODE
CODE SMELLS -> REFACTORING
OH! THIS IS SMELLY :-(
THIS IS SHORT & SWEET! :-)
THREE STEP APPROACH
CODE SMELLS -> REFACTORING
OH! THIS IS SMELLY :-(
THIS IS SHORT & SWEET! :-)
EFFECTIVE APPROACH
CODE SMELLS -> REFACTORING
class Interpret {
private static Stack<Integer> executionStack = new Stack<>();
public static int interpret(byte[] byteCodes) {
int pc = 0;
while(pc < byteCodes.length) {
switch(byteCodes[pc++]) {
case ByteCode.ILOAD:
executionStack.push((int)byteCodes[pc++]); break;
case ByteCode.IMUL:
executionStack.push(executionStack.pop() * executionStack.pop()); break;
case ByteCode.IDIV:
{
int rval = executionStack.pop();
int lval = executionStack.pop();
executionStack.push(lval / rval); break;
}
case ByteCode.IADD:
executionStack.push(executionStack.pop() + executionStack.pop()); break;
case ByteCode.ISUB:
{
int rval = executionStack.pop();
int lval = executionStack.pop();
executionStack.push(lval - rval); break;
}
}
}
return executionStack.pop();
}
THIS IS A JVM LIKE
INTERPRETER CODE -
IT IS SMELLY, AND WHEN IT
EVOLVES, IT WILL STINK!
EFFECTIVE APPROACH
CODE SMELLS -> REFACTORING
THIS IMPROVED SOLUTION
USES COMMAND PATTERN
class Interpreter {
public int interpret(ByteCode[] byteCodes) {
Stack<Integer> evalStack = new Stack<Integer>();
for(ByteCode byteCode : byteCodes) {
byteCode.exec(evalStack);
}
Arrays.stream(byteCodes).forEach(byteCode -> byteCode.exec(evalStack));
return evalStack.pop();
}
}
abstract class ByteCode {
abstract void exec(Stack<Integer> execStack);
}
class ILOAD extends ByteCode {
byte val;
public ILOAD(byte arg) {
val = arg;
}
public void exec(Stack<Integer> execStack) {
execStack.push((int) val);
}
}
class IADD extends ByteCode {
public void exec(Stack<Integer> execStack) {
execStack.push(execStack.pop() + execStack.pop());
}
}
class IMUL extends ByteCode {
public void exec(Stack<Integer> execStack)
{
execStack.push(execStack.pop() * execStack.pop());
}
}
class ISUB extends ByteCode {
public void exec(Stack<Integer> execStack) {
int rval = execStack.pop();
int lval = execStack.pop();
execStack.push(lval - rval);
}
}
class IDIV extends ByteCode {
public void exec(Stack<Integer> execStack) {
int rval = execStack.pop();
int lval = execStack.pop();
execStack.push(lval / rval);
}
}
EFFECTIVE APPROACH
DESIGN & ARCH SMELLS -> RESTRUCTURING
Step 1: Separate the interface & implementation
Step 2: Depend on the interfaces (and not on the implementations)
(Reflection: Needs code-level changes)
EFFECTIVE APPROACH
DESIGN & ARCH SMELLS -> RESTRUCTURING
Step 1: Create a separate package for the interface(s)
Step 2: Separate the interface & implementation
(Reflection: Needs package-moves - no code level changes)
EFFECTIVE APPROACH
DESIGN & ARCH SMELLS -> RESTRUCTURING
Step: Separate the interfaces in “maven-classrealm” from the
implementation (concrete classes) into to a separate module
(“maven-classrealm-api”)
(Reflection: NO CODE CHANGE(S) NEEDED!!)
RECOMMENDED APPROACH
IF YOU ARE IN THIS SITUATION => DO THIS!
KEY BENEFITS
Quickening mental
models when working
with code
The time to understand the
structure of a large codebase
(especially for teams who
have been transferred
responsibility of an existing
product) is drastically
reduced
The cost of transfer of
maintenance is lesser as KT
time reduced as much as
75%
Modularization results
in reduced change
impact
The time to make a change
(new enhancement/bug fix)
is drastically reduced as the
time to do impact analysis is
drastically reduced
The bug density is lower as
developers can better
modularize and make
changes confidently
Improved build &
subsequent testing
time
Modularization results in
reduced time for compilation
& more modular components
as part of a modular build
system
Faster build times (even
upto 90%) resulting in
quicker turnaround time for
developers for changes +
quicker unit testing

Refactoring & Restructuring - Improving the Code and Structure of Software

  • 1.
    HOW TO IMPROVESOFTWARE CODE & STRUCTURE? GANESH SAMARTHYAM (KONFHUB) ganesh@konfhub.com
  • 2.
    “HIGH INTERNAL QUALITYREDUCES THE COST OF FUTURE FEATURES, MEANING THAT PUTTING THE TIME INTO WRITING GOOD CODE ACTUALLY REDUCES COST” Martin Fowler WHY CARE? https://martinfowler.com/articles/is-quality-worth-cost.html
  • 3.
    THREE STEP APPROACH AWARENESS-> ACCEPTANCE -> ACTION Smells Tech Debt Quantification Refactoring / Restructuring
  • 4.
    IMPROVING CODE CODE SMELLS-> REFACTORING class PollutantEntry { String country; String state; String city; String place; LocalDateTime localDateTime; Float average; Float max; Float min; String pollutant; public PollutantEntry(String country, String state, String city, String place, LocalDateTime localDateTime, Float average, Float max, Float min, String pollutant) { this.country = country; this.state = state; this.city = city; this.place = place; this.localDateTime = localDateTime; this.average = average; this.max = max; this.min = min; this.pollutant = pollutant; } } OH! THIS IS SMELLY: DATA CLUMPS & LONG PARAMETER LIST
  • 5.
    IMPROVING CODE CODE SMELLS-> REFACTORING public class PollutantEntry { private Location location; private LocalDateTime lastUpdate; private PollutionData pollutionData; /* The Builder class corresponds to the PollutantEntry - it helps create a Pollutant entry object given the location object, time of reading, and the actual pollution reading */ public static class Builder { PollutantEntry pollutantEntry = new PollutantEntry(); public Builder() { } public Builder location(Location location) { pollutantEntry.location = location; return this; } public Builder lastUpdate(LocalDateTime lastUpdate) { pollutantEntry.lastUpdate = lastUpdate; return this; } public Builder pollutionData(PollutionData pollutionData) { pollutantEntry.pollutionData = pollutionData; return this; } public PollutantEntry build() { return pollutantEntry; } } } INTRODUCE ABSTRACTIONS & USE BUILDER PATTERN!
  • 6.
    IMPROVING CODE CODE SMELLS-> REFACTORING OH! THIS IS SMELLY :-( THIS IS SHORT & SWEET! :-)
  • 7.
    THREE STEP APPROACH CODESMELLS -> REFACTORING OH! THIS IS SMELLY :-( THIS IS SHORT & SWEET! :-)
  • 8.
    EFFECTIVE APPROACH CODE SMELLS-> REFACTORING class Interpret { private static Stack<Integer> executionStack = new Stack<>(); public static int interpret(byte[] byteCodes) { int pc = 0; while(pc < byteCodes.length) { switch(byteCodes[pc++]) { case ByteCode.ILOAD: executionStack.push((int)byteCodes[pc++]); break; case ByteCode.IMUL: executionStack.push(executionStack.pop() * executionStack.pop()); break; case ByteCode.IDIV: { int rval = executionStack.pop(); int lval = executionStack.pop(); executionStack.push(lval / rval); break; } case ByteCode.IADD: executionStack.push(executionStack.pop() + executionStack.pop()); break; case ByteCode.ISUB: { int rval = executionStack.pop(); int lval = executionStack.pop(); executionStack.push(lval - rval); break; } } } return executionStack.pop(); } THIS IS A JVM LIKE INTERPRETER CODE - IT IS SMELLY, AND WHEN IT EVOLVES, IT WILL STINK!
  • 9.
    EFFECTIVE APPROACH CODE SMELLS-> REFACTORING THIS IMPROVED SOLUTION USES COMMAND PATTERN class Interpreter { public int interpret(ByteCode[] byteCodes) { Stack<Integer> evalStack = new Stack<Integer>(); for(ByteCode byteCode : byteCodes) { byteCode.exec(evalStack); } Arrays.stream(byteCodes).forEach(byteCode -> byteCode.exec(evalStack)); return evalStack.pop(); } } abstract class ByteCode { abstract void exec(Stack<Integer> execStack); } class ILOAD extends ByteCode { byte val; public ILOAD(byte arg) { val = arg; } public void exec(Stack<Integer> execStack) { execStack.push((int) val); } } class IADD extends ByteCode { public void exec(Stack<Integer> execStack) { execStack.push(execStack.pop() + execStack.pop()); } } class IMUL extends ByteCode { public void exec(Stack<Integer> execStack) { execStack.push(execStack.pop() * execStack.pop()); } } class ISUB extends ByteCode { public void exec(Stack<Integer> execStack) { int rval = execStack.pop(); int lval = execStack.pop(); execStack.push(lval - rval); } } class IDIV extends ByteCode { public void exec(Stack<Integer> execStack) { int rval = execStack.pop(); int lval = execStack.pop(); execStack.push(lval / rval); } }
  • 10.
    EFFECTIVE APPROACH DESIGN &ARCH SMELLS -> RESTRUCTURING Step 1: Separate the interface & implementation Step 2: Depend on the interfaces (and not on the implementations) (Reflection: Needs code-level changes)
  • 11.
    EFFECTIVE APPROACH DESIGN &ARCH SMELLS -> RESTRUCTURING Step 1: Create a separate package for the interface(s) Step 2: Separate the interface & implementation (Reflection: Needs package-moves - no code level changes)
  • 12.
    EFFECTIVE APPROACH DESIGN &ARCH SMELLS -> RESTRUCTURING Step: Separate the interfaces in “maven-classrealm” from the implementation (concrete classes) into to a separate module (“maven-classrealm-api”) (Reflection: NO CODE CHANGE(S) NEEDED!!)
  • 13.
    RECOMMENDED APPROACH IF YOUARE IN THIS SITUATION => DO THIS!
  • 14.
    KEY BENEFITS Quickening mental modelswhen working with code The time to understand the structure of a large codebase (especially for teams who have been transferred responsibility of an existing product) is drastically reduced The cost of transfer of maintenance is lesser as KT time reduced as much as 75% Modularization results in reduced change impact The time to make a change (new enhancement/bug fix) is drastically reduced as the time to do impact analysis is drastically reduced The bug density is lower as developers can better modularize and make changes confidently Improved build & subsequent testing time Modularization results in reduced time for compilation & more modular components as part of a modular build system Faster build times (even upto 90%) resulting in quicker turnaround time for developers for changes + quicker unit testing