[hand with pencil]
Stuff For Sale
2004 Summer Tour
About
Blog
Class Stuff
Email Me
Events
Gallery
Home
In The Press
Newsletter
Services
Smalltalk
Veggie Van Gogh

Credits
© 2002,
Bytesmiths

[this is simply a banner and menu bar]

Please patronize sponsors of this page!

Bytesmiths no longer is involved in software consulting. Maintenance of this web site is currently subsidised by unrelated business activities. Please pass the word to other interested folks, so I can continue to host this page!
Your site could be listed here, for as little as $12 per month! Go to Bytesmiths Press for details.

This site has been selected by PC Webopaedia as one of the best on this topic!
This site has been awarded a Links2Go Key Resource Award in the Smalltalk category!

Originally published in The Smalltalk Report, July 1995.

Managing Modifications

by Jan Steinman
Originally published in The Smalltalk Report, July 1995.

Managing Project Documents 2

by Jan Steinman

In the last issue, we made a case for "continuous documentation," and outlined what that entails. We also promised to give you some concrete examples and source code, so you could begin to implement a continuous documentation process.

First of all, we'll need to change how classes store their comments... WE INTERRUPT THIS COLUMN TO BRING YOU A BASE IMAGE CHANGE ALERT! ALL USERS WITHIN 200 KILOBYTES OF THE IMAGE MUST EVACUATE IMMEDIATELY! WHEN YOU ARE ALLOWED TO RETURN, YOUR PRECIOUS, CAREFULLY CRAFTED, WORK OF ART CODE WILL TAKE ON STRANGE AND (we hope) WONDERFUL NEW BEHAVIOR! HAVE A NICE DAY!

Whew! We almost slipped one by the Base Image Police there, but they caught us! So, let's retitle this column and proceed.


Managing Modifications (or "Who Changed basicNew?")

Pity the poor Smalltalk vendors! You buy an object library in C++, and you typically get linkable object code -- it works, or it doesn't. But when Smalltalk customers don't like what they got from their vendor, they simply change it -- which often introduces bugs, which are often subsequently reported back to the vendor! (All of this applies to third party code as well.)

Consider the myriad ways that basic Smalltalk can become polluted:

  • Beginner naivete: "Delay := Delay forSeconds: 1."
  • Enough knowledge to hurt yourself: a seasoned ST/V user tries VisualWorks, and writes a clean up method that does "MyClass allInstances do: [:inst | inst become: nil]."
  • Enough knowledge to make it look random: the same code above, but cleverly made conditional upon rare low memory conditions, and then forgotten.
  • Unintentional overrides: such as implementing nextPutAll: in a Stream subclass that normally inherits it.
  • Forgotten halts and other test or debug code: beginners often put halts in system code (rather than putting halts in their own code, then stepping into the system code), and sometimes they forget to take them out.
  • Well meaning changes gone awry: such as the datacom specialist who changed Integer printOn: so that if the shift key is held down, they print in hexadecimal. (This one didn't quite make it to production before someone noticed strangeness when extending selection in a table by shift-clicking...)
  • Downright malicious: nah, no Smalltalker would make malicious changes, right? But if someone did, say a disgruntled soon to be former employee

Always keep in mind that base changes are the enemy of re use. One of the big wins of re use is that less testing is needed when you re use previously tested code. The down side is that changing code that is heavily re used increases the testing burden, since you aren't really sure all the uses of the changed code agree with each other.


Why are changes necessary?

In team programming, base changes fall into two categories. Personal changes are necessary for individual developers. Individuals need to be able to experiment with base changes before foisting them on their teammates, or they may experiment with base changes in order to better understand the environment, or they may simply want to customize their own environment. If you are using a code management system (such as ENVY or Team/V), you generally have numerous options for balancing the needs of the team for stability against the needs of the individuals for experimentation.

The second category is where the trouble begins. Although you should do whatever you can to discourage it, sometimes you need to make project or corporation wide base image changes. These changes might include:

  • Fix bugs in vendor's code: this is fairly unusual, but if you do find a bug that is getting in your way, and it has an obvious fix, you will probably want to incorporate it into your base. Also, maintainers love getting bug reports with fixes, so if you fix the bug carefully, document it properly, and submit it with your bug report to the vendor, there's a good chance it will be in the next release from the vendor, which makes your re integration job that much easier.
  • Make enhancements to the vendor-supplied tools: this is the category for which the Base Image Police caught us! The combination of dynamic compilation and full source code means you can easily tailor the Smalltalk development environment to your organization's specific needs. These kinds of changes have little possibility of getting into a vendor's product, and so they must be done in such a way that facilitates re integration with future vendor releases.
  • Make enhancements to the vendor-supplied framework classes: this is similar to the previous case with one important difference. The changes you make to framework classes will be delivered with your application, and so must be more robust than changes made to development tools. This should involve regression testing to ensure that the framework still functions with previously written code.

Limit scope and impact of changes

Base image changes can be categorized by their scope. You should carefully analyze your needs, and limit the scope of your change to the greatest degree possible. For example, it may first seem that you need to add an instance variable to a base image class and add two methods that use that new state, but further analysis might show that you really only need to change the use of an existing instance variable, and then hide that change by changing the methods that access that instance variable. The following change categories are roughly in order of desirability.

  • Single method , non state changes are the best. Such changes should not change the arguments or answer of the method, but only it's side effects. The answered object should have the same behavior, and no additional constraints should be placed or assumed on variables or sent methods.
  • Encapsulated state changes put different objects in instance variables , but manage those changes through the methods that access those variables. These changes tend to have small impact in any given vendor release. For example, you might need something better than simple truncation of window labels to use as icon labels, so you could change the label instance variable to be a two element Array that either answers a full, title bar length label if the window is open, or a custom short label if the window is iconified.

    Another useful encapsulated state change is arranging for an instance variable that normally holds a method selector so that it can hold a block. This can be a useful change to "pluggable views" for increasing the dynamic behavior of your system, and if properly done, is essentially invisible to old code.

    A big problem with this technique is that object state is directly visible to subclasses. If some poorly written subclass directly accesses the state you have changed, rather than going through the access methods you changed in tandem, there will be trouble, and it may be difficult to diagnose.

  • Method overrides don't seem like changes, but they can have tremendous impact. (If you don't believe us, override Behavior basicNew with a new implementation in Object class, then purposely introduce a bug and see what trouble that causes!)

    Overrides are tempting, because they do not change actual base image code, but for that same reason, an override is difficult to track and debug. They are more trouble when re integrating new vendor releases -- none of your comparison tools will detect the override as a change, but it may well conflict with vendor changes in the new release.

    Finally, there is usually a reason for the inheritance of such methods -- if an override seems attractive, be sure that the change shouldn't actually go into the inherited method.

  • Changing message arguments or return objects begins to get messy, and should be avoided. Constraining arguments or returned objects, such as requiring that an argument be an Array rather than any kind of collection, might work for your particular case, but it is certain to eventually break someone else's code that didn't share your assumption.
  • Changing object shape , or the number or ordering of instance variables, is one of the most invasive changes you can make, and is to be avoided. Adding instance variables by itself is not terrible, but if such a change is really necessary, most of the time it is because the fundamental behavior of the class is being changed -- behavior changes are what subclasses are for!
  • Changing inter class interfaces is really dangerous. You might track down all uses in your context, but third party software won't know about your change, and your Smalltalk vendor's next release certainly won't know either! If changes this extreme are required, be certain to document them well, to ease the inevitable problems that will eventually result.

