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

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

Domain Re-discovery Patterns for Legacy Code (v3.4) 🇬🇧 @Techcamp Hamburg 2026

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

August 26, 2026

More Decks by Richard

Other Decks in Programming

Transcript

  1. Domain Re-discovery Patterns for Legacy Code Richard Gross (he/him) Head

    of Software Archaeology richargh.de richargh.de 26.08.26 Software Health Checks Modernisation Hypermedia richargh
  2. Pattern: Code Tag Cloud Some domains are not very evident

    Stringly or Strongly Typed? Slide 3 CC BY-SA richargh.de Generated with: https://github.com/Richargh/code-tagcloud-py-sandbox
  3. „When will it be done“-Pressure Lack of domain Fear of

    change Code not testable Slide 5 CC BY-SA richargh.de Change here breaks there Tests become blockers Innovation jam
  4. Where do you even start? 999+ Bugs Slide 6 CC

    BY-SA richargh.de 999+ Security Issues 999+ Smells
  5. Ask the LLMTM Gobble up stuff Get a report Subagent

    Explore() 1. Grep(TODO|FIXME|HACK|XXX) 2. Grep(password|secret|token|api_key|apikey) 3. Grep(eval|exec|dangerouslySetInnerHTML) 4. Grep(SELECT.*FROM.*WHERE) 5. Grep(componentWillMount|componentWillReceivePro ps) 6. Grep(race condition|deadlock|mutex|RwMutex) 7. Grep(CVE|vuln) 8. Grep(console.log|error) 9. Bash(npm audit) 10. Bash(npm run test) 11. LineCount(*.ts) 12. LineCount(*.spec.ts) 13. Read(all package.json) 14. Read(all *.md) 15. Read(jest|webpack.config.*) 16. Read(largest files) ## Critical issues 1. MASSIVE TECHNICAL DEBT FROM TODO/FIXME MARKERS 2. SQL INJECTION VULNERABILITIES RISK 3. EXCESSIVE USE OF DANGEROUS REACT PATTERNS 4. DEPRECATED REACT LIFECYCLE METHODS 5. MASSIVE COMPONENT SIZE AND COMPLEXITY 6. INCOMPLETE TEST COVERAGE Slide 8 CC BY-SA richargh.de ## RECOMMENDATIONS ### Immediate (0-30 days) 1. Security Audit SQL Queries 2. Remove console.log statements 3. Patch bootstrap 4. Audit all dangerouslySetInnerHtml * Reads typically use Haiku to figure out if the content is safe. ** Ctrl+O in Claude and verified via mitmproxy
  6. Reliable? /clear /clear 1st try 2nd try 3rd try ##

    Critical issues 1. MASSIVE TECHNICAL DEBT FROM TODO/FIXME MARKERS 2. SQL INJECTION VULNERABILITIES RISK 3. EXCESSIVE USE OF DANGEROUS REACT PATTERNS 4. DEPRECATED REACT LIFECYCLE METHODS 5. MASSIVE COMPONENT SIZE AND COMPLEXITY 6. INCOMPLETE TEST COVERAGE 🚨 CRITICAL - Fix Immediately [1-2 weeks] 1. Hardcoded Sentry DSN Exposed 2. SQL Injection Vulnerabilities 3. Weak Cryptography (SHA-1, MD5) 🔴 CRITICAL - Fix Immediately 1. XSS Vulnerability Risk 2. Insecure Cryptographic Hash (SHA1) 3. Massive File Sizes ⚠ HIGH - Address Soon (1-2 months) 1. Monolithic App Layer (130K+ lines) 2. Technical Debt Backlog (TODO/FIXME) 3. Type Safety Issues (2,257 instances 🟠 HIGH - Fix Within Sprint 1. localStorage Security Issues 2. 1,606 TODO/FIXME Comments 3. Excessive any Type Usage ## RECOMMENDATIONS ### Immediate (0-30 days) 1. Security Audit SQL Queries 2. Remove console.log statements 3. Patch bootstrap 4. Audit all dangerouslySetInnerHtml Total effort estimate: 715-1,080 hours Logging not mentioned Patching not mentioned 📋 Recommended Priority Order Week 1-2 (Security Sprint): 1. Fix XSS vulnerabilities 2. Replace SHA-1 with SHA-256 3. Implement CSP headers 4. Secure localStorage or migrate to HttpOnly cookies SQL not mentioned Slide 9 CC BY-SA richargh.de * Same prompt three times, /clear after each result
  7. Map your Code with CodeCharta1 buildings Lines of Code f.ex.

    Complexity f.ex. Number of authors SomeService.kt Slide 10 CC BY-SA richargh.de 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/
  8. Pattern: Map your Code Map your Code. Master your Legacy.

    ModuleService Lot‘s of code. Many decisions. Lot‘s of change. And, intentionhiding name Where is it used? Lines of Code Slide 11 CC BY-SA richargh.de Cycl. Complexity Churn (high) CodeCharta visualisation https://maibornwolff.github.io/codecharta/
  9. Pattern: Start at the Edge 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. } Slide 13 CC BY-SA richargh.de
  10. Pattern: Start at the Edge Extracting all that code into

    new classes clears things up a bit 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. } Slide 14 CC BY-SA richargh.de Obvious nonsense names are ok But obvious domain should be named “AppleSauce” idea straight from https://www.digdeeproots.com/articles/on/naming-process/
  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 15 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. } Slide 16 CC BY-SA richargh.de Infrastructure
  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 Slide 17 CC BY-SA richargh.de Infrastructure
  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 Slide 18 CC BY-SA richargh.de Infrastructure
  15. Recognize the common layers 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 Application Domain Slide 19 CC BY-SA richargh.de Infrastructure
  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 Application Domain Slide 20 CC BY-SA richargh.de Infrastructure
  17. But don’t just reorder without tests 1. class AppleSauce1 {

    2. // … 3. void handle(req, res){ 4. const user = db.userTable.getById(req.token.id) 5. if(!user.permits.contains(‘A38’)) 6. res.send(403) 7. 8. const isbn = http.get(“is.bn?name=${req.body.name}”) 9. const b = db(“SELECT * FROM Book WHERE isbn = $isbn”) 10. if(b.status == ‘RENTED’) 11. throw new BookIsAlreadyRentedException(b.id) 12. 13. const rentedBook = b.copyWith({ 14. status: ‘RENTED’, 15. rentedUntil: rentedUntil 16. }) 17. 18. db.bookTable.save(rentedBook) 19. 20. // … a lot more code here 21. } Changing the execution order of stuff is a good way to get bugs Presentation Application Domain Slide 21 CC BY-SA richargh.de Infrastructure
  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 Application Domain Slide 22 CC BY-SA richargh.de Infrastructure
  19. Pattern: Strengthen Domain with Ports 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. } This menu is your best friend Presentation Application Domain Slide 23 CC BY-SA richargh.de Infrastructure
  20. Pattern: Strengthen Domain with Ports 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) Slide 24 CC BY-SA richargh.de
  21. Pattern: Configurable dependencies Make the class testable 1. class AppleSauce1

    { 2. We can now configure 3. AppleSauce1( these in our tests 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. } Slide 25 CC BY-SA richargh.de
  22. We can split but not reorder without tests DataOpsController DataOpsController

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

    every line of our testee 1. [foo, 42, true] => None, 2024-01-01 2. [bar, 12, false] => Almost, 2099-01-01 3. [bla, 0, null] => Finally, null 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. } Line covered Slide 28 CC BY-SA richargh.de See also https://approvaltests.com/ “AppleSauce” idea straight from https://www.digdeeproots.com/articles/on/naming-process/
  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 L Refactoring1: change structure without changing behavior Slide 29 CC BY-SA richargh.de 1 https://martinfowler.com/books/refactoring.html
  25. Pattern: Strongly-typed Primitives Small changes, big knowledge boost Ids Units

    of measure Domain Concepts 1. 2. 3. 4. 5. 6. 7. 8. 9. 1. 2. 3. 4. 5. 6. 7. 8. 9. 1. 2. 3. 4. 5. var userId = UserId.of(123); var bookId = BookId.of(789); // allowed Book book = getBook(bookId); // produces a design-time error Book book = getBook(userId); Slide 31 CC BY-SA richargh.de Meters meters = Meters.Of(5); Seconds seconds = Seconds.Of(2); Money money = Money.Of(5, EUR); // allowed Speed speed = meters.Per(seconds); # 1 solo field record Isbn(String raw) record HoldDuration() # 2+ that appear together record Cancellation() // produces a design-time error var foo = meters.plus(seconds); 1 uses the checker framework https://checkerframework.org/
  26. Pattern Chunk your code into components (place outliers where they’re

    used the most) DataOpsController AppleSauce1 TruffleSauce1 ModuleService Books core/AppleSauce? Slide 33 CC BY-SA richargh.de supporting/Truffles? “AppleSauce” idea straight from https://www.digdeeproots.com/articles/on/naming-process/
  27. Pattern Inverse Object Mother 1. // Required state, temporarily in

    main 2. // we’ll move this to test soon 3. void main() { 4. oneCharacterization(); 5. } 6. 7. // characterizations have no concept of why 8. void displaysListOfBooksOnStart(){ 9. // needs a user 10. createUser(); 11. // needs at least one author 12. var author = createAuthor(); 13. // needs at least one book 14. var book = createBook(author); 15. // … needs xyz as well 16. } Slide 34 CC BY-SA richargh.de
  28. Pattern 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 Slide 35 CC BY-SA richargh.de
  29. Pattern: Entity Ownership Who reads and writes book? 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 Read Slide 36 CC BY-SA richargh.de
  30. Pattern: Entity Ownership 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 37 CC BY-SA richargh.de
  31. Pattern: North-Star Architecture Define your understanding as code 1. @ArchTest

    2. static final ArchRule no_classes_should_depend_on_service = 3. freeze( // accept existing violations 4. noClasses() 5. .that().resideInAPackage("..common..") 6. .should().accessClassesThat().resideInAPackage("..patron..") 7. ); Slide 38 CC BY-SA richargh.de Example uses ArchUnit https://www.archunit.org/
  32. Pattern: North-Star Architecture Map your frozen architecture violations. Lines of

    Code Slide 39 CC BY-SA richargh.de Arc. Violations Arc. Violations (high) CodeCharta visualisation https://maibornwolff.github.io/codecharta/
  33. Legacy means less predictability for “when it will be done”*

    Mo Tu When will it be done? Slide 41 CC BY-SA richargh.de 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 Certainity of estimate 10% 30% 50% 70% 90% * When you don’t have legacy code, your predictability mainly depends on how small you make your work items
  34. Legacy means less predictability for “when it will be done”*

    Mo Tu When will it be done? Slide 42 CC BY-SA richargh.de 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 Certainity of estimate 10% 30% 50% 70% 90% * When you don’t have legacy code, your predictability mainly depends on how small you make your work items
  35. Pattern: Quality Views Colorize based on predictability 2/10* Patron ✗

    Criteria 1 ✓ … ✗ … ✗ … ✓ … ✗ … ✗ Criteria n Slide 43 CC BY-SA richargh.de ✓ Yes Component ✗ No Increasing predictability * Define appropriate criteria as a team.
  36. Pattern: Quality Views Colorize based on predictability Behavior Structure component

    bounds ✗ Enforced (f.ex. via Arch-Unit tests) ✓ Characterization tests coverage 100% ✓ Component testable ✗ Functional Unit tests coverage +80% ✗ Presentation-Infra-Domain Layering ✗ Contract test coverage +100% Service-Level Agreements See appendix for more ideas…* Slide 44 CC BY-SA richargh.de ✗ No obsolete or critically vulnerable depedendencies used * Define appropriate criteria as a team.
  37. Pattern: Quality Views Communicate High-Level-View Patron Book Inventory Better Component

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

    Inventory Better Component Capability Feature-planning often requires capability details Worse Locked Increasing predictability Focus Slide 46 CC BY-SA richargh.de Quality Views Based-on https://blog.colinbreck.com/using-quality-views-to-communicate-software-quality-and-evolution/
  39. By adressing these Structure Behavior We’ll unearth Service-Level Agreements ✓

    Domain concepts ✓ Behavior documentation (tests J) And fix this Certainity of estimate 10% 30% 50% 70% 90% Slide 47 CC BY-SA richargh.de
  40. Quality Views Domain Clusters Characterized behavior Code is testable Slide

    48 CC BY-SA richargh.de Change here breaks there
  41. Pattern: Temporal Coupling1 Map your Temporal Coupling RentService RentService Notice

    that no compile-time relation exists between the temporally coupled files. Lines of Code Slide 49 CC BY-SA richargh.de Cycl. Complexity Number of authors (high) Ingoing Temporal Coupling 1 from the book https://pragprog.com/titles/atcrime/your-code-as-a-crime-scene CodeCharta visualisation https://maibornwolff.github.io/codecharta/
  42. 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 50 CC BY-SA richargh.de Connascence by Meilir Page-Jones, “Fundamentals of Object-Oriented Design in Uml”
  43. Slide 51 CC BY-SA richargh.de Refactor this way Cheap Expensive

    Change Explicit Domain • Name: variable, method, SQL Table • Type: int, String, Money, Person Good • Meaning: what is true,‘YES‘,null Bad • And 6 more, including Execution Order Hidden domain Hard on your brain Easy Connascence of Connascence: https://www.maibornwolff.de/en/know-how/connascence-rules-good-software-design/
  44. Connascence guides refactoring Connascence of Meaning 1. // A) Connascence

    of Type 2. enum MovieType { } 3. // B) Connascence of Type 4. sealed interface Movie permits RegularMovie { } 5. // C) Connascence of Name 6. interface Movie { 7. int amount(){ … } 8. } 1. 2. 3. 4. Slide 52 CC BY-SA richargh.de // A) Connascence of Name static int OLD_PEOPLE_PENALTY = 25; // B) // appropriate solution is a team effort J Connascence: https://www.maibornwolff.de/en/know-how/connascence-rules-good-software-design/
  45. Let Connascence guide the decoupling Reduce Strength Slide 53 CC

    BY-SA richargh.de Increase Locality Lock Element Locked Domain Concept Strong of Connascence New Domain Concept Weak of Connascence
  46. Quality Views Domain Clusters Characterized behavior Code is testable Slide

    54 CC BY-SA richargh.de Decoupled some Tests become blockers
  47. Tests can cement structure and block progress Test 1 Test

    n new Book(“1”, “Abc”, …) new Book(“n”, “xyz”, …) class Book { … } The redundant initialization in n tests cements the design of the type Slide 55 CC BY-SA richargh.de
  48. Pattern Outside-in Tests via Dsl Context Approach • Keep tests

    structureinsensitive when you don’t know what your future structure will look like • Be able to convert integration tests to unit tests after remodelling • Use an abstraction for the test setup. Don‘t let tests directly … Slide 56 CC BY-SA richargh.de • 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
  49. Pattern: Outside-in Tests via Dsl Start with an integration test

    <module>/renting.integration.test.ts 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 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 c. 13. // WHEN 14. const result = testee.rentBook(book, user); 15. // THEN 16. expect(result.isRented).toBeTrue(); 17. } Slide 57 CC BY-SA richargh.de See also Java Aktuell 5/24 and https://richargh.de/posts/Structure-Cementing-Tests-1
  50. Pattern: Outside-in Tests via Dsl Go unit with one change,

    once all db logic is in domain <module>/renting.unit.test.ts 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. } Slide 58 CC BY-SA richargh.de See also Java Aktuell 6/24 and https://richargh.de/posts/Structure-Cementing-Tests-1
  51. Quality Views Domain Clusters Characterized behavior Code is testable Slide

    59 CC BY-SA richargh.de Decoupled some Outside-in Tests via Dsl Innovation jam
  52. When we safely work towards our goal … DataOpsController BookController

    delegates to AppleSauce1 handle handle CreateBook RentBook Configurable Dependencies Configurable Dependencies Presentation Application Domain Infrastructure Slide 60 CC BY-SA richargh.de
  53. … we can show technical improvements ModuleService Slide 61 CC

    BY-SA richargh.de CodeCharta visualisation https://maibornwolff.github.io/codecharta/
  54. Pattern: Code Tag Cloud … the domain says hello Slide

    62 CC BY-SA richargh.de Generated with: https://github.com/Richargh/code-tagcloud-py-sandbox
  55. Pattern: Quality Views … and so does predictable innovation* search

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

    a ⭐ Slide 64 CC BY-SA richargh.de In case you are interested in TestDSLs https://richargh.de/posts/Structure-Cementing-Tests-1 Or a PR?
  57. Happy to take questions and/or coffee Richard Gross (he/him) Head

    of Software Archaeology Software Modernisation Hypermedia Health Checks Drink a (virtual) coffee with me. richargh.de richargh.de richargh Works for maibornwolff.de/ https://content.maibornwolff.de/meetings/richard-gross Slide 65 CC BY-SA richargh.de
  58. The AI Imperatives 1. Secure Genies like malware 2. A

    genie won't stand trial 3. Call the shot before sending the prompt 4. Enjoy the struggle to learn 5. Treat genies like they have brain damage 6. Iterate with the Genie not against it Slide 67 CC BY-SA richargh.de
  59. 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 68 CC BY-SA richargh.de
  60. Passive Pattern: Map Coordination Bottlenecks Context Approach • Code elements

    that everyone changes usually require extensive coordination to avoid conflicts. 1. In the code-map, mark complex elements where most of the team have made recent changes. Slide 70 CC BY-SA richargh.de
  61. Passive Pattern: Map Coordination Bottlenecks RentingService.kt Lot‘s of code, many

    decisions and 20 authors. Why? DataMocks.kt Lot‘s of code, but no decisions. Probably fine. Lines of Code Slide 71 by richargh.de from Cycl. Complexity Number of authors (high) CodeCharta visualisation https://maibornwolff.github.io/codecharta/
  62. Passive Pattern: Map Knowledge Silos Context Approach • 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. 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 72 CC BY-SA richargh.de See also https://codescene.com/knowledge-distribution
  63. Passive Pattern: Map Knowledge Silos Librarian.kt Medium code, medium complex,

    but only one person knows about it Lines of Code Slide 73 CC BY-SA richargh.de Cog. Complexity Number of authors (low) CodeCharta visualisation https://maibornwolff.github.io/codecharta/
  64. Active Pattern: Knowledge Sharing Context Approach • Mitigate the business

    risk of knowledge silos Caveat • Having everyone know everything is time-consuming and wasteful due to forgetfulness • “Owner” delegates changes and reviews • Pair/Mob programming • Dev Talk Walkthrough • Simple code • Specification by (test) example Slide 74 CC BY-SA richargh.de
  65. How do we refactor what we don‘t understand? Slide 75

    CC BY-SA richargh.de Slides by @arghrich
  66. „Nopefactoring“ The No-thinking refactoring“ • Lift-up conditional • Split to

    Classes Advanced Testing & Refactoring Techniques Cutting Code Quickly Emily Bache Llewellyn Falco @emilybache @LlewellynFalco Slide 76 CC BY-SA richargh.de Slides by @arghrich
  67. Why all the effort to rediscover? We could just start

    anew! Slide 77 CC BY-SA richargh.de
  68. Features The True Cost of Feature-based Rewrites cope S d

    e r cove s i d n U Planned Catch Up Missing Features Enhancements Releases over time Rewrite Sub-Par Parity Adoption Old App Actual Features Slide 78 CC BY-SA richargh.de Original Article by Doug Bradbury The True Cost of Rewrites
  69. The cost of the rewrite depends on your approach Feature-based

    rewrite Outcome-based rewrite • Goal = Feature + Feature + Feature • Incrementally build feature after feature • Release when all are done • Goal = achieve an outcome • Write the minimal thing to achieve outcome • Iterate Slide 79 CC BY-SA richargh.de
  70. 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 80 CC BY-SA richargh.de
  71. Communication Pattern: Quality Views Parking-Lot View RG Pricing {10}+[25]+25% 23%

    08 2024 +- 25% Component Champion Component Name {Scoped} + [Unscoped] Transformations Completion of Scoped Transformation Expected Completion Month + Uncertainity% Better Component Capability Worse Locked Focus Slide 81 CC BY-SA richargh.de Increasing changeability
  72. Communication Pattern: Quality Views Even more detail Pricing Renting Invoicing

    search book bill quote liquidate Component Capability FE Better BE Worse Feature-planning often requires more details Slide 82 CC BY-SA richargh.de Locked Focus Increasing changeability
  73. Pattern: Complexity1 Invest Context Approach • 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. 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 83 CC BY-SA richargh.de
  74. Pattern: Complexity Invest searchPanel ribbonba r loadInitialFile.service.ts „Interesting, why is

    that so complex“? codemap store customConfigs viewCube.component.ts datamocks.ts „Complex but does not show up in tag cloud, why?“ nodeDecorator.ts „Node from the tag cloud, what does it do?“ Lines of Code Cycl. Complexity Cycl. Complexity (high) CodeCharta Code visualized by CodeCharta https://maibornwolff.github.io/codecharta/ Slide 84 CC BY-SA richargh.de
  75. 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 85 CC BY-SA richargh.de
  76. Anti-Pattern: Package by Layer1,2 utils Controller A services Controller B

    Utils are a smell Presentation Layer Service A controllers It‘s all about the frameworks Service B Domain Layer Repository A Repository B Infrastructure Layer Models intermixed with unrelated models. repositories models 1 Presentation-Domain-Data Layering: https://martinfowler.com/bliki/PresentationDomainDataLayering.html Slide 86 CC BY-SA richargh.de 2 Simon Brown has a good explanation as well: https://dzone.com/articles/package-component-and
  77. Active Pattern: Package by Component1,2 Context Approach • Components mean:

    study & (heavily) change one thing at a time • Group together what fires together: 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 • 1 feature, 1 commit, 1 component • Top-level components communicate domain Caveat • service/, models/, repositories/ vs • book/, inventory/, dailysheet/ • You can’t get it right the first time. A lot of spaghettidependencies will still exist for now 1 Presentation-Domain-Data Layering: https://martinfowler.com/bliki/PresentationDomainDataLayering.html Slide 87 CC BY-SA richargh.de 2 Simon Brown has a good explanation as well: https://dzone.com/articles/package-component-and
  78. Active Pattern: Package by Component1,2 patron Controller A book Controller

    B Presentation Layer dailysheet Service A Service B Domain Layer commons Repository A Repository B Data Layer inventory 1 Presentation-Domain-Data Layering: https://martinfowler.com/bliki/PresentationDomainDataLayering.html Slide 88 CC BY-SA richargh.de 2 Simon Brown has a good explanation as well: https://dzone.com/articles/package-component-and
  79. The Domain gets top-level modules patron ? dailysheet ? ?

    book ? ? ? ? ? ? ? ? Book ? Slide 89 CC BY-SA richargh.de ? ? ? ? ? BookOnHold ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PatronId ? ? ? ? inventory ? ?
  80. An ordered mess emerges Patron Book ? ? ? ?

    ? ? ? ? ? ? ? BookOnHold ? ? ? ? ? ? ? ? ? Slide 90 CC ? BY-SA richargh.de ? ? ? ? ? ? ? PatronId ? ? Book ? ? inventory dailysheet ? ? ? ? ? ? ? ?
  81. Communication Pattern: Quality Views Colorize based on changeability Patron Structure

    Outside Api ∅ Explicit (Explicit Schema and Dtos) component bounds ✗ Enforced (f.ex. Arch-Unit tests) Tests over 20% Tests over 40% Tests over 60% Behavior Fitness functions ✓ Characterization tests line coverage +90% No Arch Violations ✗ Unit+integration tests line coverage +90% testable Unit test domain mutation coverage +90% ✗ ✓ Component (interact with outside world via ports) ✗ Presentation-Domain-Infra Layering ✗ No obsolete or critically Code in good shape ✗ vulnerable depedendencies ✗ Key SLAs tested (low smells/good IOSP ratio) ✗ Built-in Logs, Metrics, Alerts ✓ Yes ✗ No ∅ Does not apply 2/11 Slide 91 CC BY-SA richargh.de Component Increasing changeability
  82. Passive Pattern: Activity Logging Context Approach • Know which code

    parts are reached often and potentially critical • Know which code parts are not reached at all and are potentially obsolete • Identify system entry points & deep interna, then log there Slide 94 CC BY-SA richargh.de • Alt: Prometheus Counter • Count in production Caveat • Some things are cyclical yearly/monthly (reports)
  83. 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 95 CC BY-SA richargh.de
  84. 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 96 CC BY-SA richargh.de
  85. A brief coupling primer High Coupling Low Cohesion Low Coupling

    High Cohesion Coupling Unknown Source Slide 99 CC BY-SA richargh.de
  86. Hard on your brain Refactor this way Easy Connascence Guides

    Refactoring • Name: variable, method, SQL Table • Type: int, String, Money, Person Good • Meaning: what is true,‘YES‘,null,love Bad • 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 Really Bad • Identity: reference same entity Connascence: 🇩🇪 https://www.maibornwolff.de/know-how/connascence-regeln-fuer-gutes-software-design/ Slide 100 CC BY-SA richargh.de
  87. 4-axes of Connascence Strength Level How explicit Degree Locality Number

    of Impacts How close Volatiliy How much change Slide 101 CC BY-SA richargh.de
  88. The 4½ types of testing Oracle-based testing Property-based testing1 Captures

    intended behavior Captures intended properties One Test Specific input assert actual matches expected Characterization testing2,3 Captures observed behavior Many Tests (often) Generated inputs assert actual matches snapshot One Test Random inputs assert one property of all outputs Metamorphic testing4 Captures intended metamorphic relations One assert outputs One source, keep relation to Derived Test source inputs 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 102 CC BY-SA richargh.de
  89. Metamorphic testing Input x Derive via metamorphic relation g(x) Slide

    103 CC BY-SA richargh.de Output testee f(x) testee g(f(x)) Assert Check if relation holds