Showing posts with label Technique. Show all posts
Showing posts with label Technique. 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.


Tuesday, July 12, 2016

The Art in Two


I've found that you can add a considerable amount of value and "cleanliness" during construction by designing and developing your software on two separate computers. Not simultaneously, but rather in an occasionally alternating fashion. The main point of this is to assure that you have enough of the referenced dll libraries in your project (along with any ancillary files) without constantly cranking around the older useless modules.

Make a full copy of your project folder, clear out the post-compile (and debug) junk, and then zip it up. Mail it to your other computer. Now on the receiving end unzip that puppy and see if you can compile it into something reasonable.

If you use two vastly different computers for this process, say a laptop running Windows10 and a wide screen desktop running Windows 7, you'll get the added quality checkpoint of verifying that your interface is presentable across platforms.

Yeah you need to install your development environment on both computers but this has some subtle advantages as well. Go ahead and square off your development: use the power of two!


Monday, December 14, 2015

The Art in Pseudo


Most new development work starts with ideas or directives or change orders or functional specifications. From this comes diagrams and wireframes and state diagrams and use cases and UML. From all of this documentary scaffolding one would think a person could just sit down and start coding. But for design work that is more involved than just the standard form-based update to a database, one more step will be helpful: the pseudocode.

In carpentry there's an old maxim "measure twice, cut once." Pseudocode does this same pre-measurement for you when you are programming.

Consider for example this recent change order I received: "Send the file Monday through Friday night at the time specified in the parameter unless the Stop flag is set in the database. Allow an override flag in the database to send the file immediately without otherwise affecting the schedule."

It looks simple enough to describe, but once you start programming you will trip yourself up without some pseudocode that considers the last time the file was sent and the actual time of day. Don't be shy to create the pseudocode if you have any doubts about any boundary conditions. And review the pseudocode with the user to help prevent any unintended misunderstandings.




Wednesday, June 25, 2014

Artistic Simplicity


In an earlier post I pointed to some general principles you should abide by when developing software. Two of these, parsimony and perspicuity, could use a bit of further elaboration. Together they delineate the promotion of a simpler approach to design. Why is simpler better?

As the saying goes, "there's more than one way to skin a cat." Relax it's just a saying, I have nothing against cats. The point however is that given any coding challenge there's usually an algorithmic technical nifty way to get it done, and a more lengthy but more easily understood mechanical way around the problem. The technically elegant way will execute faster and may allow for some future bells and whistles that can be accomplished with just a couple of extensions. The more transparent, simpler to understand, lengthy code will take longer to execute and will require more lines of code when it eventually becomes time for a new feature.

But frankly the longer code is easier for a novice to understand. And in the modern corporate world of high employee turnover, constant technological change, and shifting management fads, the longer code -- because it can be maintained by newcomers -- realistically has a greater probability of staying in use.

Simplicity begets Longevity.


Tuesday, August 21, 2012

Artful Recovery


Error handling is not rocket science and yet most developers get this so far wrong as to be embarrassing. The real purpose of error handling is threefold: to limit the impact of unanticipated interruption on processing overall, to provide the user with a graceful recovery so they may continue work, and to allow a programmer to determine and correct the underlying cause of the problem.

To accomplish this in modern object-oriented languages you need to bubble and persist your errors. Exceptions can happen anytime and nearly everywhere: the network could go down; a SQL log file can run out of space; a server can crash. Knowing this you should never assume that your good intentions (say to update a data record) will proceed along unperturbed.

“Try-catch” all data access routines as well as any methods you are calling from another class or library. You should think ahead to include code in your catching clause to resolve those situations you have initiated in your own class. Then depending on what invokes your class, you should either rethrow the entire exception or persist the exception so that you can retrieve it later.

Of course in your outermost class you need to decide how to notify the user of the exception. If your application is all client-side then create an error notification form that supplies both a simple-language description of the error along with a “details button” that provides the whole technical error code and stack dump, including the dead module’s version and session information.

The following pattern separates system-type errors (the query crashed) from errors in business logic. This is a perfectly reasonable way to handle errors, as system level errors might indicate a more severe situation that may merit immediate technical assistance, whereas business errors may simply be a missing or mistyped parameter to a function call.

Two key elements create this approach. The first is that you should have all your modules that perform core business-logic implement a commonly defined custom Error object. Here is an example of one I use:

public struct MyErrorObject
{
public string ErrAgentName;
public string ErrAgentVsn;
public string ErrDescription;
public int ErrSeverity;
}
public void SetErrObject(string ErrorReason, string WarnOrFatal)
{
ThisError = new MyErrorObject();
ThisError.ErrAgentName = Assembly.GetCallingAssembly().CodeBase;
ThisError.ErrAgentVsn = Assembly.GetCallingAssembly().Get- Name().Version.ToString();
ThisError.ErrDescription = ErrorReason;
if (WarnOrFatal == “Fatal”)
{ThisError.ErrSeverity = 2;}
else
{ThisError.ErrSeverity = 1;}
}

