O slideshow foi denunciado.
Seu SlideShare está sendo baixado. ×

Old code doesn't stink - Detroit

Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Carregando em…3
×

Confira estes a seguir

1 de 63 Anúncio

Old code doesn't stink - Detroit

Baixar para ler offline

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.

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.

Anúncio
Anúncio

Mais Conteúdo rRelacionado

Diapositivos para si (18)

Semelhante a Old code doesn't stink - Detroit (20)

Anúncio

Mais recentes (20)

Anúncio

Old code doesn't stink - Detroit

  1. 1. OLD CODE DOESN'T STINK Refactor or Rewrite Martin Gutenbrunner FORD FIELD, DETROIT – OCT 16, 2018 DEVELOPER AND OPS CONFERENCE
  2. 2. ABOUT ME Interested in Software and Hardware. Started coding at 14 Programmer, team lead, software architect, operator, administrator In pursuit of the right way to build software Passionate about technology, and much more about the people he's working with Find me on Twitter: @MartinGoodwell Considers himself a lucky guy
  3. 3. THIS WILL BE LIKE • James Camerson's Avatar • at least 50% of it is "heavily inspired by others" • A river runs through it, feat Brad Pitt and Tom Skerrit • the other 50% is real-life experience • The Walking Dead • we'll have a Walker • Real-life Example: Classic ASP, 15 years later • The basics – aka "Mastering the craft" • The Magic Sauce. The Kool Aid. Mr Miyagi's ultimate wisdom.
  4. 4. Yes or no? Refactor or Rewrite?
  5. 5. WHAT A REAL REWRITE REALLY MEANS • Netscape Navigator 6 goes public beta in Nov 2000 • last major release (4.0) released almost 3 years ago (there was no v5) • " During this time, Netscape sat by, helplessly, as their market share plummeted. "
  6. 6. SAME MISTAKES • Borland  Arago  dBase for Windows • Microsoft Access ate their lunch • then they made the same mistake again in rewriting Quattro Pro from scratch and astonishing people with how few features it had. • Microsoft  Pyramid  Word for Windows from scratch • Lucky for Microsoft, they had never stopped working on the old code base, so they had something to ship, making it merely a financial disaster, not a strategic one.
  7. 7. WHY? It’s harder to read code than to write it. - Joel Spolsky https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/
  8. 8. What is usually wrong? • Architectural problems • Even fairly major architectural changes can be done without throwing away the code. By just moving things around, cleaning them up, creating base classes that made sense, and creating sharp interfaces between the modules. • Because it's inefficient • When optimizing for speed, 1% of the work gets you 99% of the bang • The code is doggone ugly • One project I worked on actually had a data type called a FuckedString • So half the functions started with “_” and half with “m_”, which looked ugly. • This is the kind of thing you solve in five minutes with a macro in Emacs, not by starting from scratch.
  9. 9. When to Rewrite? • Is your code closely tied to a certain lifecycle / framework? • eg Reactive, Mobile Apps, ... • No one can maintain the existing code • obsolete technology • everyone's scared by just thinking about touching the code • Always ask yourself: does this need to be a full rewrite?
  10. 10. MAKE or BUY? • Make the parts that make your solution unique • "Buy" the parts that are common
  11. 11. Online Shop, written in ASP, rendering XHTML Back then, in development for 15 years EXAMPLE: classic ASP
  12. 12. PROJECT SETUP • Use-case: eCommerce • close connection to a 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
  13. 13. OLD ARCHITECTURE ASP UI XHTML Business Backend System SQL-DB MSMQ (Orders) COM+ (Prices) Browser (XHTML) ~ 1999
  14. 14. WE WANTED TO REWRITE. WHY? • 15 year old VB-Script codebase • lack of structure • hard to find VB-Script talent • not up to today's standards (eg Unit Testing) • Too closely bound to the business backend system • Major updates of the system locked the database and stalled the online shop
  15. 15. 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
  16. 16. 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
  17. 17. OUR PLAN WAS • 100% re-write in Java • Create a separate database for the eCommerce part • Move all tenants to the new codebase within six months
  18. 18. PLANNED ARCHITECTURE Business Backend SystemSQL DB MSMQ (Orders) COM+ (Prices) Importer Java Spring MVC MongoD B Browser (HTML5) ~ 2012
  19. 19. 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 • (so, the old solution was still running in production in parallel) • Re-doing the UI turned out to be the hard part • we still didn't have all tenants converted after 2,5 years
  20. 20. DONE ARCHITECTURE ASP Bridge Business Backend SystemSQL DB MSMQ (Orders) COM+ (Prices) Importer Java MongoD B Browser (HTML5) ASP Backend Browser (XHTML) ~ 2014
  21. 21. 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
  22. 22. WHAT WE SHOULD HAVE DONE • Identify the UI-part as the real problem • impossible to see due to non-layered codebase • 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 • Not a full rewrite
  23. 23. INTRODUCING BFF • BFF • Backend-for-Frontend • aka Edge-Service • source: Sam Newman's Microservice book • can be used for • routing • authentication • filtering
  24. 24. 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
  25. 25. BENEFITS OF DOING IT RIGHT • Save months of efforts, porting 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
  26. 26. IT MIGHT SMELL, THOUGH. MASTERING THE CRAFT OLD CODE DOESN'T STINK
  27. 27. HOW CAN THAT EVEN HAPPEN? • Bounded contexts • Things built in-house that would have been readily available • a lot of the mess happens in boilerplate • Code that's not testable • Code that's not clearly/properly structured • Harder to read than to write, remember? • Bugs due to premature optimization • YAGNI – You aren't gonna need it • I think we tought that we'll need this for that feature that's coming up.
  28. 28. BOUNDED CONTEXTS Don't mix things that don't belong together • Do you see what's wrong with the "UserAccount" table?
  29. 29. 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
  30. 30. 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
  31. 31. 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
  32. 32. NOT INVENTED HERE • Focus on business logic • and separate it from boilerplate • 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.
  33. 33. CODE DUPLICATION
  34. 34. CODE DUPLICATION Wrong: use inheritance for saving number of attributes in classes abstract class MasterPojo { protected int id; } public class AnyPojo extends MasterPojo { ... } public class AnyOtherPojo extends MasterPojo { ... }
  35. 35. CODE DUPLICATION Right: don't mix unrelated objects public class AnyPojo { private int id; } public class AnyOtherPojo { private int id; }
  36. 36. CODE DUPLICATION Code duplication really means that any code that makes decisions (eg IF, loops, ...) should not be copy/pasted. Example: • We have integrated with MailChimp for sending Emails • We also want to integrate with other messaging services • So, we copy the code from our MailChimp integration, paste it into a new package and just rename the classes and make changes according to the new 3rd party there. • Right way: identify common behavior. Have that in base-classes or a library that can be used by both implementations.
  37. 37. WHY UNIT TESTS?
  38. 38. DESIGN FOR TESTABILITY • Try to be as atomic as possible (complex objects vs atomic) • In Java: use package private instead of private • Have methods do as little as possible • Methods that do more should just call other methods
  39. 39. TESTABLE CODE for if break condition boolean condition public int categorize(Client client, int metric) { return result; }
  40. 40. TESTABLE CODE for break condition public int categorize(Client client, int metric) { return result; } boolean cascadedMethod() { if } cascadedMethod()
  41. 41. THINGS TO CONSIDER • Integers: negative, ranges • Strings: length, special characters, null, empty, case-sensitive • Objects: • all of the above, depending on member variables datatypes
  42. 42. TESTING CODE A code tester walks into a bar. Orders a beer. Orders 2.15 billion beers. Orders -1 beers. Orders äüöß beers. Orders a nothing. Orders a cat. Tries to leave without paying.
  43. 43. TESTABLE CODE • 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 • any method should be testable independently
  44. 44. TESTABLE CODE • Some relationships are hard to test. public int calcIt(int a, int b) { int one = CalcUtil.calcIt(a,b); int result = doSomethingElse(one); return result; }
  45. 45. TESTABLE CODE • Some relationships are hard to test. public int calcIt(int a, int b) { int one = CalcUtil.getInstance().calcIt(a,b); int result = doSomethingElse(one); return result; }
  46. 46. TESTABLE CODE • Any dependencies to other classes should be easy to change. private CalcUtil calcUtil = CalcUtil.getInstance(); public int calcIt(int a, int b) { int one = calcUtil.calcIt(a,b); int result = doSomethingElse(one); return result; } void setCalcUtil(CalcUtil calcUtil) { this.calcUtil = calcUtil; }
  47. 47. READABLE CODE • Be as descriptive in your code as possible public class Walker { public int moveAround(int position, int angle) { if (angle > 0 && angle < 180) { //go left ... } ... } }
  48. 48. READABLE CODE • Be as descriptive in your code as possible public class Walker { public int moveAround(int position, int angle) { boolean goLeft = angle > 0 && angle < 180; if (goLeft) { ... } ... } }
  49. 49. READABLE AND TESTABLE CODE • Be as descriptive in your code as possible public class Walker { public int moveAround(int a, int b) { boolean goLeft = shouldGoLeft(a,b); if (goLeft) { ... } } boolean shouldGoLeft(int a, int b) { return angle > 0 && angle < 180; } }
  50. 50. MEANINGFUL DOCUMENTATION • Class-level comments to give context. /** * This is the entry class. You'll find most * interesting entrypoints here. * @see: ThatOther.class */ public class Lala { }
  51. 51. MISSING INTEGRATION TESTS
  52. 52. AGAIN, NO INTEGRATION TEST DONE
  53. 53. PREMATURE OPTIMIZATION • "Because we need the performance" • We need to implement it in "D" • Do everything in Stored Procedures (even simple CRUDs) • You need to declare that variable outside of the loop to reduce the load on the GC • Specialize first, generalize later • "I think we thought that we will need that for one of the screens." • Release often • finish one feature  release it  get feedback  improve it • The IT-world has changed. It's not about CPU-cycles anymore • It's about codebases that can scale to a large number of programmers.
  54. 54. READ • Find all this and lots more here: • Clean Code, by • Robert C. Martin
  55. 55. READ • Find all this and lots more here: • Growing Object-Oriented Software, guided by Tests, by • Steve Freeman, Nat Pryce
  56. 56. CAN WE USE SOME OF THAT FOR MOBILE OR DESKTOP? THE PROPER MONOLITH
  57. 57. LAYERS • One artifact per business domain • 3-tiered architecture inside of each artifact • Sharing of interfaces by means of –api artifacts • No shared database schemas between domains
  58. 58. PROPER MONOLITH .war file catalog.jar cart.jar billing.jar order.jar Controllers Physical Database Catalog DB Cart DB Billing DB Order DB catalog-api.jar cart-api.jar billing-api.jar order-api.jar
  59. 59. TOOLS THAT HELP • Black Duck • Sonar
  60. 60. HOW TO DO IT RIGHT FROM NOW ON?
  61. 61. THE ULTIMATE TRUTH • Master the craft of programming • Avoid the mess • Bounded Contexts • domain-driven thinking and architecture
  62. 62. FORD FIELD, DETROIT – OCT 16, 2018 DEVELOPER AND OPS CONFERENCE

×