Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Old code doesn't stink

285 views

Published on

I've seen projects with shiny, new code render into unmaintainable big balls of mud within 2-3 years. Multiple times. But regardless of whether it's the code base as a whole that's rotten, or whether it's just the UI and User Experience that needs a major overhaul: the question on rewrite vs refactoring will come up sooner or later. Based on years of experience, and a plethora of bad decisions cumulating into epic failures, I'll share my experience on how to have a code base that stays maintainable - even after years. After this talk, you'll have more insight into whether you should refactor or rewrite, and how to do it right from now on.

Published in: Technology
  • Be the first to comment

Old code doesn't stink

  1. 1. OLD CODE DOESN'T STINK Refactor or Rewrite Martin Gutenbrunner LINZ, AUSTRIA - APR 26, 2018 DEVELOPER AND OPS CONFERENCE
  2. 2. 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
  3. 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. 4. Online Shop, written in ASP, rendering XHTML Back then, in development for 15 years EXAMPLE 1: classic ASP
  5. 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. 6. OLD ARCHITECTURE ASP UI XHTML Business Backend System SQL-DB MSMQ (Orders) COM+ (Prices) Browser (XHTML)
  7. 7. 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
  8. 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. 9. 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
  10. 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. 11. PLANNED ARCHITECTURE Business Backend SystemSQL DB MSMQ (Orders) COM+ (Prices) Importer Java Spring MVC MongoD B Browser (HTML5)
  12. 12. 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
  13. 13. DONE ARCHITECTURE ASP Bridge Business Backend SystemSQL DB MSMQ (Orders) COM+ (Prices) Importer Java MongoD B Browser (HTML5) ASP Backend Browser (XHTML)
  14. 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. 15. 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
  16. 16. INTRODUCING BFF • BFF • Backend-for-Frontend • aka Edge-Service • source: Sam Newman's Microservice book • can be used for • routing • authentication • filtering
  17. 17. 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
  18. 18. 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
  19. 19. BUSINESS BACKEND FIVE YEARS IN THE MAKING OLD CODE DOESN'T STINK? IT MIGHT SMELL A LOT, THOUGH. EXAMPLE 2: Java GWT
  20. 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. 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. 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. 23. IT MIGHT SMELL, THOUGH. EDUCATING THE TEAMS MASTERING THE CRAFT OLD CODE DOESN'T STINK
  24. 24. 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
  25. 25. BOUNDED CONTEXTS Don't mix things that don't belong together • Do you see what's wrong with the "UserAccount" table?
  26. 26. 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
  27. 27. 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
  28. 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. 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. 30. CODE DUPLICATION
  31. 31. 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 { ... }
  32. 32. CODE DUPLICATION Right: don't mix unrelated objects public class AnyDto { private int id; } public class AnyOtherDto { private int id; }
  33. 33. WHY UNIT TESTS?
  34. 34. 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
  35. 35. 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); } }
  36. 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. 37. AGAIN, NO INTEGRATION TEST DONE
  38. 38. 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
  39. 39. READ • Find all this and lots more here: • Clean Code, by • Robert C. Martin
  40. 40. READ • Find all this and lots more here: • Growing Object-Oriented Software, guided by Tests, by • Steve Freeman, Nat Pryce
  41. 41. HOW? WHAT DOES IT TAKE? TRANSITIONING TO A MANAGEABLE SCOPE
  42. 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. 43. 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
  44. 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. 45. 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
  46. 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 Authenticated via Username/Password Authenticated via OAUTH Authenticated via OAUTH Authenticated via OAUTH JavaScript served by NGINX or AWS CloudFront
  47. 47. 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.
  48. 48. SOMETIMES IT JUST CAN'T BE MICROSERVICES CAN WE USE SOME OF THAT FOR MOBILE OR DESKTOP? THE PROPER MONOLITH
  49. 49. 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
  50. 50. PROPER MONOLITH .war file catalog .jar cart .jar billing .jar order .jar Controllers Physical Database Catalog DB Cart DB Billing DB Order DB
  51. 51. TOOLS THAT HELP • Black Duck • Sonar
  52. 52. HOW TO DO IT RIGHT FROM NOW ON?
  53. 53. 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/
  54. 54. IN A NUTSHELL • Master the craft of programming • Avoid the mess • Bounded Contexts • Microservices-driven thinking and architecture
  55. 55. LINZ, AUSTRIA - APR 26, 2018 DEVELOPER AND OPS CONFERENCE

×