A site devoted to discussing techniques that promote quality and ethical practices in software development.

Monday, December 6, 2010

Are bookshop ripping us off with eBook?

At the moment I am looking for a couple of technical books and since the fad these days are eBook, I am going to evaluate whether or not eBooks offer real advantages.

Remember, a physical book needs paper, inks, printing press to produce it, bookbinding to bind the pages, warehouse storage facilities and transportation. The costs associated with these processes are not insignificant.

eBook, on the other hand, does not require any one of those processes. In fact, it is rare to produce a physical book without electronic manuscript in one form or another that can readily be converted to eBook format, if not already done so. It is well known in electronic products - be it software or computer games - the reproduction cost is almost zero. I have left out the royalty to the author and merchant's profits. It is not unrealistic to assume that the royalty is the same between eBook and physical book. So any cost savings must be due to the above associated costs to reproduce the physical book.

Hence, eBook should be a lot lot cheaper than physical book. Not quite as the comparisons shown below demonstrate that it in fact has negative economic benefits. The tables below show the book prices without inclusion of handling and shipping cost.

Web Service Contract Design and Versioning for SOA
Bookshop eBook Price eBook Protection Book Price Savings Used Price
Amazon $28.79 DRM $42.89 $14.1 $31.52
Barnes & Nobles N/A N/A $43.99 $0.0 $33.99
infomIT $39.59 Watermarked $49.49 $9.9 N/A

SOA with .NET & Windows Azure: Realizing Service-Orientation with the Microsoft Platform
Bookshop eBook Price eBook Protection Book Price Savings Used Price
Amazon $35.19 DRM $42.89 $7.7 $42.06
Barnes & Nobles $43.99 DRM $46.71 $2.72 $33.05
informIT $39.59 Watermarked $49.49 $9.9 N/A

The savings are not uniform and are from book to book, from bookshop to bookshop, ranging from a pittance of under $3 to $14.

Some physical book, despite all these expensive production & storage costs, is cheaper than eBook. Amazon's price for the second book is cheaper than Barnes & Nobles' eBook version. They must be more expensive 1's and 0's used by Barnes & Nobles than those inks and paper used by Amazon!

Now clearly someone is taking advantage of the latest fad to charge for something that cost zero dollar to produce and reproduce.

The above figures are just pure price comparison and now consider the real economic disadvantages of eBook:
  1. For those using DRM, even the owner cannot print out a diagram or pages after all it is something you have paid for. At least you can run the physical book over on a photo copying machine or scanner. Does that pittance in savings enough to compensate for the lost of freedom to use the things you have bought?
  2. Loss of second hand book market. While the above used book prices are indicative, nonetheless, there is a market for used copies and that you can participate without restriction. All eBooks bar you from that market and their form of compensation is only a pittance. Even allowing a discount of 50% for used copy, you can sell it for more than the saving you can get from eBook.
  3. You can't even share part of the book or form a syndicate to buy a book. At least in a physical book, no one forbids you from tearing the book apart to share it. You can tear out pages to lend to others or to give it away. You have total freedom. But not so with eBook. Amazon generously allows you to have 6 devices but they need your access code to your account. Barnes & Nobles has this lend it facility for only 14 days.
  4. No one can take the physical book away from you as long as you have paid for it. Not so with eBook. Even if the bookshop does not have any rights to sell a physical book, so long as you have paid for it and walked out of their doors, that book is yours and they can't take it away.
  5. How soon does the eBook savings recoup the investment cost of a eBook reader? The only outlay for a physical book is your bookshelf cost if you want to store it that way. The floor is perfectly good free storage space for books.
I have to acknowledge that there are benefits with eBook that physical books are not capable of offering. Such as the ability to carry almost a library-ful of eBook with you,  enhanced searchability, or almost instantaneous delivery and with no delivery charges. Well Amazon does not charge delivery charges for purchase over certain amount to US address.

In my opinion, eBook merchants are definitely taking advantage of current hype and fad to over charge the customers and in my mind, it offers more restriction than physical book and preventing book owner from disposing them in a second hand market, which can return considerable amount to the owner.

In fact, it is not uncommon to find eBook costing more than its hardcover versions. The debate whether eBook should cost so much is raging on the Internet and no end in sight if consumers are willing to pay for the hype & fad.

Sunday, December 5, 2010

Using WCF to produce Web Service Contract documents that must use a supplied schema

