OLD CODE DOESN'T STINK
Refactor or Rewrite
Martin Gutenbrunner
LINZ, AUSTRIA - APR 26, 2018
DEVELOPER AND OPS CONFERENCE
ABOUT ME
• Started with Commodore 8-bit (VC-20 and C-64)
• assembled an i386 from second-hand parts as a gaming rig
• Built Null-Modem connections for playing Doom and WarCraft I
• and IPX/SPX networks between MS-DOS 6.22 and WfW 3.11
• Did DevOps before it was a thing
• mainly Java and JavaScript
• saw rotten code and built code that suddely rotted
• Now at Dynatrace
• previous: Tech Lead for Microsoft Azure and Microservices
• now: Software Architect
• About 14 years of experience in the industry
• Find me on Twitter: @MartinGoodwell
Considers himself a lucky guy
ABOUT THE SHOW
• Driven by example
• Example #1: Legacy can mean 15 years
• Example #2: Legacy can mean 5 years
• The basics – aka "Mastering the craft"
• The Magic Sauce, finally (spoiler: there is none)
Online Shop, written in ASP, rendering XHTML
Back then, in development for 15 years
EXAMPLE 1: classic ASP
PROJECT SETUP
• Use-case: eCommerce
• integrated with business backend system
• Native Windows desktop application, connected to database
• Core: classic ASP
• Database: Pervasive SQL
• the actual database from the business backend
• Interfaces to
• MSMQ for sending orders to the business backend
• COM+ for querying prices from the business backend
OLD ARCHITECTURE
ASP UI
XHTML
Business
Backend
System
SQL-DB
MSMQ
(Orders)
COM+
(Prices)
Browser
(XHTML)
WE WANTED TO REWRITE. WHY?
• 15 year old VB-Script codebase
• lack of structure
• not up to today's standards (eg Unit Testing)
• hard to find VB-Script talent
• Too closely bound to the business backend system
• Major updates locked the database and rendered the online shop unusable
CUSTOM BRANCHING
<%
if nCategoryId = 0 and (hostInfo.Path = "clayshop" or hostInfo.Path="bikershop" or hostInfo.Path="steelshop") then
out GetPageHTML(1,"de")
end if
%>
• Individual code branches for most tenants (~30 of them)
• If a tenant canceled his contract, the codebase usually was not
cleaned
RENDERING WHILE ITERATING RECORDSET
<%
set rsProd= oProduct.GetProduct(CLng(nProductId), CLng(nTenantId))
if not rsProd.eof then %>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr><td><img height="20" width="3" src="../../layout/pic/pix_tr.gif" border="0"></td></tr>
<tr><td class="productheadcolor">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="productheadiconcolor"><img height="20" width="20" src="../../layout/pic/icons/icon_kl_produkttip.gif" border="0"></td>
<td><div class="productheadcolor">&nbsp;<%= getText("product") %>:</div></td>
</tr>
</table>
<% if (sTenantPath <> "that_special_shop") then DrawProducts rsProd, "productreplacement" end if %>
<% end if
rsProd.close
set rsProd= nothing
%>
• No separation between data-access, business logic, and UI
• Made it hard to see what really needs refactoring
OUR PLAN WAS
• 100% re-write in Java
• Create a separate database for eCommerce
• Move all tenants to the new codebase within six months
PLANNED ARCHITECTURE
Business
Backend
SystemSQL
DB
MSMQ
(Orders)
COM+
(Prices)
Importer
Java
Spring MVC
MongoD
B
Browser
(HTML5)
LEARNINGS
• 100% re-write in Java not possible
• no stable Java libraries for MSMQ and COM+ at the time
• Planned timeframe (of course) didn't work
• first tenant went online after 9 months
• but it was using the new UI
• Re-doing the UI turned out to be the hard part
• we still didn't have all tenants converted after 2,5 years
DONE ARCHITECTURE
ASP
Bridge
Business
Backend
SystemSQL
DB
MSMQ
(Orders)
COM+
(Prices)
Importer
Java
MongoD
B
Browser
(HTML5)
ASP
Backend
Browser
(XHTML)
WHAT WENT WELL?
• Introduction of dedicated DB
• Having a separate Importer component
• We built deployment automation with Jenkins
• The ASP-bridge turned out to work really well
WHAT WE SHOULD HAVE DONE
• Identify the UI-part as the real problem
• impossible to see due to no layered code
• Embrace the fact that we have a huge number of customers on the
"old" codebase and design the new system for multiple UI
technologies
• EOL the old codebase
• If customers want new features, migrate them over
INTRODUCING BFF
• BFF
• Backend-for-Frontend
• aka Edge-Service
• source: Sam Newman's Microservice book
• can be used for
• routing
• authentication
• filtering
BETTER ARCHITECTURE
Java
Backend
API-only
Browser
(HTML5)
Browser
(XHTML)
BFF
ASP
BFF
Spring MVC
Android
(JSON)
BFF
Node.js
iOS
(JSON)
MongoDB
ASP-bridge
BENEFITS OF DOING IT RIGHT
• Save months of efforts to port the messy UI code
• Have ASP UI benefit from separate database
• Only have a single touchpoint with the business backend system
• for any client
• XHTML rendered by ASP
• HTML5 rendered by Spring MVC
• potential mobile apps
BUSINESS BACKEND
FIVE YEARS IN THE MAKING
OLD CODE DOESN'T STINK? IT MIGHT SMELL A LOT, THOUGH.
EXAMPLE 2: Java GWT
SETTING THE STAGE
• Problems
• Increasing number of bugs (~ 300k lines of code)
• Steadily decreasing velocity of teams (due to # of bugs)
• DB started to make problems. Regular restarts required (once a quarter)
• Mistakes made
• Bad code structure
• Next to no test coverage
• Solution
• Transition to a manageable scope
• Boyscout rule: leave the campground cleaner than you found it
WHY NOT REWRITE?
• Existing solution in the making for 5 years
• lots of domain knowledge would have to be re-implemented
• very error-prone
• Who would rewrite it?
• Same teams: who would maintain the current version?
• New teams: they would have to learn domain knowledge from scratch
• and new teams don't appear out of nowhere
• Timeline for starting over?
• can you re-implement a five-year-old system in 6/9/12(?) months?
WHY NOT REWRITE?
• We would have built the same thing again
• Even though it seems that a system is broken as a whole, it probably
isn't. Lots of it usually works.
• Our approach: educate the teams
• what went wrong
• how to do better
• And apply their learnings to the existing system instead of creating a
new one
• Plus: bring in new people to the teams
IT MIGHT SMELL, THOUGH.
EDUCATING THE TEAMS
MASTERING THE CRAFT
OLD CODE DOESN'T STINK
HOW CAN THAT EVEN HAPPEN?
• Bounded contexts
• Things built in-house that would have been readily available
• Code that's not testable
• Code that's not clearly/properly structured
• Bugs due to premature optimization
BOUNDED CONTEXTS
Don't mix things that don't belong together
• Do you see what's wrong with the "UserAccount" table?
BOUNDED CONTEXTS
Wrong: sharing DTOs between different domains
Some services use same attributes, some use specific ones
CatalogService
ShoppingCart
Service
OrderService
ProductDto
* id
* name
* description
* userRatings
* imageUrls
* price
* vat
* quantity
BOUNDED CONTEXTS
Right: dedicated DTOs for each domain
but smells like duplication a lot, because of the names
better: each DTO only contains the attributes it needs
CatalogService
ShoppingCart
Service
OrderService
CatalogProductDto
* id
* name
* description
* userRatings
* imageUrls
* price
CartProductDto
* id
* name
* price
* quantity
OrderProductDto
* id
* name
* price
* vat
* quantity
BOUNDED CONTEXTS
Same DTOs, but different names. Much better fit to the domain.
CatalogService
ShoppingCart
Service
OrderService
ProductDto
* id
* name
* description
* userRatings
* imageUrls
* price
CartEntryDto
* id
* name
* price
* quantity
LineItemDto
* id
* name
* price
* vat
* quantity
NOT INVENTED HERE
• Focus on business logic
• Don't build what you don't need to
• Queues
• Connection Pools
• Anything you build needs to be maintained.
• The only thing you'd want to maintain is business logic.
CODE DUPLICATION
CODE DUPLICATION
Wrong: use inheritance for saving number of attributes in classes
abstract class MasterDto {
protected int id;
}
public class AnyDto extends MasterDto {
...
}
public class AnyOtherDto extends MasterDto {
...
}
CODE DUPLICATION
Right: don't mix unrelated objects
public class AnyDto {
private int id;
}
public class AnyOtherDto {
private int id;
}
WHY UNIT TESTS?
TESTABLE CODE
• Methods only deal with atomic operations
• every IF in a method requires a test for every branch
• that's why code inside blocks should go into a separate method
• allows to test that method independently
• any possible input parameters require a test
• any possible return values require a test
• the smaller your classes, the smaller your methods, the easier it is to
maintain test code
• the amount of test code can easily be equal to your "real" code
TESTABLE CODE
• Don't use static classes directly in the code
public class Lala {
public int calcIt(int a, int b) {
return CalculatorUtil.calcIt(a,b);
}
}
TESTABLE CODE
• Instead, make them a Singleton and a member variable
public class Lala {
private CalcUtil calcUtil=CalcUtil.getInstance();
public int calcIt(int a, int b) {
return calcUtil.calcIt(a,b);
}
public void setCalcUtil(CalcUtil calcUtil) {
this.calcUtil = calcUtil;
}
}
AGAIN, NO INTEGRATION TEST DONE
PREMATURE OPTIMIZATION
• "Because we need the performance"
• Can you monitor the language "D"?
• Do everything in Stored Procedures (even simple CRUDs)
• You need to declare that variable outside of the loop to safe the GC from the
load
• Specialize first, generalize later
READ
• Find all this and lots more here:
• Clean Code, by
• Robert C. Martin
READ
• Find all this and lots more here:
• Growing Object-Oriented Software,
guided by Tests, by
• Steve Freeman, Nat Pryce
HOW?
WHAT DOES IT TAKE?
TRANSITIONING TO A MANAGEABLE SCOPE
EXISTING ARCHITECTURE
• Java
• GWT
• SQL-Server
• Interfaces to 3rd parties, like
• Billing systems (SAP, AWS, Monexa, ...)
• Salesforce
• Single-Sign-On
• Deployed on AWS EC2
EXISTING ARCHITECTURE
• 300k lines of code
• 47 projects in Eclipse IDE
• 155 tables in SQL-Server
AWS
Billing
Java
Backend
MSSQLD
B
Customer
facing
GWT UI
GWT-RPC
SAP
Billing
SFDC
Internal
GWT UI
JSON
THE SOLUTION: MICROSERVICES
• Smaller contexts
• easier to grasp
• easier to test
• no dependency hell
• faster to deploy
• less to test
• Smaller databases
• faster queries
• easier for blue/green deployment
• Easier scaling
• just deploy more instances instead of built-in parallelization
IN-FLIGHT ARCHITECTURE
Java
Monolith
MSSQL
Angular
Single
Page App
Java
Microservice
Java
Microservice
Java
Microservice
BFF
Angular
BFF
Android
Android
App
BFF
iOS
iOS
App
AWS
Aurora
AWS
Aurora
AWS
Aurora
BFF
REST
Generic
REST
Client
GWT
BFF/EDGE SPECIFICS
Angular
Single
Page App
BFF
Angular
/rest
BFF
Android
/android
Android
App
BFF
iOS
/ios
iOS
App
BFF
REST
/api
Generic
REST
Client
Authenticated via
Username/Password
Authenticated via
OAUTH
Authenticated via
OAUTH
Authenticated via
OAUTH
JavaScript
served by NGINX
or AWS CloudFront
MICROSERVICES SPECIFICS
• Designed to work that way from ground up
• ie UI can handle single failing micro services
• Think of a platform for deployment
• Automation is key. It always is. But here even more.
SOMETIMES IT JUST CAN'T BE MICROSERVICES
CAN WE USE SOME OF THAT FOR MOBILE OR DESKTOP?
THE PROPER MONOLITH
LAYERS
• One artifact per business domain
• 3-tiered architecture inside of each artifact
• Sharing of interfaces by means of –api artifacts
• No shared database between domains
PROPER MONOLITH
.war file
catalog
.jar
cart
.jar
billing
.jar
order
.jar
Controllers
Physical Database
Catalog
DB
Cart
DB
Billing
DB
Order
DB
TOOLS THAT HELP
• Black Duck
• Sonar
HOW TO DO IT RIGHT FROM NOW ON?
WHAT A REAL REWRITE REALLY MEANS
• June 2006
• Netscape Navigator 6 goes public beta
• last 4.x release released almost 4 years ago (there was no v5)
• https://www.joelonsoftware.com/2000/04/06/things-you-should-
never-do-part-i/
IN A NUTSHELL
• Master the craft of programming
• Avoid the mess
• Bounded Contexts
• Microservices-driven thinking and architecture
LINZ, AUSTRIA - APR 26, 2018
DEVELOPER AND OPS CONFERENCE

Old code doesn't stink

  • 1.
    OLD CODE DOESN'TSTINK Refactor or Rewrite Martin Gutenbrunner LINZ, AUSTRIA - APR 26, 2018 DEVELOPER AND OPS CONFERENCE
  • 2.
    ABOUT ME • Startedwith Commodore 8-bit (VC-20 and C-64) • assembled an i386 from second-hand parts as a gaming rig • Built Null-Modem connections for playing Doom and WarCraft I • and IPX/SPX networks between MS-DOS 6.22 and WfW 3.11 • Did DevOps before it was a thing • mainly Java and JavaScript • saw rotten code and built code that suddely rotted • Now at Dynatrace • previous: Tech Lead for Microsoft Azure and Microservices • now: Software Architect • About 14 years of experience in the industry • Find me on Twitter: @MartinGoodwell Considers himself a lucky guy
  • 3.
    ABOUT THE SHOW •Driven by example • Example #1: Legacy can mean 15 years • Example #2: Legacy can mean 5 years • The basics – aka "Mastering the craft" • The Magic Sauce, finally (spoiler: there is none)
  • 4.
    Online Shop, writtenin ASP, rendering XHTML Back then, in development for 15 years EXAMPLE 1: classic ASP
  • 5.
    PROJECT SETUP • Use-case:eCommerce • integrated with business backend system • Native Windows desktop application, connected to database • Core: classic ASP • Database: Pervasive SQL • the actual database from the business backend • Interfaces to • MSMQ for sending orders to the business backend • COM+ for querying prices from the business backend
  • 6.
  • 7.
    WE WANTED TOREWRITE. WHY? • 15 year old VB-Script codebase • lack of structure • not up to today's standards (eg Unit Testing) • hard to find VB-Script talent • Too closely bound to the business backend system • Major updates locked the database and rendered the online shop unusable
  • 8.
    CUSTOM BRANCHING <% if nCategoryId= 0 and (hostInfo.Path = "clayshop" or hostInfo.Path="bikershop" or hostInfo.Path="steelshop") then out GetPageHTML(1,"de") end if %> • Individual code branches for most tenants (~30 of them) • If a tenant canceled his contract, the codebase usually was not cleaned
  • 9.
    RENDERING WHILE ITERATINGRECORDSET <% set rsProd= oProduct.GetProduct(CLng(nProductId), CLng(nTenantId)) if not rsProd.eof then %> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr><td><img height="20" width="3" src="../../layout/pic/pix_tr.gif" border="0"></td></tr> <tr><td class="productheadcolor"> <table border="0" cellpadding="0" cellspacing="0"> <tr> <td class="productheadiconcolor"><img height="20" width="20" src="../../layout/pic/icons/icon_kl_produkttip.gif" border="0"></td> <td><div class="productheadcolor">&nbsp;<%= getText("product") %>:</div></td> </tr> </table> <% if (sTenantPath <> "that_special_shop") then DrawProducts rsProd, "productreplacement" end if %> <% end if rsProd.close set rsProd= nothing %> • No separation between data-access, business logic, and UI • Made it hard to see what really needs refactoring
  • 10.
    OUR PLAN WAS •100% re-write in Java • Create a separate database for eCommerce • Move all tenants to the new codebase within six months
  • 11.
  • 12.
    LEARNINGS • 100% re-writein Java not possible • no stable Java libraries for MSMQ and COM+ at the time • Planned timeframe (of course) didn't work • first tenant went online after 9 months • but it was using the new UI • Re-doing the UI turned out to be the hard part • we still didn't have all tenants converted after 2,5 years
  • 13.
  • 14.
    WHAT WENT WELL? •Introduction of dedicated DB • Having a separate Importer component • We built deployment automation with Jenkins • The ASP-bridge turned out to work really well
  • 15.
    WHAT WE SHOULDHAVE DONE • Identify the UI-part as the real problem • impossible to see due to no layered code • Embrace the fact that we have a huge number of customers on the "old" codebase and design the new system for multiple UI technologies • EOL the old codebase • If customers want new features, migrate them over
  • 16.
    INTRODUCING BFF • BFF •Backend-for-Frontend • aka Edge-Service • source: Sam Newman's Microservice book • can be used for • routing • authentication • filtering
  • 17.
  • 18.
    BENEFITS OF DOINGIT RIGHT • Save months of efforts to port the messy UI code • Have ASP UI benefit from separate database • Only have a single touchpoint with the business backend system • for any client • XHTML rendered by ASP • HTML5 rendered by Spring MVC • potential mobile apps
  • 19.
    BUSINESS BACKEND FIVE YEARSIN THE MAKING OLD CODE DOESN'T STINK? IT MIGHT SMELL A LOT, THOUGH. EXAMPLE 2: Java GWT
  • 20.
    SETTING THE STAGE •Problems • Increasing number of bugs (~ 300k lines of code) • Steadily decreasing velocity of teams (due to # of bugs) • DB started to make problems. Regular restarts required (once a quarter) • Mistakes made • Bad code structure • Next to no test coverage • Solution • Transition to a manageable scope • Boyscout rule: leave the campground cleaner than you found it
  • 21.
    WHY NOT REWRITE? •Existing solution in the making for 5 years • lots of domain knowledge would have to be re-implemented • very error-prone • Who would rewrite it? • Same teams: who would maintain the current version? • New teams: they would have to learn domain knowledge from scratch • and new teams don't appear out of nowhere • Timeline for starting over? • can you re-implement a five-year-old system in 6/9/12(?) months?
  • 22.
    WHY NOT REWRITE? •We would have built the same thing again • Even though it seems that a system is broken as a whole, it probably isn't. Lots of it usually works. • Our approach: educate the teams • what went wrong • how to do better • And apply their learnings to the existing system instead of creating a new one • Plus: bring in new people to the teams
  • 23.
    IT MIGHT SMELL,THOUGH. EDUCATING THE TEAMS MASTERING THE CRAFT OLD CODE DOESN'T STINK
  • 24.
    HOW CAN THATEVEN HAPPEN? • Bounded contexts • Things built in-house that would have been readily available • Code that's not testable • Code that's not clearly/properly structured • Bugs due to premature optimization
  • 25.
    BOUNDED CONTEXTS Don't mixthings that don't belong together • Do you see what's wrong with the "UserAccount" table?
  • 26.
    BOUNDED CONTEXTS Wrong: sharingDTOs between different domains Some services use same attributes, some use specific ones CatalogService ShoppingCart Service OrderService ProductDto * id * name * description * userRatings * imageUrls * price * vat * quantity
  • 27.
    BOUNDED CONTEXTS Right: dedicatedDTOs for each domain but smells like duplication a lot, because of the names better: each DTO only contains the attributes it needs CatalogService ShoppingCart Service OrderService CatalogProductDto * id * name * description * userRatings * imageUrls * price CartProductDto * id * name * price * quantity OrderProductDto * id * name * price * vat * quantity
  • 28.
    BOUNDED CONTEXTS Same DTOs,but different names. Much better fit to the domain. CatalogService ShoppingCart Service OrderService ProductDto * id * name * description * userRatings * imageUrls * price CartEntryDto * id * name * price * quantity LineItemDto * id * name * price * vat * quantity
  • 29.
    NOT INVENTED HERE •Focus on business logic • Don't build what you don't need to • Queues • Connection Pools • Anything you build needs to be maintained. • The only thing you'd want to maintain is business logic.
  • 30.
  • 31.
    CODE DUPLICATION Wrong: useinheritance for saving number of attributes in classes abstract class MasterDto { protected int id; } public class AnyDto extends MasterDto { ... } public class AnyOtherDto extends MasterDto { ... }
  • 32.
    CODE DUPLICATION Right: don'tmix unrelated objects public class AnyDto { private int id; } public class AnyOtherDto { private int id; }
  • 33.
  • 34.
    TESTABLE CODE • Methodsonly deal with atomic operations • every IF in a method requires a test for every branch • that's why code inside blocks should go into a separate method • allows to test that method independently • any possible input parameters require a test • any possible return values require a test • the smaller your classes, the smaller your methods, the easier it is to maintain test code • the amount of test code can easily be equal to your "real" code
  • 35.
    TESTABLE CODE • Don'tuse static classes directly in the code public class Lala { public int calcIt(int a, int b) { return CalculatorUtil.calcIt(a,b); } }
  • 36.
    TESTABLE CODE • Instead,make them a Singleton and a member variable public class Lala { private CalcUtil calcUtil=CalcUtil.getInstance(); public int calcIt(int a, int b) { return calcUtil.calcIt(a,b); } public void setCalcUtil(CalcUtil calcUtil) { this.calcUtil = calcUtil; } }
  • 37.
  • 38.
    PREMATURE OPTIMIZATION • "Becausewe need the performance" • Can you monitor the language "D"? • Do everything in Stored Procedures (even simple CRUDs) • You need to declare that variable outside of the loop to safe the GC from the load • Specialize first, generalize later
  • 39.
    READ • Find allthis and lots more here: • Clean Code, by • Robert C. Martin
  • 40.
    READ • Find allthis and lots more here: • Growing Object-Oriented Software, guided by Tests, by • Steve Freeman, Nat Pryce
  • 41.
    HOW? WHAT DOES ITTAKE? TRANSITIONING TO A MANAGEABLE SCOPE
  • 42.
    EXISTING ARCHITECTURE • Java •GWT • SQL-Server • Interfaces to 3rd parties, like • Billing systems (SAP, AWS, Monexa, ...) • Salesforce • Single-Sign-On • Deployed on AWS EC2
  • 43.
    EXISTING ARCHITECTURE • 300klines of code • 47 projects in Eclipse IDE • 155 tables in SQL-Server AWS Billing Java Backend MSSQLD B Customer facing GWT UI GWT-RPC SAP Billing SFDC Internal GWT UI JSON
  • 44.
    THE SOLUTION: MICROSERVICES •Smaller contexts • easier to grasp • easier to test • no dependency hell • faster to deploy • less to test • Smaller databases • faster queries • easier for blue/green deployment • Easier scaling • just deploy more instances instead of built-in parallelization
  • 45.
  • 46.
    BFF/EDGE SPECIFICS Angular Single Page App BFF Angular /rest BFF Android /android Android App BFF iOS /ios iOS App BFF REST /api Generic REST Client Authenticatedvia Username/Password Authenticated via OAUTH Authenticated via OAUTH Authenticated via OAUTH JavaScript served by NGINX or AWS CloudFront
  • 47.
    MICROSERVICES SPECIFICS • Designedto work that way from ground up • ie UI can handle single failing micro services • Think of a platform for deployment • Automation is key. It always is. But here even more.
  • 48.
    SOMETIMES IT JUSTCAN'T BE MICROSERVICES CAN WE USE SOME OF THAT FOR MOBILE OR DESKTOP? THE PROPER MONOLITH
  • 49.
    LAYERS • One artifactper business domain • 3-tiered architecture inside of each artifact • Sharing of interfaces by means of –api artifacts • No shared database between domains
  • 50.
  • 51.
    TOOLS THAT HELP •Black Duck • Sonar
  • 52.
    HOW TO DOIT RIGHT FROM NOW ON?
  • 53.
    WHAT A REALREWRITE REALLY MEANS • June 2006 • Netscape Navigator 6 goes public beta • last 4.x release released almost 4 years ago (there was no v5) • https://www.joelonsoftware.com/2000/04/06/things-you-should- never-do-part-i/
  • 54.
    IN A NUTSHELL •Master the craft of programming • Avoid the mess • Bounded Contexts • Microservices-driven thinking and architecture
  • 55.
    LINZ, AUSTRIA -APR 26, 2018 DEVELOPER AND OPS CONFERENCE