NFop (ApacheFop.Net) with J# in .NET 4.0 and DotNetNuke 6

Recently we had a client that wanted to upgrade their web application containing modules we had written for them a long time ago for DotNetNuke 4.3 and they hadn’t had any upgrades since.  Fortunately the content on the site was fairly minimal so we decided just to set up a new instance of DNN 6 and move the content over.  This means we had to take modules written for DotNetNuke 4.3 all the way to DotNetNuke 6.0.  For the most part it really wasn’t too bad.  There were some issues with ajax and script manager as DNN 4.3 pre-dated DNN’s inclusion of a scriptmanager automatically, but the biggest problem was ApacheFop.Net and J#.

The original developers of the modules used ApacheFop.Net for the PDF generation for all reports.

Step 0:

First I had to install the J# redistributable.

Step 1:

After doing that I moved over assembly entries from the 4.3 web config:

<assemblies>
    <add assembly="vjscor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
    <add assembly="vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
</assemblies>

Step 2:

This got close.  Now when trying to generate a report I got the following error:

Could not load file or assembly ‘vjscor, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies.

Okay, well, then let’s use a binding redirect in the assembly binding section of the runtime of the web.config.  I’m not sure why it was trying to load 1.0.5000.0 when I told it to load 2.0.0.0, but we’ll correct that:

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="bin;bin\HttpModules;bin\Providers;bin\Modules;bin\Support;"/>
        <dependentAssembly>
            <assemblyIdentity name="vjslib" publicKeyToken="b03f5f7f11d50a3a"/>
            <bindingRedirect oldVersion="1.0.0.0-1.0.5000.0" newVersion="2.0.0.0"/>
        </dependentAssembly>
        <dependentAssembly>
            <assemblyIdentity name="vjsnativ" publicKeyToken="b03f5f7f11d50a3a"/>
            <bindingRedirect oldVersion="1.0.0.0-1.0.5000.0" newVersion="2.0.0.0"/>
        </dependentAssembly>
        <dependentAssembly>
            <assemblyIdentity name="vjscor" publicKeyToken="b03f5f7f11d50a3a"/>
            <bindingRedirect oldVersion="1.0.0.0-1.0.5000.0" newVersion="2.0.0.0"/>
        </dependentAssembly>
    </assemblyBinding>
</runtime>

Step 3:

One step closer.  Now I get the error:

A critical error has occurred.The type initializer for ‘java.lang.System’ threw an exception.

Well, at least it looks like it’s trying to load the J# libraries.

Finally after much googling around I finally hit upon the last step in the enigma at

http://blogs.windwardreports.com/davidt/2011/02/calling-j-code-from-net-40.html

I added to my Default.aspx the external call for manually loading libraries:

//used for loading J# libraries
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibrary(string lpFileName);

and then to the OnInit I added:

//load needed J# libraries
if (Environment.Version.Major >= 4)
{
    string folder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), @"..\Microsoft.NET\Framework\v2.0.50727");
    folder = Path.GetFullPath(folder);
    LoadLibrary(Path.Combine(folder, "vjsnativ.dll"));
    LoadLibrary(Path.Combine(folder, "vjscor.dll"));
    LoadLibrary(Path.Combine(folder, "vjslib.dll"));
}

I tried removing each step and the only step that you can do without is step one since we’re manually loading the libraries manually anyways.  Oddly, even though I’m now manually loading the libraries, I still need to do step two. Oh well, this is what works for me but as always YMMV.

 

Thanks,
Brian

Share and Enjoy:

  • Facebook
  • HackerNews

Often Unused Operators: fixed()

This post is inspired by Eric Clippert’s post on References and Pointers.

I think it escapes a lot of C# devs that they have the same pointer abilities as C/C++, or maybe it’s that those features have been intentionally down-played by Microsoft for a reason. As C# devs we’ve come to rely on nice, safe and managed code and GC. That doesn’t mean we can’t cause an out of memory exception or infinite recursion or some other bad thing along the way, but for the most part C# tries to protect us. While I’m not going to cover pointers (something of which I have a fondness for given that my very first college class was “Fundamentals of C”) I do want to go over the fixed() operation.

