Wednesday, February 11, 2015

Artistic Inheritance


In an earlier post I discussed overloading a method to handle changing it from a computation based on one measurement to having it be based on two. Another way to handle this is with inheritance: you create a new object, say "modernCar," that inherits from myCar and then implements the overload in its method. In this way developers using the old routine use myCar.getCurrentMiles, and developers using the new routine use modernCar.getCurrentMiles.

Using this approach makes more sense than a simple overload when the new way of invoking the method clearly applies to only a selected subset of the base class.

Yet another approach for handling change is to "propertize" the method call. Basically this means changing all of the arguments to the method into class properties instead. To be clear and consistent in this approach the method should place its result in a public property as well, so the sequence for using the new method becomes set miles1, date1, miles2, date2, invoke the method, and get currentMiles.

This approach has the distinct disadvantage that it causes a hard break to existing code. It also requires clearer technical documentation explaining its use. On the other hand this approach has the two distinct advantages that future properties (both set and get) can be added later without impacting the code base, and it also encourages future developers to actually review the target code before invoking the method.

So far then we've seen three approaches for managing change: overload, inheritance, and "propertizing". In a later post I'll discuss your fourth option, deprecation.


Monday, January 12, 2015

The Art in Testing


Somewhere in the deep guts of a project you enjoy the "pleasure of unit testing." Typically the dead center looks like:

Approval of interface design
Various programming tasks
Unit tests
Software corrections from unit testing
System and stress testing

The nice thing about unit testing is that it gets so buried into the guts of a project that the managers don't pay much attention to it. Despite this however, the unit testing ultimately determines, unequivocally, the quality of your product.

Just because no one is looking doesn't mean that you should slack off from the unit tests. If your software is modularized, which on a larger project is most probably the case, then you can unit test as individual modules become available.

Earlier I discussed Artistic Test Harnesses, and how they not only allow for rapidly repeatable unit testing but also provide a failsafe for workarounds during implementation. Be sure to read that previous post to learn how to construct these useful utilities.

Recognize that Quality always involves work: there are no shortcuts to achieve it. It turns out that it is mostly the hidden, unmanaged, personal part of your work that primarily determines your long-term success.


Wednesday, December 10, 2014

More Artful Beauty


We must choose in a hundred little ways how maintainable to make our software. I wish to give you some guidance, some signposts: when you are deep in the trenches facing these hundreds of little choices, what key questions that you should ask yourself? By answering them whenever you are at a design fork-in-the-road, you will know which path to follow.

You can certainly go overboard on the maintainability spectrum. After all you do have a product to deliver. But a fair and reasonable amount of refactoring is necessary to allow the software to retain its value. So every time you roll up your sleeves and dive into the code, ask yourself these eight questions:

1) Got whitespace?
I like to see code blocked out into sections in a manner akin to how a writer blocks off paragraphs. I don't need your otherwise obvious comment lines in front of each section, just a plain intervening blank line lets me know that you are moving on to another train of thought.

2) Do you have to scroll right to read it?
If so then you are probably combining too many conditions in a single statement. Refactor into a nested "if" or if you're already deeply nested make a new function.

3) Can it have a better name?
Variables and functions should say what they do and do what they say.

4) If you were to place a debug checkpoint, what would be good to know here?
It sure compresses the coding when you do dataset.table.rows.columns[4], but it's a royal pain for debugging. If you're going more than two dots deep set a work variable for the object partway down.

5) Is your module getting too long? I chatted about appropriate module sizes in an earlier post.

6) Are the parameters that control the whole bailiwick commented at the top of your code? Are there too many of them in different places?
If you are gathering input parms from every conceivable source, maybe it's time to consolidate. You may even need to create an intermediate feeder object that collects and denormalizes all this stuff.

7) Are the logic sections... ah.... logical?
When I see nested "if" statements inside of case statements I cringe. Testing for not A or not B makes my eyelid quiver. Clean up your logic to test for eligible conditions (as opposed to ineligible conditions) and set intermediate Boolean flags.

8) Will you be able to understand this piece of code three years from now?
Perform the mental experiment of imagining that after you compile this into production, you won't see it again for another three years. Will you be able to understand what the code does? Leave yourself enough breadcrumbs to find a safe path back to understanding.

Yeah following these guidelines is a little more work, but as the saying goes: hey you can pay me now or you can pay me later.