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

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

Avatar for Richard Richard
September 15, 2025

Domain Re-discovery Patterns for Legacy Code (v3.1) 🇬🇧 @JavaForumNord 2025

Legacy code projects struggle before coding even begins. Which features are implemented, where they are located, and at what maturity level, is often unclear. In short, a gap exists between the business domain and what is implemented in code.

In green-field projects we use Domain-Driven Design tools and patterns, so the gap does not happen. An additional set of patterns is needed when we start out with legacy code though.

In this talk we'll explore the core patterns to rediscover the domain. These patterns go beyond merely deciphering the code's functionality. They provide strategies to comprehend the underlying concepts, behaviors, and relationships in the domain.

Avatar for Richard

Richard

September 15, 2025
Tweet

More Decks by Richard

Other Decks in Programming

Transcript

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

    richargh.de Hypermedia TestDSLs Domain Re-discovery Patterns for Legacy Code
  2. Some domains are not very evident Slide 3 CC BY-SA

    richargh.de Generated with: https://github.com/Richargh/code-tagcloud-py-sandbox Stringly or Strongly Typed? Pattern: Code Tag Cloud
  3. Some code is not very evident ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Slide 4 CC BY-SA richargh.de
  4. Fear of change Code not testable Innovation jam Tests become

    blockers Change here breaks there Lack of domain „When will it be done“-Pressure Slide 5 CC BY-SA richargh.de
  5. Where do you even start? 999+ Bugs Issues 999+ Smells

    999+ Slide 6 CC BY-SA richargh.de
  6. Map your Code with CodeCharta1 buildings 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 7 CC BY-SA richargh.de
  7. Map your Code. Master your Legacy. CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines

    of Code Cycl. Complexity Churn (high) Pattern: Map your Code ModuleService Lot‘s of code. Many decisions. Lot‘s of change. And, intention-hiding name Slide 8 CC BY-SA richargh.de Where is it used?
  8. Start at the Edge ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Pattern Repos Controllers 3rd Party Services Intentions tend to be more obvious here ModuleService DataOpsController Slide 9 CC BY-SA richargh.de
  9. Conditionals guide the way 1. class DataOpsController { 2. 3.

    void handle(req, res){ 4. // … more code here 5. switch(req.action): 6. case ‘RNT’: 7. // … a lot of code here, then 8. res.send(b); 9. case ‘RTRN’: 10. // … a lot more code here 11. res.send(b); 12. case ‘CRT’: 13. // … something completely different 14. res.send(b); 15. case ‘EMT’: 16. // wait what?! 17. messaging().multicast(m); 18. } Pattern: Start at the Edge Slide 10 CC BY-SA richargh.de
  10. Extracting all that code into new classes clears things up

    a bit “AppleSauce” idea straight from https://www.digdeeproots.com/articles/on/naming-process/ 1. class DataOpsController { 2. 3. void handle(req, res){ 4. // … more code here 5. switch(req.action): 6. case ‘RNT’: 7. const appleSauce1 = AppleSauce1.handle(req, res) 8. res.send(appleSauce1); 9. case ‘RTRN’: 10. const appleSauce2 = AppleSauce2.handle(req, res) 11. res.send(appleSauce2); 12. case ‘CRT’: 13. const createdBookDto = CreateBook.handle(req, res) 14. res.send(createdBookDto); 15. case ‘EMT’: 16. const broadcastMessageDto = CreateBroadcast.handle(req) 17. messaging().multicast(broadcastMessageDto) 18. } Pattern: Start at the Edge Obvious nonsense names are ok But obvious things should be named Slide 11 CC BY-SA richargh.de
  11. Recognize the common abstractions 1. class AppleSauce1 { 2. //

    … 3. void handle(req, res){ 4. const isbn = http.get(“is.bn?name=${req.body.name}”) 5. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = db.userTable.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Slide 12 CC BY-SA richargh.de
  12. Recognize the common abstractions 1. class AppleSauce1 { 2. //

    … 3. void handle(req, res){ 4. const isbn = http.get(“is.bn?name=${req.body.name}”) 5. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = db.userTable.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Infrastructure Slide 13 CC BY-SA richargh.de
  13. Recognize the common abstractions 1. class AppleSauce1 { 2. //

    … 3. void handle(req, res){ 4. const isbn = http.get(“is.bn?name=${req.body.name}”) 5. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = db.userTable.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Domain Infrastructure Slide 14 CC BY-SA richargh.de
  14. Recognize the common abstractions 1. class AppleSauce1 { 2. //

    … 3. void handle(req, res){ 4. const isbn = http.get(“is.bn?name=${req.body.name}”) 5. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = db.userTable.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Presentation Domain Infrastructure Slide 15 CC BY-SA richargh.de
  15. Recognize the common abstractions 1. class AppleSauce1 { 2. //

    … 3. void handle(req, res){ 4. const isbn = http.get(“is.bn?name=${req.body.name}”) 5. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = db.userTable.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Presentation Domain Infrastructure Application Slide 16 CC BY-SA richargh.de
  16. Realize the benefit of early guards… 1. class AppleSauce1 {

    2. // … 3. void handle(req, res){ 4. const isbn = http.get(“is.bn?name=${req.body.name}”) 5. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = db.userTable.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Presentation Domain Infrastructure Application Slide 17 CC BY-SA richargh.de
  17. But don’t just reorder without tests 1. class AppleSauce1 {

    2. // … 3. void handle(req, res){ 4. const rentedUntil = Instant.of(req.body.until) 5. 6. const user = db.userTable.getById(req.token.id) 7. if(!user.permits.contains(‘A38’)) 8. res.send(403) 9. 10. const isbn = http.get(“is.bn?name=${req.body.name}”) 11. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 12. if(b.status == ‘RENTED’) 13. throw new BookIsAlreadyRentedException(b.id) 14. 15. const rentedBook = b.copyWith({ 16. status: ‘RENTED’, 17. rentedUntil: rentedUntil 18. }) 19. 20. db.bookTable.save(rentedBook) 21. } Changing the execution order of stuff is a good way to get bugs Presentation Domain Infrastructure Application Slide 18 CC BY-SA richargh.de
  18. Do realize what the infrastructure is about 1. class AppleSauce1

    { 2. // … 3. void handle(req, res){ 4. const isbn = http.get(“is.bn?name=${req.body.name}”) 5. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = db.userTable.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Presentation Domain Infrastructure Application Slide 19 CC BY-SA richargh.de
  19. Then make it about the domain: without tests but with

    your IDE 1. class AppleSauce1 { 2. // … 3. void handle(req, res){ 4. const isbn = isbnClient.findByName(req.body.name) 5. const b = books.getBy(isbn) 6. if(b.status == ‘RENTED’) 7. res.send(404) 8. 9. const rentedBook = b.copyWith({ 10. status: ‘RENTED’, 11. rentedUntil: Instant.of(req.body.until) 12. }) 13. 14. const user = users.getById(req.token.id) 15. if(!user.permits.contains(‘A38’)) 16. res.send(403) 17. 18. books.put(rentedBook) 19. 20. // … a lot more code here 21. } Presentation Domain Infrastructure Application Slide 20 CC BY-SA richargh.de Pattern: Strengthen Domain with Ports This menu is your best friend
  20. Strengthen domain with ports • Ports define what the domain

    needs from the outside world • They decouple from the actual implementation <<interface>> IsbnClient findByName(:String): Isbn <<interface>> Books getBy(:Isbn): Book? Put(:Book) Pattern: Strengthen Domain with Ports Slide 21 CC BY-SA richargh.de
  21. Make the class testable 1. class AppleSauce1 { 2. 3.

    AppleSauce1( 4. IsbnClient isbnClient, 5. Users users, 6. Books books){ 7. // set the parameters 8. } 9. 10. void applesauce1(req, res){ 11. const isbn = isbnClient.findByName(req.body.name) 12. const b = books.getBy(isbn) 13. // [...] 14. const user = users.getById(req.token.id) 15. // [...] 16. books.put(rentedBook) 17. 18. // … a lot more code here 19. } Pattern: Configurable dependencies We can now configure these in our tests Slide 22 CC BY-SA richargh.de
  22. We can split but not reorder without tests DataOpsController Presentation

    Domain Infrastructure Application delegates to AppleSauce1 DataOpsController Extract Actions Introduce Ports delegates to AppleSauce1 DataOpsController delegates to AppleSauce1 DataOpsController Configurable Ports Slide 23 CC BY-SA richargh.de
  23. Characterize your classes Given enough inputs We will cover every

    line of our testee See also https://approvaltests.com/ “AppleSauce” idea straight from https://www.digdeeproots.com/articles/on/naming-process/ AppleSauce1.Characterization.txt 1. class AppleSauce1 { 2. void handle(req, res){ 3. ~~~ 4. ~~~ 5. if(~~~) 6. ~~~ 7. 8. ~~~ 9. ~~~ 10. if(~~~) 11. ~~~ 12. 13. ~~~ 14.} 1. [foo, 42, true] => None, 2024-01-01 2. [bar, 12, false] => Almost, 2099-01-01 3. [bla, 0, null] => Finally, null Line covered Pattern Slide 25 CC BY-SA richargh.de
  24. Characterization tests capture a snapshot of the system • They

    don’t tell us why it does what it does • They’ll tell us when we refactor • They’ll stop us when we want to change behavior  1 https://martinfowler.com/books/refactoring.html Refactoring1: change structure without changing behavior Slide 26 CC BY-SA richargh.de
  25. Small changes, big knowledge boost 1 uses the checker framework

    https://checkerframework.org/ Ids Units of measure 1. var rawUserId = 123; 2. var userId = UserId.of(123); 3. var bookId = BookId.of(123); 4. 5. // allowed, but is a bug 6. Book book = RawGetBook(rawUserId); 7. 8. // produces a design-time error 9. Book book = GetBook(userId); 10. 11. 12.void RawGetBook(int id){ /* … */ } 13.void GetBook(BookId id){ /* … */ } 1. Meters meters = Meters.Of(5); 2. Seconds seconds = Seconds.Of(2); 3. Money money = Money.Of(5, Currency.Euros); 4. 5. // allowed 6. Speed speed = meters.Per(seconds); 7. 8. // produces a design-time error 9. var foo = meters + seconds; Pattern: Strongly-typed Primitives Slide 28 CC BY-SA richargh.de
  26. Chunk your code into components, outliers where it’s used the

    most “AppleSauce” idea straight from https://www.digdeeproots.com/articles/on/naming-process/ AppleSauce1 DataOpsController XyzController AppleSauce2 CreateBook MagicSauce TruffleSauce core/AppleSauce? abc supporting/Books? generic/PearSauce? def Slide 29 CC BY-SA richargh.de Pattern
  27. Entity Ownership 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 Pattern Slide 30 CC BY-SA richargh.de
  28. Who reads and writes book? Read Scattered writes • Is

    entity contract protected everywhere? • Is entity contract in sync everywhere? (pre/post conditions, invariants) Scattered reads • Does everyone really need the entity? • Does everyone need the same fields? Write Pattern: Entity Ownership Slide 31 CC BY-SA richargh.de
  29. Bound the Entity Ownership 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 32 CC BY-SA richargh.de Pattern: Entity Ownership
  30. Define your understanding as code 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. .that().resideInAPackage("..service..") 6. .should().accessClassesThat().resideInAPackage("..controller..") 7. ); Pattern: North-Star Architecture Slide 33 CC BY-SA richargh.de
  31. Map your frozen architecture violations. CodeCharta visualisation https://maibornwolff.github.io/codecharta/ Lines of

    Code Arc. Violations Arc. Violations (high) Pattern: North-Star Architecture Slide 34 CC BY-SA richargh.de
  32. Characterized behavior Code is testable Domain Clusters 3 „When will

    it be done“-Pressure Slide 35 CC BY-SA richargh.de
  33. Legacy means less predictability for “when it will be done”*

    * When you don’t have legacy code, your predictability mainly depends on how small you make your work items 10% 30% 50% 70% 90% Certainity of estimate Mo Tu We Th Fr 1 2 3 6 7 8 9 10 13 14 15 16 17 20 21 22 23 24 27 28 29 30 1 4 5 6 7 8 When will it be done? Slide 36 CC BY-SA richargh.de
  34. Legacy means less predictability for “when it will be done”*

    * When you don’t have legacy code, your predictability mainly depends on how small you make your work items 10% 30% 50% 70% 90% Certainity of estimate Mo Tu We Th Fr 1 2 3 6 7 8 9 10 13 14 15 16 17 20 21 22 23 24 27 28 29 30 1 4 5 6 7 8 When will it be done? Slide 37 CC BY-SA richargh.de
  35. Colorize based on predictability * Define appropriate criteria as a

    team. Increasing predictability Component ✓ Yes ✗ No Pattern: Quality Views Patron 2/10* Slide 38 CC BY-SA richargh.de ✗ Criteria 1 ✓ … ✗ … ✗ … ✓ … ✗ … ✗ Criteria n
  36. Colorize based on predictability * Define appropriate criteria as a

    team. Enforced component bounds (f.ex. via Arch-Unit tests) ✗ Characterization tests coverage 100% ✓ Functional Unit tests coverage +80% ✗ No obsolete or critically vulnerable depedendencies used ✗ Presentation-Infra-Domain Layering ✗ Component testable ✓ Behavior Structure Pattern: Quality Views Service-Level Agreements See appendix for more ideas…* Slide 39 CC BY-SA richargh.de Contract test coverage +100% ✗
  37. Communicate High-Level-View Quality Views Based-on https://blog.colinbreck.com/using-quality-views-to-communicate-software-quality-and-evolution/ Increasing predictability Better Worse

    Patron Book Inventory Component Locked Transforming for future features Focus Slight quality dip since last communication No changes planned or wanted Pattern: Quality Views Slide 40 CC BY-SA richargh.de
  38. Capability-View Quality Views Based-on https://blog.colinbreck.com/using-quality-views-to-communicate-software-quality-and-evolution/ Increasing predictability Better Worse Patron

    Book Inventory Component Capability Locked Feature-planning often requires capability details Focus search quote book search Pattern: Quality Views Slide 41 CC BY-SA richargh.de
  39. By adressing these Behavior Structure We’ll unearth Domain concepts ✓

    Behavior documentation (tests ☺) ✓ Service-Level Agreements 10% 30% 50% 70% 90% Certainity of estimate And fix this Slide 42 CC BY-SA richargh.de
  40. Characterized behavior Code is testable 4 Change here breaks there

    Domain Clusters Quality Views Slide 43 CC BY-SA richargh.de
  41. Map your Temporal Coupling 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 RentService RentService Notice that no compile-time relation exists between the temporally coupled files. Pattern: Temporal Coupling1 Slide 44 CC BY-SA richargh.de
  42. C is always commited together with … 1 from the

    book https://pragprog.com/titles/atcrime/your-code-as-a-crime-scene B C A Temporal Coupling Static Coupling Both are forms of Connascence Patron Book Dailysheet Inventory D E Pattern: Temporal Coupling1 Slide 45 CC BY-SA richargh.de
  43. 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 46 CC BY-SA richargh.de
  44. Connascence of 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 • And 6 more, including Execution Order Easy Hard on your brain Good Bad Refactor this way Hidden domain Explicit Domain Expensive Change Cheap Slide 47 CC BY-SA richargh.de
  45. Connascence guides refactoring 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 48 CC BY-SA richargh.de
  46. Let Connascence guide the decoupling Strong of Connascence Weak of

    Connascence Reduce Strength Increase Locality New Domain Concept Lock Element Locked Domain Concept Slide 49 CC BY-SA richargh.de
  47. Characterized behavior Code is testable 5 Tests become blockers Decoupled

    some Domain Clusters Quality Views Slide 50 CC BY-SA richargh.de
  48. Tests can cement structure and block progress The redundant initialization

    in n tests cements the design of the type class Book { … } Test 1 new Book(“1”, “Abc”, …) Test n new Book(“n”, “xyz”, …) Slide 51 CC BY-SA richargh.de
  49. 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 See also Java Aktuell 4/24 and https://richargh.de/posts/Structure-Cementing-Tests-1 Pattern Slide 52 CC BY-SA richargh.de
  50. 7. const book = a.book(); // I need a book,

    don’t care which 8. const { user } = a.user(it => it.hasPermission(“CAN_RENT_BOOK”); // a user, don’t care who 9. 10. await a.saveTo(infrastructure); // store book and user entities in repositories Start with an integration test See also Java Aktuell 5/24 and https://richargh.de/posts/Structure-Cementing-Tests-1 1. // create the low-level integration test-DSL 2. // small test, infrastructure ports are now stubs or fakes, they never connect to the real world 3. const { a, infrastructure } = integrationTest().buildDsl(); 4. 5. test(‘should be able to rent book’, () => { 6. // GIVEN <module>/renting.integration.test.ts Pattern: Outside-in Tests via Dsl Slide 53 CC BY-SA richargh.de 11. 12. const testee = configureRentingComponent(infrastructure); // configure dependencies of c. 13. // WHEN 14. const result = testee.rentBook(book, user); 15. // THEN 16. expect(result.isRented).toBeTrue(); 17.}
  51. Go unit with one change, once all db logic is

    in domain See also Java Aktuell 6/24 and https://richargh.de/posts/Structure-Cementing-Tests-1 1. // create the low-level unit test-DSL 2. // small test, infrastructure ports are now stubs or fakes, they never connect to the real world 3. const { a, infrastructure } = unitTest().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.user(it => it.hasPermission(“CAN_RENT_BOOK”); // a user, don’t care who 9. 10. await a.saveTo(infrastructure); // store book and user entities in repositories 11. 12. const testee = configureRentingComponent(infrastructure); // 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 Pattern: Outside-in Tests via Dsl Slide 54 CC BY-SA richargh.de
  52. Characterized behavior Code is testable 6 Innovation jam Outside-in Tests

    via Dsl Decoupled some Domain Clusters Quality Views Slide 55 CC BY-SA richargh.de
  53. When we safely work towards our goal … Presentation Domain

    Infrastructure Application delegates to AppleSauce1 DataOpsController BookController CreateBook RentBook handle handle Configurable Dependencies Configurable Dependencies xyzController DoXyz inform Configurable Dependencies Slide 56 CC BY-SA richargh.de
  54. … and so does predictable innovation* * When you don’t

    have legacy code, your predictability mainly depends on how small your make your work items Increasing predictability Patron Book Inventory Component Capability Locked Focus search quote book search M T W T F 1 2 3 6 7 8 9 10 13 14 15 16 17 20 21 22 23 24 27 28 29 30 1 4 5 6 7 8 M T W T F 1 2 3 6 7 8 9 10 13 14 15 16 17 20 21 22 23 24 27 28 29 30 1 4 5 6 7 8 M T W T F 1 2 3 6 7 8 9 10 13 14 15 16 17 20 21 22 23 24 27 28 29 30 1 4 5 6 7 8 Pattern: Quality Views Slide 59 CC BY-SA richargh.de
  55. In case you are interested in mapping your code Give

    CodeCharta a Slide 60 CC BY-SA richargh.de
  56. Thank you Ask me about these topics ☺ richargh.de Richard

    Gross (he/him) IT Archaeology + Health Checks Hypermedia TestDSLs Works for maibornwolff.de/ richargh.de richargh Slide 61 CC BY-SA richargh.de
  57. We could talk even more about patterns Map Temporal Coupling

    Passive Code Tag Cloud Passive Map Coordination Bottlenecks Passive Package by Component Active Map Knowledge Silos Passive Quality Views Communication Map Churn Passive Inverse Object Mother Active Entity Ownership Passive Outside-in Tests via DSL Active Slide 63 CC BY-SA richargh.de
  58. 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 65 CC BY-SA richargh.de
  59. Passive Pattern: Map Coordination Bottlenecks Slide 66 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?
  60. 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. See also https://codescene.com/knowledge-distribution Slide 67 CC BY-SA richargh.de
  61. Passive Pattern: Map Knowledge Silos 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 68 CC BY-SA richargh.de
  62. 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 69 CC BY-SA richargh.de
  63. How do we refactor what we don‘t understand? Slides by

    @arghrich Slide 70 CC BY-SA richargh.de
  64. Slides by @arghrich 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 CC BY-SA richargh.de
  65. 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 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. // … needs xyz as well 15.} Pattern Slide 72 CC BY-SA richargh.de
  66. Why all the effort to re-discover? We could just start

    a-new! Slide 73 CC BY-SA richargh.de
  67. The True Cost of Feature-based Rewrites 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 74 CC BY-SA richargh.de
  68. 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 Slide 75 CC BY-SA richargh.de
  69. Generate and view a CodeCharta map 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 76 CC BY-SA richargh.de
  70. Communication Pattern: Quality Views Parking-Lot View 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 77 CC BY-SA richargh.de
  71. Invoicing Communication Pattern: Quality Views Even more detail Increasing changeability

    Better Worse Pricing Renting Locked Feature-planning often requires more details Focus Component Capability FE BE search quote book bill liquidate Slide 78 CC BY-SA richargh.de
  72. 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 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 79 CC BY-SA richargh.de
  73. Pattern: Complexity Invest 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 80 CC BY-SA richargh.de
  74. 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 infrastructure2 • Finally group things that only interact with each other and extract as new type • You now have new domain concepts to name 1 Flag arguments https://martinfowler.com/bliki/FlagArgument.html 2 Presentation Domain Data Layering https://martinfowler.com/bliki/PresentationDomainDataLayering.html Slide 81 CC BY-SA richargh.de
  75. Anti-Pattern: Package by Layer1,2 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 Infrastructure Layer It‘s all about the frameworks Models intermixed with unrelated models. Utils are a smell Slide 82 CC BY-SA richargh.de
  76. 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 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 83 CC BY-SA richargh.de
  77. commons Active Pattern: Package by Component1,2 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 84 CC BY-SA richargh.de
  78. The Domain gets top-level modules ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold patron book dailysheet inventory Slide 85 CC BY-SA richargh.de
  79. Slide 86 CC BY-SA richargh.de inventory dailysheet Patron An ordered

    mess emerges ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId Book BookOnHold Book
  80. Communication Pattern: Quality Views Colorize based on changeability 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-Domain-Infra 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 Tests over 20% Tests over 40% Tests over 60% Fitness functions No Arch Violations Slide 87 CC BY-SA richargh.de
  81. 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) Slide 90 CC BY-SA richargh.de
  82. 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 Slide 91 CC BY-SA richargh.de
  83. Our highest priority is to satisfy the customer by not

    changing what doesn’t need changing. The second principle of the legacy software manifesto (if one is ever written). Slide 92 CC BY-SA richargh.de
  84. A brief coupling primer High Coupling Low Cohesion Low Coupling

    High Cohesion Coupling Unknown Source Slide 95 CC BY-SA richargh.de
  85. 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 96 CC BY-SA richargh.de
  86. 4-axes of Connascence Strength Level How explicit Locality How close

    Degree Number of Impacts Volatiliy How much change Slide 97 CC BY-SA richargh.de
  87. The 4½ types of testing 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 98 CC BY-SA richargh.de
  88. Metamorphic testing Input Output testee x f(x) g(x) g(f(x)) testee

    Derive via metamorphic relation Assert Check if relation holds Slide 99 CC BY-SA richargh.de