Fixed is basically telling the GC, “Don’t move me, don’t touch me, don’t GC me, don’t even look at me” which is why fixed can only be used within an unsafe context. It’s important to mention that unsafe means, um, unsafe. When you identify an area of code as unsafe you’re saying, “Hey, I know I’m doing something that could potientially be really bad and all the work MS has done to try and protect me I’m ignoring. I really, really promise to only use smart code here.”

Here’s a bit of code using fixed:

unsafe void FixedExample()
{
    double[] arr = { 0, 1.5, 3, 4.5, 6, 7.5, 9 };
    fixed(double* p1 = &arr[1])
    {
        fixed (double* p2 = &arr[5])
        {
            int diff = (int)(p2 - p1);
            Debug.Print(diff.ToString());
        }
    }
}
unsafe void FixedExample2()
{
    double[] array =  { 0, 1.5, 3, 4.5, 6, 7.5, 9 };
    fixed (double* arr = array)
    {
        double* p1 = &arr[1];
        double* p2 = &arr[3];
        int diff = (int)(p2 - p1);
        Debug.Print(diff.ToString());
    }
}

Basically, fixed allows you to get pointers to managed types which would otherwise be subjected to GC. If you were to work just with pointers and not managed types you can do the above (using stackalloc) without even needing fixed.

unsafe void StackallocExample()
{
    int arraySize = 7;
    double* arr = stackalloc double[arraySize];
    double j = 0;
    for (int i = 0; i < arraySize; i++, j += 1.5)
    {
        arr[i] = j;
    }

    double* p1 = &arr[1];
    double* p2 = &arr[15];
    int diff = (int)(p2 - p1);
    Debug.Print(diff.ToString());
}

but of course the above code is very bad. If you didn't notice, the second pointer points to the 16th element (since we're obviously 0 based) but I only allocated 7 doubles worth of memory. If I were do try and do something with arr[15] (rather then just determine the distance between the two elements) that would be very bad as it wouldn't represent a value I had intended and would just be some random bit of whatever was stored in that memory location. Interestingly enough, if you try the same in the first two examples you'll get a runtime exception.

Thanks,
Brian

Share and Enjoy:

  • Facebook
  • HackerNews

The Task Parallel Library and System.Collections.Concurrent Namespace

Arguably one of the biggest parts of .NET 4.0 was the Task Parallel Library (TPL). The TPL makes it ridiculously easy to parallelize code. With Parallel.ForEach, Parallel.For, Parallel.Invoke, and Task.Factory developing for today’s multi-core systems should be a matter of fact.

Recently I was tasked with optimizing a large code base to utilize the TPL. The first problem? List and Dictionary aren’t thread safe. That’s right, two of the most common data structures aren’t thread safe. Fortunately we’ve been provided with alternatives that are thread safe in the System.Collections.Concurrent Namespace.

The biggest difference I’ve seen between using List and using one of the alternatives, ConcurrentBag is that you can’t access items in a ConcurrentBag via an indexer. The truth, however, is that alternative methods are provided via LINQ extension methods.

Consider:

ConcurrentBag<int> myBag = new ConcurrentBag<int>();

var task1 = Task.Factory.StartNew(() =>
    {
        LongRunningMethod();
        myBag.Add(1);
    });

var task2 = Task.Factory.StartNew(() =>
    {
        LongRunningMethod();
        myBag.Add(2);
    });

var task3 = Task.Factory.StartNew(() =>
    {
        LongRunningMethod();
        myBag.Add(3);
    });

Task.WaitAll(task1, task2, task3);
Parallel.For(0, myBag.Count, i =>
{
    Debug.Print(myBag.ElementAt(i).ToString());
});

