Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Domain Re-discovery Patterns for Legacy Code (v...

Richard
October 21, 2024

Domain Re-discovery Patterns for Legacy Code (v2) 🇬🇧 @Software Architecture Alliance 2024

In the world of software development, legacy code often poses significant challenges. Existing codebases, built years ago, may lack documentation and understanding of the original domain. This lack of knowledge can hinder effective maintenance, upgrades, and feature development. The process of rediscovering the domain of legacy code is invaluable for developers seeking to enhance and extend these systems.

In this talk we'll delve into various domain re-discovery patterns that help in identifying and reconstructing the domain model that the legacy code represents. These patterns go beyond merely deciphering the code's functionality; rather, they provide strategies to comprehend the underlying concepts, behaviors, and relationships in the domain.

Attendees can expect to gain practical insights, methodologies, and practical tips to tackle the challenges associated with legacy codebases. By embracing domain rediscovery patterns, developers can bring order and coherence to legacy systems, paving the way for future enhancements and system evolution.

Richard

October 21, 2024
Tweet

More Decks by Richard

Other Decks in Programming

Transcript

  1. 22.10.24 Richard Gross (he/him) Archaeology + Health Checks richargh.de/ speakerdeck.com/richargh

    @arghrich Hypermedia TestDSLs Domain Re-discovery Patterns for Legacy Code
  2. What we find on the stone tablet is ... confusing

    Slide 3 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
  3. We’ll work backwards from code Slide 5 by richargh.de from

    https://fs.blog/2013/07/the-difference-between-science-and-engineering/ IT Archaeology
  4. We’ll be working with metrics 1. Every measure which becomes

    a target becomes a bad measure1 2. Metric hotspots are only conversation starters 3. The truth is in the conversation Slide 6 by richargh.de from 1 https://en.wikipedia.org/wiki/Goodhart%27s_law
  5. We‘ll visualize metrics with CodeCharta1 buildings Slide 7 by richargh.de

    from Plug by Zaufishan Gource is a cool git visualizer https://gource.io/ CodeScene is a good Charta-alternative: https://codescene.com/ 1 CodeCharta is open-source https://maibornwolff.github.io/codecharta/ f.ex. Complexity f.ex. Number of authors Lines of Code SomeService.kt
  6. We’ll also lean on the IDE1 a lot 1. Your

    IDE can do a lot safely that would be dangerous to do by hand 2. But only provide you actually use the tools: “Refactor this” 3. In some languages you can also “lean on the compiler”2 4. Moving stuff by hand, without tests, will always byte you 5. Please don’t ever change order of methods without extensive tests Slide 8 by richargh.de from 1 Integrated development environment https://en.wikipedia.org/wiki/Integrated_development_environment 2 Working Effectively with Legacy Code https://www.amazon.com/dp/0131177052/
  7. A lot of helpful rediscovery patterns Slide 9 by richargh.de

    from Software Health Check Modernization Project Passive Patterns (Analyze) Active Patterns (Change) Significantly longer 3 weeks
  8. A lot of helpful rediscovery patterns Slide 10 by richargh.de

    from Passive Patterns (Analyze) Active Patterns (Change) Where to start What to try
  9. Disclaimer: Most of this is “just” extract+move code and give

    it a name The patterns guide the extraction Slide 11 by richargh.de from This is the hard part
  10. Pattern categories Slide 12 by richargh.de from Capture the Behavior

    Mine the Expert Mine the Repo Not the focus today
  11. Passive Pattern: Code Tag Cloud1,2 Context • Get a high

    level overview of possible domain terms Approach • Generate a tag cloud from code by extracting either the names or the behaviors (invoked methods) Caveat • Repetition wins, not necessarily importance Slide 14 by richargh.de from 1 Original Idea, probably by Kevlin Henney https://youtu.be/SUIUZ09mnwM?feature=shared&t=2226 2 Original tag cloud code: https://www.feststelltaste.de/word-cloud-computing/
  12. Pattern: Code Tag Cloud Discover the modeled names Slide 15

    by richargh.de from Generated with: https://github.com/Richargh/code-tagcloud-py-sandbox Stringly or Strongly Typed?
  13. Active Pattern: Strong1 Code Tag Cloud Slide 16 by richargh.de

    from 1 Opposite of stringly https://www.hanselman.com/blog/stringly-typed-vs-strongly-typed 2 https://luzkan.github.io/smells/magic-number 3 Nine steps to better software design today, by Jeff Bay Bring the domain forward • Model your ids record UserId(String rawValue){} → Remove a bug source, see connections better • Primitive Value Objects • Is it a anything-goes string or are there domain constraints? • Does a number have significance, can you give it a name2 or type? • Elements that are always passed/returned together → is there a domain concept missing? • Object Calisthenics3
  14. Annotation-Whiskers or strong types Slide 17 by richargh.de from 1

    uses the checker framework https://checkerframework.org/ As annotation1 (not as invasive) Or as type (when you are sure) 1. @m int meters = 5 * UnitsTools.m; 2. @s int seconds = 2 * UnitsTools.s; 3. 4. // allowed 5. @mPERs int speed = meters / seconds; 6. 7. // produces a compile-error 8. @m int foo = meters + seconds; 1. Meters meters = Meters.of(5); 2. Seconds seconds = Seconds.of(2); 3. 4. // allowed 5. Speed speed = meters.per(seconds); 6. 7. // produces a design-time error 8. var foo = meters.plus(seconds);
  15. Pattern: Code Tag Cloud The domain peeks out Slide 18

    by richargh.de from Generated with: https://github.com/Richargh/code-tagcloud-py-sandbox Where is “String”? Still some work left…
  16. The Domain peeks out Slide 19 by richargh.de from ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold
  17. Anti-Pattern: Package by Layer1,2 Slide 20 by richargh.de from 1

    Presentation-Domain-Data Layering: https://martinfowler.com/bliki/PresentationDomainDataLayering.html 2 Simon Brown has a good explanation as well: https://dzone.com/articles/package-component-and services repositories controllers utils models Controller A Controller B Presentation Layer Service A Service B Domain Layer Repository A Repository B Data Layer It‘s all about the frameworks Models intermixed with unrelated models. Utils are a smell
  18. Active Pattern: Package by Component1,2 Context • Components mean: study

    & (heavily) change one thing at a time • Group together what fires together: • 1 feature, 1 commit, 1 component • Top-level components communicate domain • service/, models/, repositories/ vs • book/, inventory/, dailysheet/ Approach 1. Move interacting elements closer to each other (with IDE) 2. Start with the controller and group what it needs 3. Layering is only the secondary organisation mechanism Caveat • You can’t get it right the first time. A lot of spaghetti- dependencies will still exist for now Slide 21 by richargh.de from 1 Presentation-Domain-Data Layering: https://martinfowler.com/bliki/PresentationDomainDataLayering.html 2 Simon Brown has a good explanation as well: https://dzone.com/articles/package-component-and
  19. commons Active Pattern: Package by Component1,2 Slide 22 by richargh.de

    from 1 Presentation-Domain-Data Layering: https://martinfowler.com/bliki/PresentationDomainDataLayering.html 2 Simon Brown has a good explanation as well: https://dzone.com/articles/package-component-and Controller A Controller B Presentation Layer Service A Service B Domain Layer Repository A Repository B Data Layer patron book dailysheet inventory
  20. The Domain gets top-level modules Slide 23 by richargh.de from

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold patron book dailysheet inventory
  21. inventory dailysheet Patron An ordered mess emerges Slide 24 by

    richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold Book
  22. When will it be done? About now is when people

    get nervous Slide 25 by richargh.de from
  23. Communication Pattern: Quality Views1 Context • Your domain re-discovery is

    going to take quite some time • It’s best to communicate where new features are easy and where they are hard Approach 1. Draw your code structure 2. Colorize based on ease-of-change 3. Mark what you work on right now Slide 26 by richargh.de from 1 Based-on https://blog.colinbreck.com/using-quality-views-to-communicate-software-quality-and-evolution/
  24. Communication Pattern: Quality Views Colorize based on changeability Slide 27

    by richargh.de from Increasing changeability Patron Component Explicit Outside Api (Explicit Schema and Dtos) ∅ Enforced component bounds (f.ex. Arch-Unit tests) ✗ Characterization tests line coverage +90% ✓ Unit+integration tests line coverage +90% ✗ Key SLAs tested ✗ Presentation-Data-Domain Layering ✗ Code in good shape (low smells/good IOSP ratio) ✗ ✓ Yes ✗ No ∅ Does not apply Component testable (interact with outside world via ports) ✓ Unit test domain mutation coverage +90% ✗ Behavior Structure No obsolete or critically vulnerable depedendencies ✗ Built-in Logs, Metrics, Alerts ✗ 2/11
  25. Communication Pattern: Quality Views Colorize based on changeability Slide 28

    by richargh.de from Increasing changeability Patron Component Explicit Outside Api (Explicit Schema and Dtos) ∅ Enforced component bounds (f.ex. Arch-Unit tests) ✗ Characterization tests line coverage +90% ✓ Unit+integration tests line coverage +90% ✗ Key SLAs tested ✗ Presentation-Data-Domain Layering ✗ Code in good shape (low smells/good IOSP ratio) ✗ ✓ Yes ✗ No ∅ Does not apply Component testable (interact with outside world via ports) ✓ Unit test domain mutation coverage +90% ✗ Behavior Structure No obsolete or critically vulnerable depedendencies ✗ Built-in Logs, Metrics, Alerts ✗ 2/11
  26. Communication Pattern: Quality Views High-Level-View Slide 29 by richargh.de from

    Increasing changeability Better Worse Patron Book Inventory Component Locked Transforming for future features Focus Slight quality dip since last communication No changes planned or wanted
  27. Communication Pattern: Quality Views Capability-View Slide 30 by richargh.de from

    Increasing changeability Better Worse Patron Book Inventory Component Capability Locked Feature-planning often requires capability details Focus search quote book search
  28. By adressing these Slide 31 by richargh.de from Explicit Outside

    Api (Explicit Schema and Dtos) Enforced component bounds (f.ex. Arch-Unit tests) Characterization tests Unit+integration tests Presentation-Data-Domain Layering Code in good shape (low smells/good IOSP ratio) Component testable (interact with outside world via ports) Domain Unit tests Behavior Structure We’ll unearth Domain concepts ✓ Behavior documentation (tests ☺) ✓ Fixing this typically leads to smaller things that need names Typically leads to questions that unearth missing concepts
  29. We could stop here but... …the additional patterns will show

    us where to look Slide 32 by richargh.de from
  30. Passive Pattern: Temporal Coupling1 Context • If a change in

    A often requires a change in B, the elements are temporally coupled1. • The reason for this often invisible coupling is a high Connascence. Approach 1. Count how often A was commited together with B. If high, draw A → B. 2. Count how often B was commited without A. If high, don’t draw B → A. 3. In the code-map, mark these elements. Slide 33 by richargh.de from 1 from the book https://pragprog.com/titles/atcrime/your-code-as-a-crime-scene
  31. Passive Pattern: Temporal Coupling1 Slide 34 by richargh.de from 1

    from the book https://pragprog.com/titles/atcrime/your-code-as-a-crime-scene CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines of Code Cycl. Complexity Number of authors (high) Ingoing Temporal Coupling BookService BookService Notice that no compile-time relation exists between the temporally coupled files.
  32. You’ve probably been a victim of temporal coupling Slide 35

    by richargh.de from B C A Temporal Coupling Static Coupling Both are forms of Connascence Patron Book Dailysheet Inventory D E
  33. Slide 36 by richargh.de from Connascence by Meilir Page-Jones, “Fundamentals

    of Object-Oriented Design in Uml” 2 elements A,B are connascent if there is at least 1 possible change to A requires a change to B in order to maintain overall correctness.
  34. Connascence of Slide 37 by richargh.de from Connascence: https://www.maibornwolff.de/en/know-how/connascence-rules-good-software-design/ •

    Name: variable, method, SQL Table • Type: int, String, Money, Person • Meaning: what is true,‘YES‘,null,love • And 6 more Easy Hard on your brain Good Bad
  35. Connascence describes the type of coupling Slide 38 by richargh.de

    from Connascence: https://www.maibornwolff.de/en/know-how/connascence-rules-good-software-design/ • Name (CoN) • Type (CoT) • Meaning (CoM) • And 6 more Easy Hard on your brain Static coupling Temporal coupling (Duplication) Refactor this way Expensive Change Cheap Hidden domain Explicit Domain
  36. Connascence guides refactoring Slide 39 by richargh.de from Connascence: https://www.maibornwolff.de/en/know-how/connascence-rules-good-software-design/

    Connascence of Name 1. // A) 2. enum MovieType { } 3. // B) 4. sealed interface Movie permits RegularMovie { } 3. // C) 4. interface Movie { 5. int amount(){ … } 6. } 7. // D) 8. // appropriate solution is a team effort 1. // A) 2. static int OLD_PEOPLE_PENALTY = 25; 3. // B) 4. // appropriate solution is a team effort ☺ Connascence of Meaning
  37. Active Pattern: Temporal decoupling Slide 40 by richargh.de from •

    Find elements with lots of temporal coupling • Identify type of Connascence that leads to the coupling 1. Try to move strongly connascent elements closer to each other 2. Try to refactor to a connascence of lower strength 3. If all else fails, “lock” the connascent elements → move them to a place that won’t receive changes (2) Will usually uncover new domain concepts
  38. inventory dailysheet Patron New concepts emerge, some move, some merge

    Slide 41 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold Book BookType Daily Copy
  39. Passive Pattern: Entity Ownership Context • Which part of the

    code “owns” an entity is highly related to which WRITEs to the table. • Only one code part should “own” an entity so it can protect its contract (invariants, pre-/post conditions) Approach 1. grep Reads: SELECT, JOIN 2. grep Writes: INSERT, UPDATE, DELETE 3. Table or plot for each entity which components reads an entity and which writes Slide 42 by richargh.de from
  40. Passive Pattern: Entity Ownership Who reads and writes book? Slide

    43 by richargh.de from Patron Book Dailysheet Inventory Read Scattered writes • Is entity contract protected everywhere? • Is entity contract in sync everywhere? Scattered reads • Does everyone really need the entity? • Does everyone need the same fields? Write
  41. Active Pattern: Entity Ownership Bounds Only one write location •

    Don’t write the entity if you don’t own it • If you have to write, delegate to owner • The owner knows what a valid entity is Read • If scattered reads have little field overlap, consider splitting entity • Get feedback on domain names of splits • See if split has a different owner • Keep it in sync via events Slide 44 by richargh.de from
  42. Active Pattern: North-Star Architecture Context • At some point we

    want to write new code but still have so much confusing legacy • We want to make sure we’re all working toward the same goal • We don’t want to wait with feature development until everything is shiny Approach 0. Define the north-star: the architecture you desire 1. Codify north-star1,2,3 2. Freeze existing violations 3. Continuously run static analysis 4. Fix new violations immediately Slide 45 by richargh.de from 1 Using ArchUnit for Java https://www.archunit.org/ 2 TSArch for TS/JS https://github.com/ts-arch/ts-arch 3 or Dependency Cruiser for TS/JS https://github.com/sverweij/dependency-cruiser
  43. Active Pattern: North-Star Architecture Slide 46 by richargh.de from Example

    uses ArchUnit https://www.archunit.org/ 1. @ArchTest 2. static final ArchRule no_classes_should_depend_on_service = 3. freeze( // accept existing violations 4. noClasses() 5. .should() 6. .dependOnClassesThat() 7. .resideInAPackage("..service..")); 8. @ArchTest 9. static final ArchRule services_should_not_access_controllers = 10. noClasses() // only green without violations 11. .that().resideInAPackage("..service..") 12. .should().accessClassesThat().resideInAPackage("..controller..");
  44. Don’t forget to map architecture violations Slide 47 by richargh.de

    from CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines of Code Arc. Violations Arc. Violations (high)
  45. Characterization tests capture a snapshot of the system Slide 49

    by richargh.de from There are 2½ more testing types that we won‘t get into here. 1 see also „Golden Master“ https://en.wikipedia.org/wiki/Characterization_test 2 see approval test framework https://approvaltests.com 3 see verify framework https://github.com/VerifyTests/Verify/ Oracle-based testing (aka unit/int. tests) Characterization testing1,2,3 Captures intended behavior Captures observed behavior Specific input assert actual matches expected One Test (often) Generated inputs assert actual matches snapshot Many Tests „Classic“ testing Legacy-focused testing
  46. Active Pattern: Inverse Object Mother Context • Learn the minimal

    domain and characterize it as test. Approach 1. Start application with empty database 2. Click through a UseCase 3. Analyse exceptions and errors • „App needs at least an object A with this field“ 4. Expand Domain Modell with your finding 5. Create required state in the DB with your model 6. Document finding as characterization test 7. Repeat Slide 50 by richargh.de from
  47. Active Pattern: Inverse Object Mother Approach 1. Start application with

    empty database 2. Click through a UseCase 3. Analyse exceptions and errors • „App needs at least an object A with this field“ 4. Expand Domain Modell with your finding 5. Create required state in the DB with your model 6. Document finding as characterization test 7. Repeat Slide 51 by richargh.de from 1. // Required state, temporarily in main 2. // we’ll move this to test soon 3. void main() { 4. oneCharacterization(); 5. } 6. // characterizations have no concept of why 7. void displaysListOfBooksOnStart(){ 8. // needs a user 9. createUser(); 10. // needs at least one author 11. var author = createAuthor(); 12. // needs at least one book 13. var book = createBook(author); 14.}
  48. Active Pattern: Outside-in Tests via Dsl Context • Keep tests

    structure- insensitive when you don’t know what your future structure will look like • Be able to convert integration tests to unit tests after remodelling Approach • Use an abstraction for the test setup. Don‘t let tests directly … • create entities • put entities into db • Stub out external systems • Write tests outside-in Slide 52 by richargh.de from See also Java Aktuell 4/24, 5/24 and 6/24
  49. Active Pattern: Outside-in Tests via Dsl Start with an integration

    test Slide 53 by richargh.de from See also Java Aktuell 4/24, 5/24 and 6/24 1. // create the low-level test-DSL 2. // small test, all secondary ports are now stubs or fakes, they never connect to the real world 3. const { a, secondaryPorts } = integrationTest().withoutState().buildDsl(); 4. 5. test(‘should be able to rent book’, () => { 6. // GIVEN 7. const book = a.book(); // I need a book, don’t care which 8. const { user } = a.userBundle(it => it.hasPermission(“CAN_RENT_BOOK”); // a user, don’t care who 9. 10. await a.saveTo(secondaryPorts); // store book and user entities in repositories 11. 12. const testee = configureRentingComponent(floor); // configure dependencies of component 13. // WHEN 14. const result = testee.rentBook(book, user); 15. // THEN 16. expect(result.isRented).toBeTrue(); 17.} <module>/renting.integration.test.ts
  50. Active Pattern: Outside-in Tests via Dsl Go unit with min.

    change, once all db logic is in domain Slide 54 by richargh.de from See also Java Aktuell 4/24, 5/24 and 6/24 1. // create the low-level test-DSL 2. // small test, all secondary ports are now stubs or fakes, they never connect to the real world 3. const { a, secondaryPorts } = unitTest().withoutState().buildDsl(); 4. 5. test(‘should be able to rent book’, () => { 6. // GIVEN 7. const book = a.book(); // I need a book, don’t care which 8. const { user } = a.userBundle(it => it.hasPermission(“CAN_RENT_BOOK”); // a user, don’t care who 9. 10. await a.saveTo(secondaryPorts); // store book and user entities in repositories 11. 12. const testee = configureRentingComponent(floor); // configure dependencies of component 13. // WHEN 14. const result = testee.rentBook(book, user); 15. // THEN 16. expect(result.isRented).toBeTrue(); 17.} <module>/renting.unit.test.ts
  51. Communication Pattern: Quality Views You’ll get better over time Slide

    55 by richargh.de from Increasing changeability Better Worse Patron Book Inventory Component Capability Locked Focus search quote book search
  52. Why all the effort to re-discover? We could just start

    a-new! Slide 56 by richargh.de from
  53. The True Cost of Feature-based Rewrites Slide 57 by richargh.de

    from Original Article by Doug Bradbury The True Cost of Rewrites Features Releases over time Catch Up Missing Features Sub-Par Parity Enhancements Planned Rewrite Actual Features Old App Adoption
  54. In case you want help with the metrics Slide 59

    by richargh.de from CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Target complexity
  55. richargh.de Slide 61 by richargh.de from Thank you @arghrich Richard

    Gross (he/him) IT Archaeology + Health Checks Hypermedia TestDSLs Works for maibornwolff.de/ Slides, Code, Videos Contact me <firstname>.<lastname> at maibornwolff de Contact Ask me about these topics ☺ richargh.de/ rigross Code Quality Insights
  56. The cost of the rewrite depends on your approach Feature-based

    rewrite • Goal = Feature + Feature + Feature • Incrementally build feature after feature • Release when all are done Outcome-based rewrite • Goal = achieve an outcome • Write the minimal thing to achieve outcome • Iterate 64
  57. Generate and view a CodeCharta map 65 npm install -g

    codecharta-analysis git clone [email protected]:MaibornWolff/codecharta.git ccsh sonarimport https://sonarcloud.io -o petclinic.code.cc.json ccsh gitlogparser repo-scan --repo-path=spring-petclinic/ -o petclinic.git.cc.json ccsh merge petclinic.git.cc.json.gz petclinic.code.cc.json.gz -o petclinic.cc.json → Open petclinic.cc.json.gz in https://maibornwolff.github.io/codecharta/visualization/app/index.html The official docs: https://maibornwolff.github.io/codecharta
  58. Passive Pattern: Activity Logging Context • Know which code parts

    are reached often and potentially critical • Know which code parts are not reached at all and are potentially obsolete Approach • Identify system entry points & deep interna, then log there • Alt: Prometheus Counter • Count in production Caveat • Some things are cyclical yearly/monthly (reports) 68
  59. Active Pattern: Legacy Toggle Context • Know if a feature

    really is obsolete and deletable Approach • Add a UI toggle, count if activated (soft) • Deactivate in backend via env variable, reactivate env if someone complains (hard) • Increasing Thread.sleep before answer (evil) • Return static result, see if someone complains (rockstar) Caveat • Some things are cyclical (reports) • People still might not complain 69
  60. Our highest priority is to satisfy the customer by not

    changing what doesn’t need changing. 70 The second principle of the legacy software manifesto (if one is ever written).
  61. Passive Pattern: Cluster Invest 72 Lines of Code N/A N/A

    Visualization by CodeCharta https://maibornwolff.github.io/codecharta/ codemap ribbonbar searchPanel store datamocks.ts customConfigs Do the cluster names and sizes match domain expectations?
  62. Pattern: Complexity1 Invest Context • Cyclomatic complexity1 counts places where

    the control flow branches (if, for, catch, …). • A lot of complexity is an indicator that domain decisions are being made. Approach 1. In the code-map mark the places with a lot of complexity Caveat • Cyclomatic complexity penalizes switch cases heavily and ignores indendation2,3 73 1 McCabe‘s cyclomatic complexity (MCC) counts branches in control flow (if, for, while, catch) 2 Alternative: Cognitive Complexity https://www.sonarsource.com/resources/cognitive-complexity/ 3 Alternative: Indendation based „Bumby Road“ smell https://codescene.com/engineering-blog/bumpy-road-code-complexity-in-context/
  63. Pattern: Complexity Invest 74 Lines of Code Cycl. Complexity Cycl.

    Complexity (high) CodeCharta Code visualized by CodeCharta https://maibornwolff.github.io/codecharta/ codemap ribbonbar searchPanel store datamocks.ts loadInitialFile.service.ts „Interesting, why is that so complex“? nodeDecorator.ts „Node from the tag cloud, what does it do?“ viewCube.component.ts „Complex but does not show up in tag cloud, why?“ customConfigs
  64. Active Pattern: Complexity Limit • Remove indentation with guard clauses

    • switch(anEnum) { case “A”: doThingA() } → polymorphic dispatch anABCobj.doThing(); • Replace flag argument1 with specific methods • Separate presentation from domain from data2 • Finally group things that only interact with each other and extract as new type • You now have new domain concepts to name 75 1 Flag arguments https://martinfowler.com/bliki/FlagArgument.html 2 Presentation Domain Data Layering https://martinfowler.com/bliki/PresentationDomainDataLayering.html
  65. Passive Pattern: Multi-Level Dependency Graph Context • Imports between elements

    mean coupling • Code Coupling is (roughly) domain coupling • Any circle (tangle) creates knots in our brain Approach 1. Graph the import statements between elements. Stable elements (with no dependencies) at the bottom. 2. Mark arrows that go “up” in red, they create tangles1. 3. View graph, first on high-level, then focus on subsets. 76 1 https://structure101.com/help/java/studio/Content/xs/tangle.html
  66. Passive Pattern: Multi-Level Dependency Graph 77 CodeCharta Code visualized by

    Dependency Cruiser https://github.com/sverweij/dependency-cruiser Alternative: Structure101 and tangles: https://structure101.com/help/cpa/studio6/Content/restructure101/tangles.html npx depcruise app/codeCharta/ --output-type dot | dot –T svg Tangle model → util → state → model
  67. Passive Pattern: Knowledge Silos Context • Code elements that are

    only changed by few authors are likely only understood by these authors. • If the elements are complex and only have one author, we have a business risk as well. Approach 1. In the code-map, mark complex elements that have only 1 or 2 authors. 2. Hightlight elements where the author is about to leave or has left. 78 See also https://codescene.com/knowledge-distribution
  68. Passive Pattern: Knowledge Silos 79 Lines of Code metricColorRangeDiagram.component.ts Medium

    code, medium complex, but only one person knows about it Cog. Complexity Number of authors (low) CodeCharta Code visualized by CodeCharta https://maibornwolff.github.io/codecharta/
  69. Active Pattern: Knowledge Sharing Context • Mitigate the business risk

    of knowledge silos Caveat • Having everyone know everything is time-consuming and wasteful due to forgetfulness Approaches • Simple code • Specification by (test) example • “Owner” delegates changes and reviews • Pair/Mob programming • Dev Talk Walkthrough 80 See also https://codescene.com/knowledge-distribution
  70. Passive Pattern: Coordination Bottlenecks Context • Code elements that everyone

    changes usually require extensive coordination to avoid conflicts. Approach 1. In the code-map, mark complex elements where most of the team have made recent changes. 81
  71. Passive Pattern: Coordination Bottlenecks 82 Lines of Code datamocks.ts Lot‘s

    of code, but no decisions. Probably fine. Cycl. Complexity Number of authors (high) CodeCharta Code visualized by CodeCharta https://maibornwolff.github.io/codecharta/ codeMap.mouseEvent.service.ts Lot‘s of code, many decisions and 20 authors. Why?
  72. Communication Pattern: Quality Views Parking-Lot View 83 Increasing changeability Better

    Worse Pricing Component Capability Locked Focus 08 2024 +- 25% RG Component Champion 23% {10}+[25]+25% Component Name {Scoped} + [Unscoped] Transformations Completion of Scoped Transformation Expected Completion Month + Uncertainity%
  73. How do we refactor what we don‘t understand? 91 Slides

    by @arghrich Autumn: Product Baseline
  74. 92 Slides by @arghrich Autumn: Product Baseline Emily Bache @emilybache

    „Nopefactoring“ The No-thinking refactoring“ Advanced Testing & Refactoring Techniques • Lift-up conditional • Split to Classes Llewellyn Falco @LlewellynFalco Cutting Code Quickly
  75. A brief coupling primer High Coupling Low Cohesion Low Coupling

    High Cohesion 93 Coupling Unknown Source
  76. Connascence Guides Refactoring • Name: variable, method, SQL Table •

    Type: int, String, Money, Person • Meaning: what is true,‘YES‘,null,love • Position: order of value • Algorithm: encoding, SPA vs Server • Execution (order): one before other • Timing: doFoo() in 500ms | doBar() in 400ms • Value: constraints on value, invariants • Identity: reference same entity Easy Hard on your brain Good Bad Really Bad Refactor this way Connascence: https://www.maibornwolff.de/know-how/connascence-regeln-fuer-gutes-software-design/
  77. 4-axes of Connascence Strength Level How explicit Locality How close

    Degree Number of Impacts Volatiliy How much change
  78. The 4½ types of testing 96 Oracle-based testing Property-based testing1

    Characterization testing2,3 Metamorphic testing4 Captures intended behavior Captures observed behavior Captures intended properties Random inputs assert one property of all outputs Captures intended metamorphic relations One Test Specific input assert actual matches expected One Test (often) Generated inputs assert actual matches snapshot Many Tests One source, Derived inputs assert outputs keep relation to source One Test The remaining ½ is the mutation which you can use to test your tests. Mutate code, run test, see if enough tests break. 1 see jqwik https://jqwik.net/ 2 see also „Golden Master“ https://en.wikipedia.org/wiki/Characterization_test 3 Alternative name, „Approval Tests“ including test framework https://approvaltests.com 4 see https://www.hillelwayne.com/post/metamorphic-testing/
  79. Metamorphic testing 97 Input Output testee x f(x) g(x) g(f(x))

    testee Derive via metamorphic relation Assert Check if relation holds