This post is to describe a very common scenario in SOA/Web Service world. To avoid chaos in SOA world, practitioners are encouraged to use "Standardization of Service Data Representation" so that services and clients are communicating using standardized or common vocabulary. They may be standards data representations specified within an enterprise or by trade groups like MIMOSA and are not necessary standard endorsed by W3C.

The problem at hand is to produce service contract documents that a system needs to interact with abstractly and the data interchanges must use a standardized or common representation. For ease of discussion, let's assumed the standardized data is from ACME Enterprise and supplied in schema file called ACMEEnterprise.xsd.

One way to do this is to use a process called Contract-First or Schema-First. This gives the designer the maximum control in what to put into the Service Contract. Service Contract, include WSDL and XSD, is not just for machine to execute but also containing information that are useful to service producers and consumers. However, it is not for the faint-hearted; it is only for the most determined soul.

Instead of using Contract-First, in this post, I am describing the necessary steps and settings of using WCF to generate Service Contract documents that use the prescribed data representation. In the frequently described scenarios, the developer is responsible for specifying the data and service contract. But in the problem at hand, much of the data representation or data contract, are predetermined.


Designing a Service Contract using WCF conforming to supplied data representation

As stated previously, the data representation is supplied in ACMEEnterprise.xsd and the targetNamespace is "http://ACMEEnterprise.org/2010/12/ACMEEnterprise.xsd". All data or message exchanges must use types specified in this schema file. The Service Contract many specify other data contracts it sees fit but if it is to describe data for ACME Enterprise, it must use the types specified in ACMEEnterprise.xsd.

Step 1 - Produce the .Net classes

The first step is to convert the types specified in the ACMEEnterprise.xsd into .Net classes that we can use in WCF constructs. For illustration purpose, I use C# but you can use any other .Net languages.

This can be achieved by using XSD.exe or SvcUtil.exe. Most WCF materials will recommend one to use SvcUtil.exe but if the schema is specified using the full set of W3C XSD Schema syntax, the chance that SvcUtil can process your schema file is slim. The reason is that SvcUtil is designed to work with DataContractSerializer which maps the CLR data types to XSD's or vice versa and that the Data Contract model only supports a limited subset of the W3C Schema specifications.

You have a better chance of successful conversion by using XSD.exe provided that you follow the caveat. What CLR namespace you choose to use is immaterial and is not something that will affect the wire format. They are local artifacts affecting only your .Net solutions.

Step 2 - Incorporate the generated file into your project

The next step is to incorporate the generated file into your project and begin using the types in that files to design your Service Contract as if you are dealing with normal WCF DataContract types.

The main thing to note is that each class generated is adored with the XmlTypeAttribute declaring the Namespace corresponding to the targetNamespace in ACME, like this:
[System.Xml.Serialization.XmlTypeAttribute
        (Namespace="http://ACMEEnterprise.org/2010/12/ACMEEnterprise.xsd")]
    [System.Xml.Serialization.XmlRootAttribute("employee",
          Namespace="http://ACMEEnterprise.org/2010/12/ACMEEnterprise.xsd", 
          IsNullable=false)]
    public partial class Employee : Person {
        // . . . 
    }

It is vitally important that this namespace is maintained when we generate the Service Contract documents for this type.

Step 3 - Mark the Service Contract with XmlSerializerFormatAttribute

This is a very important point. You may apply this attribute to only those Contract Operations that require to use XmlSerializer. In my case since every operation is using this serializer, I apply this attribute to the entire service contract. If you do not apply this attribute, types that came from ACMEEnterprise.xsd will be placed under the targetNamespace of "http://schemas.datacontract.org/2004/07/ACME.Enterprise" like this:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://schemas.datacontract.org/2004/07/ACME.Enterprise" 
 elementFormDefault="qualified" 
 targetNamespace="http://schemas.datacontract.org/2004/07/ACME.Enterprise" 
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="Person">
    <xs:sequence>
      <xs:element name="ageField" type="xs:int" />
      <xs:element name="firstNameField" nillable="true" type="xs:string" />
      <xs:element name="genderField" type="tns:Gender" />
      <xs:element name="hobbyField" nillable="true" type="xs:string" />
      <xs:element name="lastNameField" nillable="true" type="xs:string" />
      <xs:element name="secretNumberField" nillable="true" type="xs:long" />
    </xs:sequence>
  </xs:complexType>
This effective produces a different type on the wire. The use of XmlSerializerFormatAttribute retains the original targetNamespace.