"Conditionalize" changes

Especially in the latter categories mentioned above, it becomes increasingly important to factor your change in a way that makes it easy to back out. For example, if you want to add some special processing to what happens when you compile a method, it is tempting to simply put your modifications in line, but a better way is to make all your modifications in a separate method, then conditionally send that message if it exists, by using testing methods such as respondsTo: or canUnderstand: . Note that these tests can have a performance impact, but so can a broken change that you can't isolate!

This "base change boundary" is one of the few places we tolerate the use of isKindOf: . Using isKindOf: as part of your program logic is contrary to good OO design, because it imposes the sending method's viewpoint on another object, rather than obtaining the other object's willing collaboration.

However, at the base change boundary, isKindOf: is useful for testing the existence of base changes, so that they can be easily backed out without once again changing the base. It still isn't good OO design, but it's a bit more justified when used to verify module interface boundaries.

Another useful technique for managing changes is to make them conditional upon an arbitrary "signature" method. For example, you might implement hasBeenHacked in Object , and then bracket your changes inside (self resopondsTo: #hasBeenHacked) ifTrue: [...]. This way, if a particular module of enhancements are present, they are used by changed base methods, but if they are not present, the base changes skip the conditional changes.


Positive Identification

There are two principle reasons to keep track of exactly what you changed: it will make your integration with the next new release from the vendor less painful, and it will help you to back out a change if it proves to be a mistake. Identification needs to happen at the method, module, and system level.

For method changes, we've implemented a "hot key" that inserts "Modified by [user] on [date]: ." where "user" and "date" are properly filled in, and the cursor is positioned before the period to encourage the user to further describe the change. We use this two ways. In a short method, we simply place it after the normal method comment. In a long method, we bracket our changes by placing this hot key comment both before and after the change.

Common code management systems allow version names for code modules. Keep in mind that your base image changes are not the main development stream; they are a branch! So if you modify a code component that the vendor named "R1.43", you should not call your version "R1.44", because that will most likely collide with the vendor's next release!

We use two techniques for naming changed base image modules; both help indicate a branch has taken place. The simplest is to append a "dot level," so the above example becomes "R1.43.0". This can get messy if you have a number of changes from different sources, so we often prepend some identifying information, such as "Bytesmiths R1.43.0". Either way makes re integration with new vendor releases easier.

At the system level, a separate document that records every change or addition, organized by module, class, and method, is highly useful. These release notes are necessary even if your source code management system provides a version comparison tool; it is very useful to have a linear document to review when things start breaking!

In some cases, your code management comparison facilities can be harnessed to survey changes and build templates for these release notes (we plan to demonstrate this in a future column), but documenting why and how the change was made will remain a human activity.


Organizational Issues

In organizations with multiple Smalltalk development teams, there is usually an individual or a committee that has authority to decided whether a particular change to the base image will be allowed. This role of base image "Keeper" is particularly important when there is a shared corporate-wide version of all base image classes.

A trial period for changes is a good idea. The Keeper cannot always tell that a particular change is benign to all the development teams' applications. If any team reports a problem with a change to the base image, the Keeper can then modify or back out the change to correct the problem.

Even when there is only one Smalltalk team, the integrity of the base image is usually guarded by a Keeper who is the sole developer allowed to release changes to the base classes, also based on a trial period. In our experience, a trial of about one development cycle (six to eight weeks) is a good idea.


Conclusion

Now that you know how to make base changes in a way that is limited in scope, conditional, identifiable, re integratable, documented, and "back outable," we return you to your regularly scheduled column. In the next issue, we'll give you some actual base image changes to practice with as we proceed with examples of "continuous documentation."


Go to the previous column in the series, or the next column in the series.

160 Sharp Road, Salt Spring Island, British Columbia, V8K 2P6, Canada