Showing posts with label Modularity. Show all posts
Showing posts with label Modularity. Show all posts

Thursday, May 17, 2018

Artful Versions


When you focus on modular resilience, one item that helps a great deal is to pay attention to versioning. Version numbers are of use not only for change management but for testing and customer support as well. Apps developed with Microsoft tools keep their version numbers in a config file. You should standardize on a common meaning for the four subparts of version numbers.

Upgrades that produce a system that is incompatible with a prior release should increment the "major" version number. Upgrades that retain compatibility but that add significant functionality, should upgrade the "minor" version number. Bug fixes that get packaged into a distribution should increment the "release" version number. Finally the developers should increment the fourth component, the "build" number, with every compilation.

I like to present versions to the customer as major dot minor, for example this is versions 2.4 of the software. This version should display in a welcome pop-up and in the title bar of the main screen of your application. But for purpose of technical support I like to display (in a pop-up "About" box) something like version 2.4.3 build 118. Of course all of the version components are retrievable using the system reflection class.

Another helpful trick is to include a parallel matching version number within the backing database. When the client begins to run your software, make sure the version number in the data is compatible with the number in your software.

You should also version all of the modules within your software in a similar fashion. Although the component module versions aren't typically presented to the end user, reflect them to a called module (for debugging purposes) so that the called class can display the version of the invoker whenever it throws an error.

Sometimes it may seem superfluous, but it only takes about 30 seconds to update a version number, and with multiple developers on a project you will find that having this tracking available is invaluable.


Monday, January 15, 2018

The Art in Modularity


Good software developers tend to be organizing freaks: everything goes in a tiny little box in its proper place. You know the saying “there’s no limit to how complicated things can get, on account of one thing always leading to another.” This is especially true in programming.

To combat this creeping complexity we strive for modularity and maintainability. A small module that encloses a specific business rule is easier for testing and allows for greater flexibility of reuse. When modules become too small however, the sheer act of tracking them, organizing their use, maintaining consistent versions, and finding where a business rule gets implemented interferes with your productivity.

Somewhat counter-intuitively smaller modules also need greater external documentation, mainly to track the nature of the parameters passed between them.

What’s the right size for a module then? And how many modules should a system have? Although I like a module to be between a half and five printed pages it really depends upon two things: how many people are maintaining the software and the density of the development language.

The fewer the number of developers the larger modules can be. Each of you is baking your own cake. So if it’s just the two of you maintaining a legacy billing system written in an old dialect of Basic (that gets about as complicated as a GOSUB and a CALL) then sure, go with the 5-page modules.

But if a half-dozen of you are planning to maintain ASP web pages in C# (with overloads and inheritance) then you’d better veer closer to the halfpage module size. In that case you’re not each baking a cake; you’re all contributing to building a car. You need to be able to swap in new replacement parts when the old ones wear thin. Every once in a while step back to review how you are developing the code: are you using appropriately sized modules?


Wednesday, March 11, 2015

Artful Modality


In the many years that I've done software development I've noticed that systems written in-house tend toward two extremes. On one side you have useful software designed for a small quantity of people to do a very specific task, and on the other side you have somewhat clumsy software designed for two thirds of the company to support a wide variety of operations.

The smaller, task-specific software has tens and hundreds of flavors, yet it only survives three or four years until it gets replaced by a new incarnation. The multipurpose clunky software has just one flavor but seems to live for fifteen years, usually well beyond its prime. How come there is no middle ground?

Well just like in animation, sociology creates an "uncanny valley" of in-house software that doesn't comfortably exist. And this is due very much to the nature of people and the work that they do. In most jobs people tend to leverage certain knowledge and skills to support the company with specific tasks. A capable software designer can create very nifty systems that can maintain incredible complexity with the understanding that a moderately competent user will be trained on its use.

Such systems can be local successes even though they don't translate well to other users. And they don't survive for long because they don't incorporate the dynamics of both system-level and human-level changes. People change jobs and don't communicate all of the knowledge. Small systems fall into disuse because they are people and knowledge specific. They are still good for what they are good at: improving local productivity.

Large systems live past their prime for similar sociological reasons. The rate of change in people-skills overwhelms the complexity of linking processes. Linked processes become unlinked, and the training materials don't keep up with the changes in the software.

Large systems still serve a useful purpose however, to the extent that they organize people to work together. Where does the personality of a corporation really exist? In between the modes of small and large software.


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.


Tuesday, June 5, 2012

Artful Deprecation


In a couple earlier posts I discussed three alternatives for handling changes to business rules in an object-oriented development environment. Now we come to the last approach, deprecation. We use deprecation when we want to "take back" a previously distributed method; we want to completely replace its implementation with something new and different.

Rather than reusing the same method name, we create (within the same class) an entirely new method with a new name. Then in meta-compiler statements we deprecate the old method (we prepend the "Obsolete" directive) so that if a developer attempts to use it they will receive a pop-up that recommends that they use the new method name instead. Unlike an overload, it may not be possible for to modify the old method to call the new method with nulls in the new arguments. Do it however if a safe way can be found to "translate" between the two methods.

The only drawback with deprecation is that once a team becomes accustomed to certain common method-names it becomes a hassle to have to relearn them. Usually when you deprecate a method you will make an extra effort to communicate this change to the development staff with a global notice; you may even make the effort to search the source code library for any routines that used the old method and then schedule some working time to update them.

In summary then, handle change by overloading, inheriting, propertizing, and deprecating, but use the approach that is appropriate for your situation.


Saturday, March 20, 2010

More Art in Change


Business rules change and you will need a way to accommodate that. To maintain system resilience and modular compatibility you must however avoid certain habits; here's how. The four secret words are inheritance, overload, deprecation, and propertizing.

These are pretty standard concepts in the object oriented world, but using them correctly takes a bit of discipline. Part of the issue is that post-hoc seat of the pants changes are a poor excuse for thoughtful refactoring in the first place.

For example say you have a routine that estimates a car's mileage based upon a single measurement plus a date of measurement. This gets implemented as myCar.getCurrentMiles. Now your company wants to change the method to use two measurements and two measurement dates instead. How do you proceed?

Look at all the options and consider what they buy you, but also consider what they break.

The first and easiest option is the overload. You already have myCar.getCurrentMiles (measurement, date), now in the same class you add myCar.getCurrentMiles(measure1, date1, measure2, date2). Of course this new method starts out with the code that used to be in the old method (have the old method call the new method with null values for the second measure and date).

External calls using the old method will still work, and developers coding for this method will see both signatures in their intellisence pop up. This does have the drawback however of failing to provide much to the consumer in the way of choosing the newer signature of the method over the older. When to call it with one measurement, and when with two? More about the other alternatives for handling change in a later post.