Holding The Gains

Once you come up with the elegant solution, how do you make sure that you won't have to turn around and solve it again tomorrow?

Code libraries are nice; every programmer's got a big directory full of snippets and clippings, past projects and hard-won knowledge. But after a while you spend more time trying to remember where you put things than it'd take to do it over. Also, languages change, platforms change; there's a lot of BitRot going on.

One possible method would be to use web-page-like keywords in some kind of differently formatted comment? Then you can grep to your heart's content. Extensive keywords =)

Good question! Here's the version of it I'm currently struggling with: You refactor out some common code & generalize it for the three cases you currently have. Over time, it gets used in more & more places & generalized further. Eventually, though, you have a bit of code that is used in dozens, hundreds, maybe thousands of places in the code base. You want to make a small change to it to use it in just one more place. You don't have time, however, to validate that your change doesn't break everything else. Your automated unit tests might not catch a break because they weren't written with your new change in mind. What do you do?

''This seems pretty obvious. You write a new test that will show you whether the new usage works. Verify that the new test doesnt pass. Then do the change. Verify that (1) The new test passes, and (2) all the old tests pass.''

But the old tests might seem to pass because they couldn't have conceived of the function failing in ways that it can now fail but which look like success to the old unit tests. Auditing all the unit tests would take as much time as auditing all the uses of the function. (Gah! This is another one of those, it's hard to talk about without a concrete example, but a concrete example is too complex to discuss easily and would take forever to ensure you'd removed anything proprietary from it.)

If the old tests still pass, then doesnt that indicate that what they're testing, still works? You only need one test failure to show you that some behavior is broken. Therefore, write new tests that will show you if the function fails in the 'new' ways. As long as the new failure condition is covered by *a* test, you can be confident its not broken if all your tests pass.

It also occurs to me that if you have a function this complex, then something is horribly wrong.

The trouble with tests is that they can only prove the existence of bugs, not their absence. I've seen some evidence of types being a natural successor to tests. Using the maybe monad, for example, precludes the possibility of a null pointer exception. Using strict enough types seems to reduce the need to test for edge-cases. Even so, I'm not sure that types are the end of the road either. This article paints a great picture of continual progression: http://www.dustingetz.com/2011/05/05/how-to-become-an-expert-swegr.html

--Chad Zawistowski

View edit of January 22, 2013 or FindPage with title or text search