List<int> myList = new List<int>();
LongRunningMethod();
myList.Add(1);
LongRunningMethod();
myList.Add(2);
LongRunningMethod();
myList.Add(3);
for (int i = 0; i < myList.Count; i++)
{
    Debug.Print(myList[i].ToString());
}

resulted in the output:

1
3
2
1
2
3

with, obviously, the parallel methods being faster.

The other problem we ran into is manipulation of visuals. As I’m sure you know manipulation of visuals is required to be on the main thread. Otherwise you start getting all sorts of permission issues. The minute you start having to move doing things on the main thread you lose the advantages you gained by moving towards all this parallelism. Future projects will move towards the MVVM pattern but past projects we’ll have to work on what we can.

Share and Enjoy:

  • Facebook
  • HackerNews

String comparisons

At one of the blogs I most frequent there was a post asking, “Where is the bug?”. In the comments section it was mentioned,

Use ToUpperInvariant rather than ToLowerInvariant when normalizing strings for comparison.

Sure enough if you go to the Microsoft page on it, it says

Use the String.ToUpperInvariant method instead of the String.ToLowerInvariant method when you normalize strings for comparison

Not wanting to blindly trust “Teh MAN” I threw together a quick testbed for testing many of the variants on doing string comparisons.

Here are the results (running 1 million comparisons) comparing
Guid.NewGuid().ToString() to “Now is the time for all good men to come to the aid of their country.”:

Method Time
equals operator 10 milliseconds
equals method 8 milliseconds
CompareOrdinal(ToUpper) 436 milliseconds
CompareOrdinal(ToUpperInvariant) 876 milliseconds
CompareOrdinal(ToLower) 418 milliseconds
CompareOrdinal(ToLowerInvariant) 878 milliseconds
Compare(OrdinalIgnoreCase) 29 milliseconds
Compare(InvariantCultureIgnoreCase) 113 milliseconds
Compare(CurrentCultureIgnoreCase) 135 milliseconds
Compare(ToUpperInvariant, Ordinal) 889 milliseconds
Compare(ToLowerInvariant, Ordinal) 889 milliseconds


If you read through Microsoft’s whole article you’ll see for case-insensitive ordinal comparisons they recommend

String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase)

As you can see in the results this is borne out. It’s not inconceivable that these results could be significant, especially when doing a significant number of string comparisons.

The problem arises when I change what’s being compared to a string that is almost identical to the original.

Here are the results (running 1 million comparisons) comparing
“Now is the time for all good men to come to the aid of their countrt.” to
“Now is the time for all good men to come to the aid of their country.”:

Method Time
equals operator 37 milliseconds
equals method 36 milliseconds
CompareOrdinal(ToUpper) 502 milliseconds
CompareOrdinal(ToUpperInvariant) 1111 milliseconds
CompareOrdinal(ToLower) 494 milliseconds
CompareOrdinal(ToLowerInvariant) 1120 milliseconds
Compare(OrdinalIgnoreCase) 369 milliseconds
Compare(InvariantCultureIgnoreCase) 176 milliseconds
Compare(CurrentCultureIgnoreCase) 191 milliseconds
Compare(ToUpperInvariant, Ordinal) 1096 milliseconds
Compare(ToLowerInvariant, Ordinal) 1099 milliseconds


In both cases I ran the test (with code below this) several times and each time it gave similar results.

So what does this all mean? If you’re only doing a few thousand comparisons where case is an issue I wouldn’t worry about anything and just keep doing what you’re doing.
Other then that I can only recommend staying with Microsoft’s suggestion. While there was significant differences between the results, in general Microsoft’s suggestion would be more applicable without some sort of explicit knowledge of the data. I think if you’re going to analyze much beyond this you risk attempting to over-optimize.

Thanks,
Brian

string myStringToCompare = "Now is the time for all good men to come to the aid of their countrt.";// Guid.NewGuid().ToString();
string originalString = "Now is the time for all good men to come to the aid of their country.";
string results = "";

