Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

22.10.24 Richard Gross (he/him) Archaeology + Health Checks richargh.de/ speakerdeck.com/richargh @arghrich Hypermedia TestDSLs Domain Re-discovery Patterns für Legacy Code

Slide 3

Slide 3 text

3 Joining a legacy code project

Slide 4

Slide 4 text

What we find on the stone tablet is ... confusing Slide 4 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

Slide 5

Slide 5 text

Now what? Slide 5 by richargh.de from

Slide 6

Slide 6 text

We’ll work backwards from code Slide 6 by richargh.de from https://fs.blog/2013/07/the-difference-between-science-and-engineering/ IT Archaeology

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

We’ll 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/

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

A lot of helpful rediscovery patterns Slide 10 by richargh.de from Passive Patterns (Analyze) Active Patterns (Change) Where to start What to try

Slide 11

Slide 11 text

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 I might have simplified programming a bit too much.

Slide 12

Slide 12 text

Pattern categories Slide 12 by richargh.de from Capture the Behavior Mine the Expert Mine the Repo Not the focus today

Slide 13

Slide 13 text

Slide 13 by richargh.de from Capture the Behavior Mine the Expert Mine the Repo

Slide 14

Slide 14 text

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/

Slide 15

Slide 15 text

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?

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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);

Slide 18

Slide 18 text

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…

Slide 19

Slide 19 text

The Domain peeks out Slide 19 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

The Domain gets top-level modules Slide 23 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold patron book dailysheet inventory

Slide 24

Slide 24 text

inventory dailysheet Patron An ordered mess emerges Slide 24 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold Book

Slide 25

Slide 25 text

When will it be done? About now is when people get nervous Slide 25 by richargh.de from

Slide 26

Slide 26 text

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/

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

richargh.de Slide 32 by richargh.de from Where could we even start? If we’re lucky we have a long list of smells: • Smell 1 in file x • Smell 2 in file y • Smell 3 in file z • Smell … • Smell … • Smell … • Smell … • Smell … • Smell … • Smell … • Smell … • Smell … • Smell … • Smell 9001

Slide 33

Slide 33 text

Passive Pattern: Map the complexity See the important trees in the forrest Slide 33 by richargh.de from CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines of Code Cycl. Complexity Code Coverage (high) ModuleService.kt Lot‘s of code, many decisions, little coverage, generic name.

Slide 34

Slide 34 text

We could stop here but... …the additional patterns will show us where to look Slide 34 by richargh.de from

Slide 35

Slide 35 text

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 35 by richargh.de from 1 from the book https://pragprog.com/titles/atcrime/your-code-as-a-crime-scene

Slide 36

Slide 36 text

Passive Pattern: Temporal Coupling1 Slide 36 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.kt Notice that no compile-time relation exists between the temporally coupled files.

Slide 37

Slide 37 text

You’ve probably been a victim of Connascence Slide 37 by richargh.de from B C A Temporal Coupling Static Coupling Both are forms of Connascence Patron Book Dailysheet Inventory D E

Slide 38

Slide 38 text

Slide 38 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.

Slide 39

Slide 39 text

Connascence of Slide 39 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

Slide 40

Slide 40 text

Connascence describes the type of coupling Slide 40 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

Slide 41

Slide 41 text

Connascence guides refactoring Slide 41 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

Slide 42

Slide 42 text

Active Pattern: Temporal decoupling Slide 42 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

Slide 43

Slide 43 text

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 43 by richargh.de from

Slide 44

Slide 44 text

Passive Pattern: Entity Ownership Who reads and writes book? Slide 44 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

Slide 45

Slide 45 text

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 45 by richargh.de from

Slide 46

Slide 46 text

inventory dailysheet Patron New concepts emerge, some move, some merge Slide 46 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold Book BookType Daily Copy

Slide 47

Slide 47 text

Our domain models can also give feedback to the domain Slide 47 by richargh.de from

Slide 48

Slide 48 text

Passive Pattern: Map 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. Slide 48 by richargh.de from

Slide 49

Slide 49 text

Passive Pattern: Map Coordination Bottlenecks Slide 49 by richargh.de from CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines of Code DataMocks.kt Lot‘s of code, but no decisions. Probably fine. Cycl. Complexity Number of authors (high) RentingService.kt Lot‘s of code, many decisions and 20 authors. Why?

Slide 50

Slide 50 text

Passive Pattern: Map 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. Slide 50 by richargh.de from See also https://codescene.com/knowledge-distribution

Slide 51

Slide 51 text

Passive Pattern: Map Knowledge Silos Slide 51 by richargh.de from CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines of Code Librarian.kt Medium code, medium complex, but only one person knows about it Cog. Complexity Number of authors (low)

Slide 52

Slide 52 text

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 Approach • “Owner” delegates changes and reviews • Pair/Mob programming • Dev Talk Walkthrough • Simple code • Specification by (test) example Slide 52 by richargh.de from