Notice that I define a severity level that allows you, in the calling parent, to decide if you may continue processing with just a warning. Also notice that the SetErrObject logic sets version information using reflection to allow for more detailed debugging by tech support.

Secondly, methods in classes that implement this object should return a boolean to indicate success or failure: a typical invocation then would look like:

public bool ValidateParameters()
{
if (userid.Length < 3)
{
string invalidUser = “User ID ” + userid + ” is Invalid”;
this.SetErrObject(invalidUser, “Fatal”);
return false;
}
// … more validation stuff follows

And the outer call to run the whole thing would look like:

try
{if (!ValidateParameters())
{if (ThisError.ErrSeverity > 1)
{return false;}
}
catch
{throw new Exception(“ValidateParameters Failed!”);}

Notice that we still try-catch the method and bubble up errors along both pathways: any exception from the called method gets thrown upward in the catch clause, and if the error was a business logic failure we return “false” to the user interface.

If your application is running on a server then have the top module send appropriate email notification to tech support. If your app is a stateless service then you should persist the error to a database (along with the session key naturally); furthermore you should provide an additional method to retrieve and send this info to the client side on a subsequent request.

When your application hits an error it is like a sick child that needs some loving attention. The difference between just bailing with a message-box and providing fully nuanced error-handling is like the difference between giving the sick kid some consommé or giving him a nice hot bowl of chunky chicken noodle soup and calling the doctor. Take the time to handle your errors with the same care you would dedicate to your own sick child.


Saturday, May 12, 2012

Artful Culture


In some development efforts, our goal is to deliver software that is "fun." In other words the purpose of the software is to Entertain. In most workplace environments however the goal is to deliver software that helps people get stuff done; in other words the software has Utility.

In actual cultural practice however, most developers strive to make their software a little bit of each: you want to make your software "fun enough" that folks don't get bored out of their skulls when using it while they are getting stuff done. In fact, you generally want to hit a certain sweet spot such that in addition to its Utility, what you produce is:

Culturally Appropriate, Fun, Inexpensive, Courteous, Accurate, Responsive

Unfortunately these "cultural" aspects of development are seldom if ever captured in an Analyst's "Requirements" document. They also fall a bit outside the realm of what IT management might normally consider as platform based "non- functional" requirements.

How does this culture get baked into the software then? Well it usually takes culturally aware developers.

Thursday, February 16, 2012

An Artistic API


Application Programming Interfaces, or APIs as they are commonly called, present interesting design challenges, both from the perspectives of a producer and from the point of view of the chain of consumers.

The immediate consumer is a developer who wants to use a certain service (say for example a geography API). His concern is that he gets consistent results returned in a structure that is both easy to parse, and robust enough to handle a variety of error conditions. The second consumer is the business analyst, who would like any API's that the staff use provide adequate documentation and that subsequent upgrades to the API avoid deprecating functions. The third consumer is a development manager, who has concerns that API use remains in a controlled enough environment that he can hand off development to future staff, as well as avoid vendor lock-in.

The challenge therefore is how to offer powerful APIs that aren't fragile. Some of the design variability involved revolves around the choice of granularity: do you just provide a couple of methods with dozens of properties, or do you provide a dozen methods each having two or three properties?

Ah, so here comes the magic. You see, the consumer who is the developer prefers a small quantity of functions with numerous parameters: once he actually makes the effort to learn them, they are easier for him to remember and the fragility affords him job security. The analyst prefers a middle course, a dozen functions each with four or five properties, as the documentary chunks are more easily absorbed. The manager prefers a hundred functions with a couple properties, as he can develop separate human resources to digest subsets of the entire functionality. So think now: as the developer, who is actually the "buyer" of your API?


Saturday, February 5, 2011

Artistic Coupling


Creating software is similar to making a building but it is also somewhat different. You do need to start with a strong foundation of understanding the business rules and users of the system you are creating. This is usually captured in some graphical modeling. But once you start putting the pieces together the process should feel more like you are doing the interior design of a modular office.

Especially if more than a single developer is doing the programming, you want to leverage the concept of "loose coupling". This is a series of techniques, cautions, and an awareness that enables components to gradually change without breaking the interconnections between them.

One way to do this is to persist intermediate results to a database or file. A great advantage to this approach is that it's easy to mock up test data while you're still developing so that your work doesn't hold up anybody else. This does cause quite a bit of overhead however for high-activity classes or processes.

You can also provide loose coupling by adhering to strict standards in your object-design methodology. The types used on return values for methods and property set and gets should be strictly defined and maintained. If you need to provide more information back from a method than a simple system-type then define your own structure, but avoid using weak data types.

To maintain loose coupling you also need to avoid changing types after you have "published" them, as I described in this earlier post about maintaining modular compatibility.