Stopwatch watch = new Stopwatch();

int numComparisons = 1000000;
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (myStringToCompare == originalString) { }
}
watch.Stop();
results += "equals operator:" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (myStringToCompare.Equals(originalString)) { }
}
watch.Stop();
results += "\nequals method:" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.CompareOrdinal(myStringToCompare.ToUpper(), originalString.ToUpper()) == 0) { }
}
watch.Stop();
results += "\nCompareOrdinal(ToUpper):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.CompareOrdinal(myStringToCompare.ToUpperInvariant(), originalString.ToUpperInvariant()) == 0) { }
}
watch.Stop();
results += "\nCompareOrdinal(ToUpperInvariant):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.CompareOrdinal(myStringToCompare.ToLower(), originalString.ToLower()) == 0) { }
}
watch.Stop();
results += "\nCompareOrdinal(ToLower):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.CompareOrdinal(myStringToCompare.ToLowerInvariant(), originalString.ToLowerInvariant()) == 0) { }
}
watch.Stop();
results += "\nCompareOrdinal(ToLowerInvariant):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.Compare(myStringToCompare, originalString, StringComparison.OrdinalIgnoreCase) == 0) { }
}
watch.Stop();
results += "\nCompare(OrdinalIgnoreCase):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.Compare(myStringToCompare, originalString, StringComparison.InvariantCultureIgnoreCase) == 0) { }
}
watch.Stop();
results += "\nCompare(InvariantCultureIgnoreCase):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.Compare(myStringToCompare, originalString, StringComparison.CurrentCultureIgnoreCase) == 0) { }
}
watch.Stop();
results += "\nCompare(CurrentCultureIgnoreCase):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.Compare(myStringToCompare.ToUpperInvariant(), originalString.ToUpperInvariant(), StringComparison.Ordinal) == 0) { }
}
watch.Stop();
results += "\nCompare(ToUpperInvariant, Ordinal):" + (watch.ElapsedMilliseconds) + " milliseconds";

watch.Reset();
watch.Start();
for (int i = 0; i < numComparisons; i++)
{
    if (string.Compare(myStringToCompare.ToLowerInvariant(), originalString.ToLowerInvariant(), StringComparison.Ordinal) == 0) { }
}
watch.Stop();
results += "\nCompare(ToLowerInvariant, Ordinal):" + (watch.ElapsedMilliseconds) + " milliseconds";

txtResults.Text = results;
Share and Enjoy:

  • Facebook
  • HackerNews

netTiers and stored procedures

Want to see what netTiers sees when it determines what return type should be for a stored procedure?

It uses FMTONLY ON which returns the metadata on the results if the stored procedure was run.

netTiers bases return type from stored procedures based on the results of running FMTONLY ON with the stored procedure passing null to all values. The problem with this approach is that all branches are run. This means if you have an if/else where two selects are run you will get a DataSet return type, even if both branches return all the fields needed for the base type. This is because two sets of data is returned.
So, if you want to see what netTiers sees try:

SET FMTONLY ON;
EXEC [dbo].[_Plan_GetByUserId] null
SET FMTONLY OFF;

where _Plan_GetByUserId is your stored procedure and all parameters that are passed in are null.

Some of the other problems with this is that when the stored proc is run between FMTONLY the permissions it has to run are severely restricted because it is run as a user with the membership public. You can’t create temp tables due to this permissions restriction as well as other issues. Additionally you’ll get a void method for the get if your stored proc errors out on one of the parameters being null.

Share and Enjoy:

  • Facebook
  • HackerNews

Recursive Common Table Expressions in SQL Server for working with a hiearchy

Just wanted to throw in a quick write up about common table expressions (CTE) in sql server.

There are plenty of good and complete write-ups on ctes floating around teh tubes these days but I wanted to address my paticular issue, if nothing else so I don’t forget about them.

Imagine you have a hiearchy of units (like military units) though this same thing can apply to other cases like attributes or types. Working with some made-up data you may have:

5th Division
    9th Brigade
        17th Regiment
        21st Regiment
    12 Brigade
        54th Regiment
        88th Regiment

I have a case where a user will be assigned to just one level of these units but being assigned to one level means that all child units also apply to the user.

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[_Unit_GetByUserId]
(
    @UserId [uniqueidentifier]
)
AS

    with c as (
        select b.* from Unit b, UserUnit u
            where u.UserId = @UserId
            and b.Id = u.UnitId
        union all
        select b.* from Unit b
            join c on b.ParentId = c.Id
            where b.Id != b.ParentId
    )

    select * from c

This allows me to get all the units that apply to the user. In this case, if a user is assigned to the 9th Brigade then the stored proc will return: 9th Brigade, 17th Regiment, 21st Regiment

The key is in the union where the join to the CTE is and the ParentId is being set to the Id coming from the CTE.

I realize there isn’t much explanation here as to what is happening but if you follow the links above you’ll get a better explanation then I could give.

Share and Enjoy:

  • Facebook
  • HackerNews

WP7, ApplicationBar, ImageUri and the Icon not showing up

I know this may sound stupid but when working with the WP7 ApplicationBar make sure the images you are using have the following properties:

Build Action: Content
Build Action: Content
Copy to Output: Copy always (or ‘Copy if newer’, either should work)

I spent a half an hour trying to figure out why my application bar was showing an “X” instead of the image I was putting in IconUri. When first adding images the properties default to:

Build Action: Resource
Copy to Output: Do not copy

Since the application bar is referencing the image by Uri it can’t seem to find it embedded in the application.

My xaml looks like:

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton
            IconUri="/Images/appbar.feature.settings.rest.png"
            Text="Settings"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
Share and Enjoy:

  • Facebook
  • HackerNews

Upgrading/Migrating to .net 4.0, two gotcha’s I found

Recently while upgrading a large project to .net 4.0 we ran into two problems. The first was simply that none of our xaml dictionaries were styling anything. The second was that all of our images looked horrible. These two problems aren’t mentioned anywhere by Microsoft in their guide to migrating to .net 4.0.

The first problem relates to merged dictionaries within a dictionary. In our project we have a master dictionary in a common project. All other projects call this common project. The common project contains only the xaml resource dictionaries. Our main dictionary looks kind of like:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="ComboBoxStyle.xaml"></ResourceDictionary>
        <ResourceDictionary Source="ButtonStyle.xaml"></ResourceDictionary>
   </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Virtually every element in our application is styled via this dictionary. This creates a clean set of styles in seperate files that makes maintenance of these really simple. The problem is that in .net 4.0 none of the styles in these dictionaries were being applied. It seemed like .net 4.0 just wouldn’t go down into the resource dictionaries and get the proper styles. After posting a question in the msdn forums I took a look around Connect (Microsoft’s listing of bugs) and found that someone else had the same problem.

Fortunately one of the MSDN moderators came to my rescue and posted a workaround. Oddly enough all you have to do is include an empty style after the merged dictionary and everything starts working again. Our new master resource dictionary looks something like:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="ComboBoxStyle.xaml"></ResourceDictionary>
        <ResourceDictionary Source="ButtonStyle.xaml"></ResourceDictionary>
   </ResourceDictionary.MergedDictionaries>
   <Style TargetType="{x:Type Window}"/>
</ResourceDictionary>

and now all cascading styles are being applied correctly.

The second problem has to do resizing and scaling of images. Once again I started with a post on MSDN forums and then went and took a look around connect (I really should begin starting at Connect). It seems that in .net 4.0 in order to increase the effeciency of everything they changed the default rendering of images from Fant (extremely high quality) to Low quality. Not finding any easy solutions I simply changed our master resource dictionary from as show above to:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="ComboBoxStyle.xaml"></ResourceDictionary>
        <ResourceDictionary Source="ButtonStyle.xaml"></ResourceDictionary>
   </ResourceDictionary.MergedDictionaries>
   <Style TargetType="{x:Type Window}"/>
       <!--Fix for images rendering in low quality-->
   <Style TargetType="{x:Type Image}">
      <Setter Property="RenderOptions.BitmapScalingMode" Value="HighQuality"/>
   </Style>