Step 4 - Build the WCF Service Library and Generate the Contract documents

After you have finished building the WCF Service Library you can use SvcUtil.exe to produce the Service Contract documents. The process will generate the WSDL as well as the companion XSD files. While this process with the aid of XmlSerializerFormatAttribute preserves the targetNamespace, as shown below, for the types that are used in this service library and placing them in a XSD file resembling the original XSD file, the process is at best of low fidelity. That is the process loses information that are in the original documents deem not needed in WCF. Information such as <annotation>, <documentation>, and <restriction> elements are lost. The schema file only contains a subset of the types described in the original schema; it includes only types used in the service.
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://ACMEEnterprise.org/2010/12/ACMEEnterprise.xsd" 
 elementFormDefault="qualified" 
 targetNamespace="http://ACMEEnterprise.org/2010/12/ACMEEnterprise.xsd" 
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="Person">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="FirstName" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" name="LastName" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="Age" type="xs:int" />
      <xs:element minOccurs="1" maxOccurs="1" name="Gender" type="tns:Gender" />
      <xs:element minOccurs="1" maxOccurs="1" name="SecretNumber" nillable="true" type="xs:long" />
      <xs:element minOccurs="1" maxOccurs="1" name="Hobby" nillable="true" type="xs:string" />
    </xs:sequence>
  </xs:complexType>

You may replace the regenerated schema file for types for ACME Enterprise with the original one without problem so that it contains all the valuable information.

The process described here is a low fidelity process. It does not allow designer to include annotation in the WSDL file. The only way to create a full fidelity Service Contract is to use Contract-First process by authoring the messages and then the WSDL. This will be reported in full post.


"SOA Principle of Service Design" Section 6.3 "Types of Service Contract Standardization"

Wednesday, December 1, 2010

Caveat in using xsd.exe

Here is a trap that many can fall into when using xsd.exe:
1) If your xsd schema file uses <xsd:import> to add multiple schemas from different target namespace into the document, xsd ignores the schemaLocation attribute value.

In this case, you need to specify those imported xsd files on the command line.

2) If the document uses <xsd:include>, xsd uses the schemaLocation attribute value.

While it is great to use the latest and greatest but in many situation, particularly when you are given an XSD authored in other platform, SvcUtil.exe /dcOnly will frequently unable to handle XSD Schema syntax that XSD.exe can handle.

In that situation, alternately you can use the /importXmlTypes (/ixt) and ended up with types that implements IExtensibleDataObject, something that only existing in Microsoft world and may even impact on interoperability, certainly a J2EE Web Service does not have this.

Sunday, November 14, 2010

Beware when using AppDomain.BaseDirectory in NUnit

In my previous post I accused Uri() exhibiting some quirky behavior,
The reason is that depending on whether the base uri for file protocol is your current execution directory, that constructor will drop the last component of your base uri if the current directory is not your base uri  when forming the final uri.
This is indeed a wrong accusation. System.Uri() is functioning correctly in accordance to RFC 3986. For example
Assert.IsTrue( (new Uri( new Uri(@"C:\A\B\C"), "test.txt")).LocalPath,
               @"C:\A\B\test.txt" );
