Working Effectively with Legacy Code  written by Michael Feathers Something I understand about the book
Contents The Book Definition of Legacy Code Problems of the Conservative Approach The Automatic Test Approach Example of Characterization Test Breaking Dependencies (not yet ready) Seams (not yet ready) Conclusion: the complaints/solutions matrix
The Book Robert C. Martin Series Michael C. Feathers http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052   Paperback:  456 pages Publisher:  Prentice Hall PTR; 1 edition (October 2, 2004)
What is legacy code? (for me/us?) Code that: is difficult to understand is fragile is difficult to modify is not (or not well) documented has been inherited from others But that : is still useful!
What is legacy code? (M.Feathers) M.Feathers says: Legacy Code  ≡ Code without automatic tests Why?
The conservative approach No automatic tests  leads to   Manual testing  that   leads to   High risk of introduce bugs  that leads to   Fear of change  so You prefer add more mess instead  cleaning up  the existing code and   the code get worse,  the cost/time of adding features increases If it ain't broke don't fix it
Preserving Behaviour with Characterization Tests  Write tests for an existings program Write a test that “Describe” the  actual  behaviour Use these test as regression tests during the refactoring/feature addition. Please note that: “actual” is not always the same as “correct”.
Characterization Tests Example (1/5) public static int[] slaDjcl(double djm) { long ld, jd, n4, nd10; if ((djm <= -2395520.0) || (djm >= 1e9)) { throw new IllegalArgumentException(&quot;MJD out of valid range&quot;); } ld = (long) djm; jd = ld + 2400001L; n4 = 4L * (jd + ((6L * ((4L * jd - 17918L) / 146097L)) / 4L + 1L) / 2L - 37L); nd10 = 10L * (((n4 - 237L) % 1461L) / 4L) + 5L; int[] ret = new int[3]; ret[0] = (int) (n4 / 1461L - 4712L); ret[1] = (int) (((nd10 / 306L + 2L) % 12L) + 1L); ret[2] = (int) ((nd10 % 306L) / 10L + 1L); return ret; }
Characterization  Tests Example (2/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{},slaDjcl(0.0)); } Testcase: testSlaDjcl(prova.ProvaTest):  FAILED array lengths differed, expected.length=0 actual.length=3 junit.framework.AssertionFailedError: array lengths differed, expected.length=0 actual.length=3 at prova.ProvaTest.testSlaDjcl(ProvaTest.java:19)
Characterization  Tests Example (3/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{0,0,0},slaDjcl(0.0)); } Testcase: testSlaDjcl(prova.ProvaTest):  Caused an ERROR arrays first differed at element [0]; expected:<0> but was:<1858> arrays first differed at element [0]; expected:<0> but was:<1858> at prova.ProvaTest.testSlaDjcl(ProvaTest.java:19)
Characterization  Tests Example (4/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{1858,11,17},slaDjcl(0.0)); } BUILD SUCCESSFUL (total time: 0 seconds)
Characterization  Tests Example (5/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{1858,11,17},slaDjcl(0.0)); assertArrayEquals(new int[]{-3617, 1, 24},slaDjcl(-2000000.0)); assertArrayEquals(new int[]{10346, 5, 23},slaDjcl(3100000.0)); assertArrayEquals(new int[]{24309, 9, 19},slaDjcl(8200000.0)); assertArrayEquals(new int[]{38273, 1, 15},slaDjcl(1.33E7)); assertArrayEquals(new int[]{52236, 5, 14},slaDjcl(1.84E7)); assertArrayEquals(new int[]{66199, 9, 10},slaDjcl(2.35E7)); assertArrayEquals(new int[]{80163, 1, 7},slaDjcl(2.86E7)); assertArrayEquals(new int[]{94126, 5, 6},slaDjcl(3.37E7)); assertArrayEquals(new int[]{108089, 9, 1},slaDjcl(3.88E7)); assertArrayEquals(new int[]{122052, 12, 29},slaDjcl(4.39E7)); assertArrayEquals(new int[]{136016, 4, 27},slaDjcl(4.9E7)); assertArrayEquals(new int[]{149979, 8, 25},slaDjcl(5.41E7)); assertArrayEquals(new int[]{163942, 12, 22},slaDjcl(5.92E7)); assertArrayEquals(new int[]{177906, 4, 20},slaDjcl(6.43E7)); assertArrayEquals(new int[]{191869, 8, 16},slaDjcl(6.94E7)); assertArrayEquals(new int[]{205832, 12, 13},slaDjcl(7.45E7)); assertArrayEquals(new int[]{219796, 4, 10},slaDjcl(7.96E7)); assertArrayEquals(new int[]{233759, 8, 8},slaDjcl(8.47E7)); assertArrayEquals(new int[]{247722, 12, 5},slaDjcl(8.98E7)); assertArrayEquals(new int[]{261686, 4, 2},slaDjcl(9.49E7)); }
The TDD algorithm 0. Get the class you want to change under test. 1. Write a failing test case. 2. Get it to compile. 3. Make it pass. (Try not to change existing code as you do this.) 4. Remove duplication. 5. Repeat.
Conclusions: Complaints / Solutions matrix So then you ... But legacy code… add them (as characterization tests) lacks of test redesign it (with refactoring) is ugly
Conclusions: I can’t put code under tests because So you I can’t because code Break Depedencies Use the seams depends of everything Introduce sensing variabiles Use exract method (safe version) lacks of modularity
Thanks Andrea Francia http://andrefrancia.it/ http://blog.andreafrancia.it [email_address] These slide will be available at my blog.