</ResourceDictionary>

so that images would always defualt to rendering in High Quality. After I created this solution someone posted as a workaround in Connect just to add:

RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.HighQuality);

to the constructor of your main window and this will inherit to all images by default. I haven’t tried this solution and I’m not sure what would happen if you have projects other than the main project that open their own windows. It would seem like this should be a global setting since you’re setting the static in RenderOptions and this should work fine. For us this was unneeded since every window in all our projects calls the xaml shown above.

Well, if we run into anything else I’ll post it here but these were the only two issues we’ve found so far. It’s been a big benefit to upgrade for us to .net 4.0 if for no other reason then to have Parallel.For and Parallel.ForEach.

Thanks,
Brian

Share and Enjoy:

  • Facebook
  • HackerNews

Programming for Maintenance

Code Complete defines Maintainability as “The ease with which you can modify a software system to change or add capabilities, improve performance, or correct defects.”

I’ve been thinking for awhile now about this post and I’ve been unsure of exactly what needs to be here.  I know I’ve done a lot of maintenance work.  It’s inevitable.  You name the field and chances are we’ve developed something for it.  A lot of the software we develop we do so because there just isn’t commercial off the shelf (COTS) software for the business process.  COTS is for the broad market.  So customers come to us when COTS doesn’t meet their needs.

Which gets us to maintenance.  We try the best we can but in any software application there will be bugs.  It is inevitable, it is a fact of life.  Unless you are just writing a “Hello World” program there will be people who use your product in ways you didn’t expect.  We have had clients who take the application we’ve written for them and walk away.  Not because they’re unhappy but because the cost to fix every single bug and add in every feature they want can often be prohibitive.  They’re happy where the app is and that’s good enough.

Most clients stay with us for a maintenance tail.  They prioritize the bugs they want fixed and features they want added and budget accordingly.  We want happy clients, it’s that simple.  The happier they are with us the more work they bring to us.  This is common across all businesses, whether you’re a dry-cleaner or doing software services, a happy client is good.

So in software services how do you make a client happy?  In my experience it’s been minimizing maintenance tail costs.  But how do you do this?  I’m going to try and sum up where I have noticed problems.  I could reprint Code Complete here and say, “There, that’s how you do it” but that would be meaningless.  The point of this post is to focus on a few key points.

1.  The obvious:  Requirements, requirements, requirements.
As much as possible try to impress upon your clients the importance of requirements.  The more requirements analysis and design that is done up front before even beginning development the cheaper the maintenance tail.  It’s fact the earlier you find a problem the cheaper it is to fix. (Code Complete, Table 3-1, Average Cost of Fixing Defects Based on When They’re Introduced and Detected)

The biggest problem with this?  Most of the time clients don’t know what they want.  That’s right, they come to us and say, “Well, I want an application that does this.”  But what does that mean?  Does this need to mirror an existing business process?  Is it something completely new?  Do you have an idea of how the process should flow?  Do you know where you get your data and where it should go?  What kind of reporting are you going to need? (Customers never know that last one)

A lot of time we end up building up an existing business process in order to understand what needs to be developed for the application.  That’s okay.  I can accept that business analysis may be a part of the job.  So this is key:  (most of the time)  Clients don’t know what they want.  They will usually have a generally vague idea but you will have to help them.  Try to come up with a basic set of requirements.  Next, at a minimum, whip out Visio (there are better products out there but Visio is probably the most common) and throw together a few pages of the app.  Put together some sort of flow.  Help them bring their vision to reality.  Only then will they really begin to understand their own requirements and process.  Only then will they see what’s there and what’s missing.  Most clients can’t envision software.  They’re paying money for something that is completely virtual and just can’t see the final product.