Slide 53

Slide 53 text

Slide 53 by richargh.de from Capture the Behavior Mine the Expert Mine the Repo

Slide 54

Slide 54 text

Characterization tests capture a snapshot of the system Slide 54 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

Slide 55

Slide 55 text

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 55 by richargh.de from

Slide 56

Slide 56 text

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 56 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.}

Slide 57

Slide 57 text

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 57 by richargh.de from See also Java Aktuell 4/24, 5/24 and 6/24

Slide 58

Slide 58 text

Active Pattern: Outside-in Tests via Dsl Start with an integration test Slide 58 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.} /renting.integration.test.ts

Slide 59

Slide 59 text

Active Pattern: Outside-in Tests via Dsl Go unit with min. change, once all db logic is in domain Slide 59 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.} /renting.unit.test.ts

Slide 60

Slide 60 text

Communication Pattern: Quality Views You’ll get better over time Slide 60 by richargh.de from Increasing changeability Better Worse Patron Book Inventory Component Capability Locked Focus search quote book search

Slide 61

Slide 61 text

Patterns Slide 61 by richargh.de from Code Tag Cloud Passive Map Temporal Coupling Passive Package by Component Active Map Coordination Bottlenecks Passive Quality Views Communication Map Knowledge Silos Passive Entity Ownership Passive Inverse Object Mother Active Map Complexity Passive Outside-in Tests via DSL Active In case you want some help here…

Slide 62

Slide 62 text

richargh.de Slide 62 by richargh.de from Code Quality Insights Scan to get your insights

Slide 63

Slide 63 text

richargh.de Slide 63 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 . at maibornwolff de Contact Ask me about these topics ☺ richargh.de/ rigross Code Quality Insights

Slide 64

Slide 64 text

Backup

Slide 65

Slide 65 text

Why all the effort to re-discover? We could just start a-new! Slide 66 by richargh.de from

Slide 66

Slide 66 text

The True Cost of Feature-based Rewrites Slide 67 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

Slide 67

Slide 67 text

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 68

Slide 68

Slide 68 text

Generate and view a CodeCharta map 69 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

Slide 69

Slide 69 text

How do we refactor what we don‘t understand? 70 Slides by @arghrich Autumn: Product Baseline

Slide 70

Slide 70 text

71 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

Slide 71

Slide 71 text

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 72 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

Slide 72

Slide 72 text

Active Pattern: North-Star Architecture Slide 73 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..");

Slide 73

Slide 73 text

inventory dailysheet Patron We can track architecture violations Slide 74 by richargh.de from ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold Book BookType Daily Copy

Slide 74

Slide 74 text

We can map architecture violations Slide 75 by richargh.de from CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines of Code Arc. Violations Arc. Violations (high)

Slide 75

Slide 75 text

The Dirty (but useful) Patterns 76

Slide 76

Slide 76 text

Some Modernizations required dirtier patterns than others 77

Slide 77

Slide 77 text

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) 78

Slide 78

Slide 78 text

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 79

Slide 79

Slide 79 text

Our highest priority is to satisfy the customer by not changing what doesn’t need changing. 80 The second principle of the legacy software manifesto (if one is ever written).

Slide 80

Slide 80 text

Passive Pattern: Cluster Invest 82 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?

Slide 81

Slide 81 text

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 83 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/

Slide 82

Slide 82 text

Pattern: Complexity Invest 84 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

Slide 83

Slide 83 text

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 85 1 Flag arguments https://martinfowler.com/bliki/FlagArgument.html 2 Presentation Domain Data Layering https://martinfowler.com/bliki/PresentationDomainDataLayering.html

Slide 84

Slide 84 text

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. 86 1 https://structure101.com/help/java/studio/Content/xs/tangle.html

Slide 85

Slide 85 text

Passive Pattern: Multi-Level Dependency Graph 87 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

Slide 86

Slide 86 text

Communication Pattern: Quality Views Parking-Lot View 88 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%

Slide 87

Slide 87 text

Invoicing Communication Pattern: Quality Views Even more detail 89 Increasing changeability Better Worse Pricing Renting Locked Feature-planning often requires more details Focus Component Capability FE BE search quote book bill liquidate

Slide 88

Slide 88 text

A brief coupling primer High Coupling Low Cohesion Low Coupling High Cohesion 91 Coupling Unknown Source

Slide 89

Slide 89 text

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/

Slide 90

Slide 90 text

4-axes of Connascence Strength Level How explicit Locality How close Degree Number of Impacts Volatiliy How much change

Slide 91

Slide 91 text

The 4½ types of testing 94 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/

Slide 92

Slide 92 text

Metamorphic testing 95 Input Output testee x f(x) g(x) g(f(x)) testee Derive via metamorphic relation Assert Check if relation holds