Working Effectively with Legacy Code (draft)

  • 1.
    Working Effectively withLegacy Code written by Michael Feathers Something I understand about the book
  • 2.
    Contents The BookDefinition of Legacy Code Problems of the Conservative Approach The Automatic Test Approach Example of Characterization Test Breaking Dependencies (not yet ready) Seams (not yet ready) Conclusion: the complaints/solutions matrix
  • 3.
    The Book RobertC. Martin Series Michael C. Feathers http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 Paperback:  456 pages Publisher:  Prentice Hall PTR; 1 edition (October 2, 2004)
  • 4.
    What is legacycode? (for me/us?) Code that: is difficult to understand is fragile is difficult to modify is not (or not well) documented has been inherited from others But that : is still useful!
  • 5.
    What is legacycode? (M.Feathers) M.Feathers says: Legacy Code ≡ Code without automatic tests Why?
  • 6.
    The conservative approachNo automatic tests leads to Manual testing that leads to High risk of introduce bugs that leads to Fear of change so You prefer add more mess instead cleaning up the existing code and the code get worse, the cost/time of adding features increases If it ain't broke don't fix it
  • 7.
    Preserving Behaviour withCharacterization Tests Write tests for an existings program Write a test that “Describe” the actual behaviour Use these test as regression tests during the refactoring/feature addition. Please note that: “actual” is not always the same as “correct”.
  • 8.
    Characterization Tests Example(1/5) public static int[] slaDjcl(double djm) { long ld, jd, n4, nd10; if ((djm <= -2395520.0) || (djm >= 1e9)) { throw new IllegalArgumentException(&quot;MJD out of valid range&quot;); } ld = (long) djm; jd = ld + 2400001L; n4 = 4L * (jd + ((6L * ((4L * jd - 17918L) / 146097L)) / 4L + 1L) / 2L - 37L); nd10 = 10L * (((n4 - 237L) % 1461L) / 4L) + 5L; int[] ret = new int[3]; ret[0] = (int) (n4 / 1461L - 4712L); ret[1] = (int) (((nd10 / 306L + 2L) % 12L) + 1L); ret[2] = (int) ((nd10 % 306L) / 10L + 1L); return ret; }
  • 9.
    Characterization TestsExample (2/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{},slaDjcl(0.0)); } Testcase: testSlaDjcl(prova.ProvaTest): FAILED array lengths differed, expected.length=0 actual.length=3 junit.framework.AssertionFailedError: array lengths differed, expected.length=0 actual.length=3 at prova.ProvaTest.testSlaDjcl(ProvaTest.java:19)
  • 10.
    Characterization TestsExample (3/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{0,0,0},slaDjcl(0.0)); } Testcase: testSlaDjcl(prova.ProvaTest): Caused an ERROR arrays first differed at element [0]; expected:<0> but was:<1858> arrays first differed at element [0]; expected:<0> but was:<1858> at prova.ProvaTest.testSlaDjcl(ProvaTest.java:19)
  • 11.
    Characterization TestsExample (4/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{1858,11,17},slaDjcl(0.0)); } BUILD SUCCESSFUL (total time: 0 seconds)
  • 12.
    Characterization TestsExample (5/5) @Test public void testSlaDjcl() { assertArrayEquals(new int[]{1858,11,17},slaDjcl(0.0)); assertArrayEquals(new int[]{-3617, 1, 24},slaDjcl(-2000000.0)); assertArrayEquals(new int[]{10346, 5, 23},slaDjcl(3100000.0)); assertArrayEquals(new int[]{24309, 9, 19},slaDjcl(8200000.0)); assertArrayEquals(new int[]{38273, 1, 15},slaDjcl(1.33E7)); assertArrayEquals(new int[]{52236, 5, 14},slaDjcl(1.84E7)); assertArrayEquals(new int[]{66199, 9, 10},slaDjcl(2.35E7)); assertArrayEquals(new int[]{80163, 1, 7},slaDjcl(2.86E7)); assertArrayEquals(new int[]{94126, 5, 6},slaDjcl(3.37E7)); assertArrayEquals(new int[]{108089, 9, 1},slaDjcl(3.88E7)); assertArrayEquals(new int[]{122052, 12, 29},slaDjcl(4.39E7)); assertArrayEquals(new int[]{136016, 4, 27},slaDjcl(4.9E7)); assertArrayEquals(new int[]{149979, 8, 25},slaDjcl(5.41E7)); assertArrayEquals(new int[]{163942, 12, 22},slaDjcl(5.92E7)); assertArrayEquals(new int[]{177906, 4, 20},slaDjcl(6.43E7)); assertArrayEquals(new int[]{191869, 8, 16},slaDjcl(6.94E7)); assertArrayEquals(new int[]{205832, 12, 13},slaDjcl(7.45E7)); assertArrayEquals(new int[]{219796, 4, 10},slaDjcl(7.96E7)); assertArrayEquals(new int[]{233759, 8, 8},slaDjcl(8.47E7)); assertArrayEquals(new int[]{247722, 12, 5},slaDjcl(8.98E7)); assertArrayEquals(new int[]{261686, 4, 2},slaDjcl(9.49E7)); }
  • 13.
    The TDD algorithm0. Get the class you want to change under test. 1. Write a failing test case. 2. Get it to compile. 3. Make it pass. (Try not to change existing code as you do this.) 4. Remove duplication. 5. Repeat.
  • 14.
    Conclusions: Complaints /Solutions matrix So then you ... But legacy code… add them (as characterization tests) lacks of test redesign it (with refactoring) is ugly
  • 15.
    Conclusions: I can’tput code under tests because So you I can’t because code Break Depedencies Use the seams depends of everything Introduce sensing variabiles Use exract method (safe version) lacks of modularity
  • 16.
    Thanks Andrea Franciahttp://andrefrancia.it/ http://blog.andreafrancia.it [email_address] These slide will be available at my blog.

Editor's Notes