Now, this is all well and good.  The problem is that a lot of clients don’t want to spend several weeks (or longer) designing the app, they want to get moving on it.  This leads to iterative development.  It’s more expensive.  But done properly I’ve still had happy clients.  Prioritize parts of the application.  Model a couple of those pages, build it, move on.  You will have problems.  I had one client who missed telling us about a key part of data for the app.  The ERD we had didn’t support this new data so we had to build out some new tables and fix everything.  This was after we’d done requirements and modeled the pages.  It wasn’t until the app was in beta that he went, “Oh, and you need to put this in there.”

2.  Naming Conventions: Use it, love it
This is going to sound stupid but one of the biggest problems I’ve had coming in to maintain an app I didn’t design or build is naming conventions.  Even if the names are consistent and meaningful, when there is no standard across an application where multiple developers have been working on it, it takes way too long to understand everything.  I’m not saying that your company should enact a strict naming convention company wide.  That would be nice but it’s not reality.  Your project lead, however, should.  Even if you’re working under different project leads you need to adapt.  This is especially true in this polyglot world of development.  At one time I was working on a PHP project, a java project and a C# project.  Each has their own naming conventions.  Yes, it took longer when I had to switch projects to re-orient myself but that in and of itself is the nature of polyglot programming.  I know that when someone comes behind me to maintain the app (and in a lot of cases it’s even me) it is a hell of a lot easier to get going on maintenance when there is some sort of naming convention across the app.

So what do I mean by naming conventions?  Method names, event and delegate names, variable names.  If it takes a name there needs to be a standard.  Whether it’s public, private, static, constant, an enum or class, there needs to be a standard.  The only place I haven’t seen a need for naming conventions is internal to methods.  If the app is coded right and adhering to basic OOP methodology variables internal to a method shouldn’t be around that long.  They still should be meaningful but I don’t care if you want to put an underscore in front of your internal variables or not.  I don’t care if you want to capitalize them or not.  Anytime I have to walk through from class to method to class to method to delegate to method there better be some sort of convention.  Otherwise it costs the client money.  There will always be some sort of spin-up when entering maintenance.  Just try and minimize it.

So you’ve got your requirements and naming conventions, what’s next?

3.  OOP, if you use it, use it
The biggest trouble I’ve had, excepting those mentioned above, are projects where developers seemed to take a procedural approach to OOP.  God classes and methods (functions?), poor use of inheritance and virtually no use of interfaces.  This makes maintenance tough.  I have to admit I myself was bit lacking in this area out of college.  Sure I took an OOP class, sure I had the theory but applying the theory is wholly different then having the theory.  And I’m not the only one.  It seems one of the universal constants having new engineers on my team fresh out of college is that they can’t apply OOP.  This often also applies to self-taught programmers who many times don’t even have the theory.

I want a black box (Code Complete, Encapsulate Implementation Details, Information Hiding).  When I step into a method during troubleshooting I want to only go into that method once.  There after I want to trust that the method works as intended.  I want a method to only do one thing and not affect parts outside of itself.  If I have a login control it should only be doing login.  I want a class to default all properties (getters/setters) to private.  I don’t want those variables being changed outside the class unless they need to be.  I want interfaces(Code Complete, Form Consistent Abstractions).  It seems like this is the least understood by programmers straight out of college.  So often new engineers like to inherit and override.  Obviously this has it’s time and place.  But think if this really is needed or if you just need to create an interface (Code Complete, Inherit-When Inheritance Simplifies the Design).

I digress, I’m starting to write out Code Complete.  If nothing else, use and apply Code Complete, 5.3 Design Building Blocks: Heuristics.

