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

Fearlessly Improving Legacy Code - PhillyXP

Fearlessly Improving Legacy Code - PhillyXP

Fearlessly Improving Legacy Code (or: How I Learned to Stop Worrying and Love Dreadful Code)

Complicated, convoluted, obfuscated, messy code without any unit tests; some call it "spaghetti code" or a "big ball of mud." There are a lot of names and euphemisms for what we call legacy code. This code often invites developers to create more of it because it can be time-consuming and unsafe to change. We tend to make the most straightforward, smallest change possible, and the code continues on its death spiral until it can no longer be patched. It can be scary to have to work in legacy code.

In this talk, I'll share my nine rules for characterization testing and show them in practice by working through a legacy code kata I have created. If you learn these rules for approaching this type of code, you'll see improving legacy code does not have to be scary. You may even grow to enjoy it as I did!

Anthony Sciamanna

November 15, 2022

More Decks by Anthony Sciamanna

Other Decks in Programming


  1. @asciamanna anthonysciamanna.com @asciamanna anthonysciamanna.com Fearlessly Improving Legacy Code or: How

    I Learned to Stop Worrying and Love Dreadful Code Anthony Sciamanna Senior Consultant - Industrial Logic
  2. @asciamanna anthonysciamanna.com How do you change your perspective? https://www.industriallogic.com/training/elearning/ Industrial

    Logic eLearning Album - Legacy Code Working Effectively with Legacy Code - Michael Feathers • Nine rules for characterization testing • Inspired by the WELC book (Michael Feathers) and Industrial Logic Legacy Code eLearning
  3. @asciamanna anthonysciamanna.com Characterization tests Source: Working Effectively with Legacy Code

    - Michael Feathers In computer programming, a characterization test (also known as Golden Master Testing) is a means to describe (characterize) the actual behavior of an existing piece of software, and therefore protect existing behavior of legacy code against unintended changes via automated testing. This term was coined by Michael Feathers. [Source: Wikipedia] Characterization Tests Golden Master Tests Pinning Tests Pin-Down Tests Scaffolding Tests Approval Tests
  4. @asciamanna anthonysciamanna.com Legacy code death spiral Make the most straightforward,

    smallest change that feels the safest in the short term (no improvements) Design degrades further making future changes harder Need to modify convoluted, untested legacy code Creates more duplication and conditional complexity Why? • “I don’t have time to [learn how to] improve the code.” • “I fear I’ll break the functionality because it isn’t clear.”
  5. @asciamanna anthonysciamanna.com Conditional logic and code duplication are the Yin

    and Yang of unmaintainable code. - Bryan Helmkamp - CEO, Code Climate “ ”
  6. @asciamanna anthonysciamanna.com Why does this happen? • Organizational pressure •

    Taylorism (developers are order takers / typists) • Managing teams by output (velocity) • Short term speed is the only success measure • We don’t know how / No time to learn • Lack of agile engineering practices
  7. @asciamanna anthonysciamanna.com REWRITE! • Same team(s) • Same (or additional)

    organizational pressures • Same organizational view on software development (Taylorism) • Never learned / practiced how to incrementally improve the code …Your rewrite is DOOMED!
  8. @asciamanna anthonysciamanna.com Law of Holes Source: Wikipedia - https://en.wikipedia.org/wiki/Law_of_holes The

    first law of holes, or the law of holes, is an adage which states: "if you find yourself in a hole, stop digging." It is used as a metaphor, warning that when in an untenable position, it is best to stop making the situation worse
  9. @asciamanna anthonysciamanna.com Team Working Agreements All new code comes with

    microtests All existing code gets tested when it needs to be changed
  10. @asciamanna anthonysciamanna.com Rule #1 - Clear the area • Once

    the change area is identified ◦ Reformat the file (automatically) ◦ Use team-shared formatting and linting rules ◦ Remove obvious noise in the code without needing to think about it
  11. @asciamanna anthonysciamanna.com Claude Shannon - “Father of Information Theory” "Almost

    every problem that you come across is befuddled with all kinds of extraneous data of one sort or another; and if you can bring this problem down into the main issues, you can see more clearly what you're trying to do." Source Business Insider From his lecture “Creative Thinking” at Bell Labs - March 20, 1952 Wikipedia: Claude Shannon
  12. @asciamanna anthonysciamanna.com Rule #2 - Use power tools Code Editors

    IDEs Provides automation tools, automated refactorings, test runners, code coverage tools
  13. @asciamanna anthonysciamanna.com Arlo Belshee’s Core Six Refactorings You should never

    have to do these things: • Format your code • Type basic language stuff / get things precisely right • Look up what refactorings you can use and their keystrokes Source: https://arlobelshee.com/the-core-6-refactorings/
  14. @asciamanna anthonysciamanna.com Rule #3 - Call it in a test

    harness • Can you create the object you want to test? • Can you call the method with null arguments? • Call method under test with simple arguments to start (then elaborate) xUnit framework, unit testing framework, or Approval Tests https://github.com/approvals https://approvaltests.com/
  15. @asciamanna anthonysciamanna.com Rule #4 - Use tests to get to

    testability • Probe existing behavior with tests • DO NOT spend time trying to figure out complicated, obfuscated logic Try this… Not that…
  16. @asciamanna anthonysciamanna.com Rule #5 - Let assertion failures expose the

    behavior • Assert null to discover results • Write assertions for actual results Agile in a Flash - Tim Ottinger & Jeff Langr Source: Working Effectively with Legacy Code - Michael Feathers
  17. @asciamanna anthonysciamanna.com Rule #6 - Examine inputs and outputs •

    Are the outputs for a single pass covered in assertions? • “Outputs” could be void calls on dependencies • How many possible input combinations are there? (Helps to give you an idea of the number of tests you’ll need)
  18. @asciamanna anthonysciamanna.com Rule #7 - Use code coverage to drive

    input variation • Vary inputs to cover more of the legacy code • Track progress via code coverage • Look for hidden branches - regex matches, LINQ chaining (C#), streams (Java) • Tools: Console Writing & Debugger
  19. @asciamanna anthonysciamanna.com Rule #8 - Use automated refactoring to create

    seams • Seams allow for breaking dependencies and getting to testability • Exploit seams by replacing objects with test doubles (object seams) • Addresses the Legacy Code Dilemma
  20. @asciamanna anthonysciamanna.com Legacy Code Dilemma “When we change code we

    should have tests in place. To put tests in place we often have to change code.” -Michael Feathers Source: Working Effectively with Legacy Code - Michael Feathers
  21. @asciamanna anthonysciamanna.com Seams A seam is a place where you

    can alter behavior in your program without editing it Every seam has an enabling point, a place where you can make the decision to use one behavior or another Source: Working Effectively with Legacy Code - Michael Feathers We introduce test doubles to exploit object seams
  22. @asciamanna anthonysciamanna.com Rule #9 - Don’t fix bugs • You

    do want to fix them, just not now • Characterize the bug’s behavior instead
  23. @asciamanna anthonysciamanna.com Next - Refactor and Introduce Microtests • Once

    legacy code is characterized (including bug behavior)... ◦ Refactor production code ◦ Cover in microtests ◦ Delete or refactor characterization tests ◦ Fix identified bugs
  24. @asciamanna anthonysciamanna.com Microtests Microtests are micro in size, run in

    milliseconds, and test a micro-behavior. They are fast and cheap, and you can run the tests thousands of times without false negatives. Because they are so small they are very easy to scan and understand
  25. @asciamanna anthonysciamanna.com Microtests Don’t • Access a database • Access

    the network • Access the filesystem • Depend on other tests • Require a complex environment to run in • Require a deployed app to test against
  26. @asciamanna anthonysciamanna.com @asciamanna anthonysciamanna.com Contact Info: Anthony Sciamanna - [email protected]

    Twitter: @asciamanna Web: anthonysciamanna.com This talk: https://bit.ly/phillyxp-legacy-code Thank you!