Assert.IsTrue( (new Uri( new Uri(@"C:\A\B\C\"), "test.txt")).LocalPath,
               @"C:\A\B\C\test.txt" );

In normal application, like a console application or WinForm application, the following result is true:
Debug.Assert( AppDomain.CurrentDomain.BaseDirectory.EndWith( @"\" ) );

But when executing the above statement in a TestFixture class in NUnit, the assert fails. This is because when NUnit defines the AppDomainSetup.ApplicationBase, it fails to include a trailing '\'. NUnit simply uses Environment.CurrentDirectory, which does not have a ending '\'. Incidentally, the sample code makes the mistake of assuming the Environment.CurrentDirectory ends with a '\' resulting in forming an incorrect path to the application configuration file.

Normally, this discrepancy does not matter if you use System.IO.Path.Combine() to form path names. However, in the case of when log4net is used inside NUnit it becomes an issue when it uses Uri( Uri, string ) to form the Uri to the log4net configuration file with the value from AppDomain.CurrentDomain.BaseDirectory. The end result is a wrong Uri to the configuration file. To overcome this problem in NUnit, it is therefore wise to specify a full path name for the configuration file in the NUnit's project's configuration file as recommended.

After all these experiments, I am none the wiser what is the official convention for AppDomain.BaseDirectory and Environment.CurrentDirectory? At least in Uri, it is unambiguously defined in the RFC. Perhaps someone reading this from Microsoft can care to comment on this to let the world know if there is such a convention at all.

Saturday, November 13, 2010

Log4Net configuration issue when using with NUnit - Solutions & Explanation

Log4Net is a widely used logging framework and an application has to configure it in order for your instructions, such as where to log your message to, what to ignore, etc, are carried out. However, there are several quirks that one has to be aware, particularly when using it with NUnit. This post uses NUnit version 2.5.7 and log4net version 1.2.10 and hence the comments below are relevant to this version combination.

However, when used in NUnit, report has been filed that it does not generate any log file. In other word, the combination fails to find the required configuration file. The report accused NUnit being the culprit but I can assure readers that it is not entirely. This triggers a number of unnecessary work around.

In this post I will give you a number of solutions first and I will go into detail explanation of what causes this quirky behavior. It is neither party's fault but exposing an issue with how log4net loads the configuration, which is not a fault either.

If you are not interested in the technical discussion, just skip the second section.

Solutions

Solution 1 - No need to write code

I prefer this solution because it fits perfectly into "The Most Beautiful Code I Never Wrote".

In order to use NUnit with your test assemblies, which may or may not contain log4net logging calls, that call into your deploy assemblies, the ones being tested, containing log4net calls, do the following steps:
1) Create a NUnit project config file which usually is in the same directory as your NUnit project file, if not already present.
2) In the config file define the key (in red):
<configuration>
  <appSettings>
    <add key="log4net.Config" 
   value="E:\Libraries Tests\Log4NetDemo\Unit Tests\SimpleLog4net-UnitTest.config"/>
  </appSettings>
</configuration>
The value of that key defines the log4net configuration file that will be used to initialize the default repository.

That is all to it! Nice and simple.

Now when you run your NUnit, all your log4net calls will follow the instructions specified in that configuration file.

It is recommended to use full path name in specifying the configuration file due to some quirky behavior of System.Uri( String, String ) call. This will be disclosed in the Technical Discussion section.

Solution 2 - Use work around

While work around generally works but not very scalable, particularly when you have a collection of test fixture classes and/or assemblies. You need to ensure no matter which test assemblies are called that the configuration file still is used. Here is the recipe of a better work around:
1) Create a class NUnitLog4NetLogManager which has a method called GetLogger( Type type ) and package that inside an helper assembly:
static public class NUnitLog4NetLogManager {
  public static ILog GetLogger( Type type ) {
    return GetLogger( Assembly.GetCallingAssembly(), type );
  }
  // ...
} 

2) NUnitLog4NetLogManager.GetLogger() will use the given assembly object to attempt to retrieve any custom attribute of type log4net.Config.XmlConfiguratorAttribute type. Once found it will then use it to retrieve the config file and watch state. With these pieces of information it will form the full pathname of the configuration file using AppDomain.CurrentDomain.BaseDirectory, if the config file is not a full path.

It then form the System.IO.FileInfo with which it then calls XmlConfigurator.Configure() or XmlConfigurator.ConfigureAndWatch() to load the configuration into log4net.

3) Once step 2 is completed, it then calls LoggerManager.GetLogger(Assembly, Type) to grab the logger and return it to the caller.

This technique allows user to use familiar way of specifying the preferred configuration file like this:
// AssemblyInfo.cs
[assembly: log4net.Config.XmlConfigurator(ConfigFile="MyTest.config")]

and to create the logger in a TextFixture class like this:
// MySimpleTest.cs
[TestFixture]
public class MySimpleTest {
  static ILog log = NUnitLog4NetLogManager.GetLogger( typeof(MySimpleTest) );
  [Test]
  public void SimpleTest() {
  // ....
  }
} 

This has advantage over the work around in that any class included into this assembly can make use of the familiar syntax to get a logger and in doing so indirectly load up the configuration file.

Technical Discussion

Typically, an assembly can include an assembly-level Log4Net custom attribute, XmlConfiguratorAttribute, to specify the configuration information. When a situation that needs to load up the configuration file, log4net internal logic will look for an instance of this custom attribute in a given assembly for the configuration file, etc. This class is located in src\Config\XmlConfiguratorAttribute.cs.

In theory, each assembly can use this to specify different configuration file. log4net does not process or configure its system until the first reference to create a logger or an appender. But in practice the first logger created determines the configuration file used and subsequent calls to create different loggers will not load the config file specified in that assembly.