So how to fix this?  If you have an engineer on your team that you’re not familiar with do a code review.  Assign him a task and take a look at his code.  I don’t think I carry the title Senior Software Engineer because I such a great programmer.  Really I barely consider myself adequate.  I have seen a lot of code, written a lot myself, I lead teams pretty well as a project lead and clients like to work with me.  If you’re leading a project remember the code of the engineers underneath you is your responsibility.  Yeah, you might ruffle some feathers.  We programmers take our code personally.  We’re proud of it.  No different than an architect whose proud of a house he’s built or a chef whose proud of his food.

4.  Stay up-to-date
I won’t harp on this long because I’ve said this before.  Programmers use what’s available to them.  I love LINQ, but I know LINQ because I read about it before it was even released publicly.  I can do a lot of stuff easier with LINQ.  I love WPF, but I know WPF because I read about it before it was even released publicly.  I can do a lot of stuff easier with WPF then I could with WinForms.  If you don’t stay current there’s a good chance you’ll jump into a project where it’s using something you don’t understand.

5.  Code to maintain
I suppose this is really the heart of the issue.  What the hell does this mean?  Any adequate programmer should be able to jump into a piece of code and start working on bugs and adding features.  I’m not saying there won’t be a spin-up.  Of course there is going to be some spin-up.  But try and minimize that.  When most people are working on code they’re not thinking that in a year or more someone is going to have to come behind them and read the same code trying to figure out what they were doing.  But they should be.

Finally, I want happy clients.  That means minimizing maintenance.  There will always be maintenance, it’s inevitable.  But when I can jump into the maintenance tail of an project faster, fix bugs faster, implement new features faster than clients are happier and my job is easier.  And I like it when my job is easier.  It will always be challenging but I prefer challenging and fun to challenging and a pain in my ass.

I could keep going.  Someone could write a book on the subject (yeah, I know, Code Complete).  This post is just intended to hit a few top issues I’ve seen when maintaining code. YMMV.  You may have seen other issues that top this list.  Tell me about them.  Post a comment.

Later,
Brian

Share and Enjoy:

  • Facebook
  • HackerNews

Development for iPhone and Android with Mono

Okay, for the first time ever I’m a bit jealous of iPhone users.(See below)  Novel (the sponsor of Mono) has released the MonoTouch, a C# and .NET development platform for the iPhone.  Granted it’s $400 for the platform but at least it’s not Objective C.  For those of you that don’t know Mono, it’s a platform agnostic port of the .NET libraries, stable at about the equivalent of .NET 3.0 (which means LINQ and a bunch of other cool stuff).  That means, yes, if you want to you can do ASP.NET hosting on apache and linux.  Novell has long been a sponsor of mono and it seems like they’re going to get the opportunity to make some money back with the release of MonoTouch.  Of course you still need to have a Mac and be part of Apple’s iPhone Developer Program which is enough to kill it for me.

See
http://monotouch.net/

For those of us with an Android phone, Mono is running in Android, it’s just not very stable.  Here is a post of one of the Mono developers that compiled the Mono libraries from his Android from a debian install on his SD card.  Yes, in Android he’s running debian to compile Mono.  Is that not just sweet nerdy goodness?  While there is no time line for a full stable release of Mono on Android it’s in active development and, unlike the iPhone/MonoTouch option, it probably won’t cost a thing once it gets stable.

So what does this mean for us doing smart phone development?  When I was in college I applied for and received a grant for a platform agnostic project, written in java, to do streaming media, web cam and audio between multiple PCs.  For the first month of the project I developed on linux (don’t remember the branch) with Java.  I moved all the code to Windows and came across serious threading issues.  I fixed all those and spent the next month developing in Windows.  After that second month I moved everything back to linux and ran across serious threading issues.  For the last month of the project I compiled to a shared directory and tested on both linux and Windows.  I highly doubt there will ever be a stable environment that will work for all platforms, hardware is just too different.  MonoTouch has specific interfaces to the iPhone APIs.  Once Mono for Android gets stable I’m sure there are going to be wrappers for the Java APIs for Android.  But is this a bad thing?  Isn’t competition good?

Later,
Brian

Share and Enjoy:

  • Facebook
  • HackerNews
←Older