There is really no need to have separate configuration files as one can control all loggers in one configuration file.

Bear this piece of information in mind will help to understand the issue at hand.

When NUnit is launched and before running your test assemblies, code inside NUnit causes NUnit.Core.Log4NetCapture.StartCapture() to execute. This is an override of its parent abstract class TextCapture. The result is that it causes a the log4net.Appender.TextWriteAppender to be created.

When this happens, some how it triggers a call to log4net.Config.BasicConfigurator() for this appender. This in turn causes the creation of Logger Repository for the assembly nunit.core, which in turns starts looking for custom attributes for repository.

Since these custom attributes are rarely used and nothing is found, it then look for an instance of custom attribute log4net.Config.ConfiguratorAttribute or log4net.Config.XmlConfiguratorAttribute in nunit.core to configure this default repository. Note that it is not your assembly that is used to create the logger repository; it is nunit.core.

If no such custom attribute is found it will then look for a value for the key. If a value is returned, it will then use it to configure this default repository (see code DefaultRepositorySelector.ConfigureRepository(Assembly, ILoggerRepository) in src\Core\DefaultRepositorySelector.cs). The logic will eventually then call XmlConfigurator.Configure() to load and configure the default repository.

If nothing is found, it will simply leave this default repository with its default initialized state.

After all this is completed, NUnit will begin to execute the first TestFixture class it can find, when that happens, assuming the logger is created in a static object initialization style, NUnit will call into log4net to create a logger identified by the given type or string. log4net will grab the calling assembly to begin creation of the logger.

Instead of looking for the custom attribute that you have injected into the AssemblyInfo.cs file, for example, to specify the log4net config file, log4net now has a default repository and it will no need to go through the motion to locate those custom attributes.

The result causes people to believe NUnit or log4net is responsible for not finding their configuration file. This is because they do not realize NUnit is the culprit that causes the creation of a default repository with default initialization state. It is not that log4net can't find your log4net config file, no matter how convenient you have placed it. It is simply that by then log4net does not need to do that.

log4net will then create a logger and stashes it into the default repository, which happens to have the default initialized state, that your logger will use when call upon. It then returns this logger back to your code.

When you call the logger (ILogger) method to log messages, it has found that it is not initialized and hence it will not generate the expected log file, if you use FileAppender for example leading to the submission of the bug report on NUnit.

In reality, perhaps NUnit in version 2.5.7 happens by accident trips the load once issue of log4net as described above and hence when it comes to your test assembly, it simply ignores your directive placed inside the config file.

Using Solution 1 causes the creation of the default repository to use that file to initialize the default repository thereby ensuring the logger will obey your instructions.

With respect to using my solution 1, I mentioned that there is a quirky issue with System.Uri(Uri, string) that makes me to recommend the use of full path name for the value in the key. The reason is that depending on whether the base uri for file protocol is your current execution directory, that constructor will drop the last component of your base uri if the current directory is not your base uri  when forming the final uri. The following code fragment illustrates the issue
// Current directory in MyTestAssembly is 
String curDir = "C:\A\Test\MyTestAssembly\bin\debug";
Debug.Assert( Environment.CurrentDirector==curDir );
Uri x = new Uri( new Uri("C:\A\Test\MyTestAssembly"), "hello.txt" );
Debug.Assert( x.LocalPath == "C:\A\Test\hello.txt" );

In the log4net, it uses AppDomain.CurrentDomain.BaseDirectory as the base Uri and this may or may not be the current directory and this uncertainty is very hard to tell since log4net does not throw any exception when the config file does not exist. For this reason, it is best just to use a full path name for your configuration file and this causes Uri(Uri, string) to ignore the base uri.

The work around specifies a full path to the custom log4net config file to construct the FileInfo object to XmlConfigurator.Configure(). This is not necessary because at that point in time, your current directory is the target directory of your test assembly and FileInfo() will always use current directory to fully qualify the supplied file name if it is not an absolute path name.



[1] "Beautiful Code - Leading Programmers Explain How They Think" Chapter 3 "The Most Beautiful Code I never wrote".

Sunday, October 24, 2010

GMail mishandles e-mail addresses - '.' is insignificant in GMail

Tonight, I received a spam mail sent to A.BCD.HelloWorld@gmail.com and intrigued how it could arrive in the in box of ABCD.HelloWorld@Gmail.com my proper GMail account.

BTW, the above e-mail addresses are fictitious containing only structural information, like the presence or absence of a . to illustrate how GMail mishandling e-mail addresses.

So I did some experiments. I sent an e-mail from my hotmail account to A.BCD.HelloWorld@gmail.com and lo and behold, it arrived in ABCD.HelloWorld@gmail.com.

I did that with several other GMail accounts some with no '.' in the address and I could add as many '.' as I like and they were obediently sent to the address without '.'.

I took my other GMail account like this OneBrownFox@gmail.com and sent e-mail to O.n.e.B.r.o.w.n.F.o.x@gmail.com and without failing it ended up in OneBrownFox@gmail.com.

In other words, GMail tries to guess e-mail addresses and that kind of dangerous practices can increase the SPAM mail you receive. In E-Mail format, the '.' is significant. That is OneBrownFox@gmail.com and One.BrownFox@gmail.com are two distinct e-mail addresses with distinct inboxes. But in the eyes of GMail, they are not.

So far GMail is the only e-mail service that seems to mishandle e-mail address in this manner.

Monday, October 4, 2010

Management never learns

Let's wind the clock back to year 2000, not long after the Y2K had been vindicated as hoax of the millennium, I was working as a senior software developer in a software company called Mincom that has a product called LinkOne in its product suite. At that time I was not associated with this business unit but was being enticed to join it to redevelop to exploit component technology.

Suddenly, the lead developer resigned and next the product manager. The tide continued and eventually the whole team resigned en masse, include the clerical assistant. Leaving behind was a pile of undocumented C/C++ Win32 SDK code. Business Units in Mincom were, and to a large extend still is, very disjointed groups with very little cross pollination of knowledge. In the mean time, the product had a large users base world wide and they obviously weren't happy being left with an unsupported product after heavy investment in entrusting their data to this product. It is not like swapping Microsoft Word with OpenOffice.

The management at that time speedily mounted a rescue mission, which I called Rescue ver 1, to deal with this product. I was one of the two senior engineers ordered in to take charge to rebuild the team and to take control of the product. At that time, Mincom had the luxury in terms of availability of resources to mount an effective rescue mission and the process went smoothly even though it took a long time for the team to get on top of it.

It was not exactly clear what spooked the team so badly that they resigned en masse but one rumor had it that they did not like the management initiative and the pasture was greener outside. Consultative process and listening to staff/developers were not the forte of the company and still is not. Hence this rumor has credence.

Now fast forward to post GFC era in 2008, which by then I have parted company with this business unit for over 4 years, the company used the GFC as an excuse to begin shedding staff giving rounds of redundancy. LinkOne was not treated any differently even though it was pulling in a respectable income for the company.

Once again, people in this team gradually left the company either through redundancy, disenchantment or simply tossed it in before the ship sank. By 2010, the team, including the key architect who transformed the product from a Win32 product to a respectable .Net product, suffered a bout of anorexia reducing the number down to 1 person with a manager! To this observer, it is a case of Deja Vu.

The remaining person was a product support engineer and obviously they desperatly need a 'team'. While keeping the news quiet from their customers trying to prevent the riotous reaction of the first episode, the company tried to mount a rescue, Rescue ver 2, albeit a vain attempt given the company was now depleted of resources; they had easily more managers than developers. It could only mount a vain mission using time-shared resources. The ineffectiveness of this management style has been well documented by DeMarco. Having worked in this product and knowing what resources remained in the organisation, the future looks bleak not only in terms of supporting the product but to enhance it. It is in precarious position because they cannot afford to lose any more.

Perhaps it is their desire to wind up this product without telling the users by natural attrition.

Once again, Management could have stemmed the loss of unquantifiable resource if they are more consultative and treat their staff with respect. There are other well-known cases of people doing the right thing for ending up being sacked when the company is not exactly flushed with resources. Truly a last act of desperate death throe.

When we mounted the ver 1 rescue, we had the luxury of a pool of resources and knowledgeable long term users to guide us and to show us how the product was supposed to perform. Not anymore. All those knowledge has gone out of the window. The only consolation they now have is this team that left was keen methodologists leaving them with a product source code in a much better shape than when I inherited in ver 1 rescue. I wonder how long this will last before it degenerates into a mess as management presses the poor soul to rush out the fix as his time-slice is over spent.

From this vantage point and from my personal involvement, it is a case of management never learns and never knows how to manage. I am wondering if the loss can be translated into an entry in their profit and loss statement, will their board of directors or share holders still take such a quiet sheepish position.

Blog Archive