Rob Gray

  Home  |   Contact  |   Syndication    |   Login
  5 Posts | 0 Stories | 1 Comments | 0 Trackbacks

News

Twitter












Archives

Thursday, March 12, 2009 #

Under most models of software development of any significant scale, user sign-off of requirements is a must before the development can continue.  Even agile methodologies that involve a customer representative in the development team needs to have a set of at least significantly complete requirements before development can begin.

 

Requirements form the basis for generating a Tender or Quote and help generate a Statement of Works.  Detailed requirements are often elicited during contract negotiations and once established, a fixed price can be agreed (if that is the model being used). Requirements sign-off is all important in formally contracting what needs to be provided in any software project deliverable.  It is the signed requirements document that is brought out when there is confusion about items being in or out of scope, and determining if a product has been completed. 

 

A hopefully typical contract stipulates that software is “done” when all Acceptance Tests have passed.   Although our contracts often stipulate that once all sites are Live the software has been accepted and the project is “done”, though in my experience this is painful because reasons for not going Live are quite often more political than technical or caused by any software defect, and it’s these situations where Acceptence Test success would greatly speed up the “Done” state of a project (and the “paid” state of the Invoice).

 

My company traditionally produces an “Operational Scenarios Document” (OSD) as the document indicating the requirements and to be signed off the customer.  The OSD is simply a document containing all the user stories or use cases of the system to be built.  I believe this presents a more understandable view of the system to the customer, as it’s written in their language.  From the OSD Analysts produce a more engineering oriented “Software Requirements Document” (SRD), where the OSD is broken down into hundreds and thousands of numbers system requirements.

The problem with this approach is the OSD only captures user facing actions such as steps in a business process.  It is up to the SRD to capture the more fine grained requirements, such as “Patient information shall include a valid Medicare number in the format XXXX-XXXX-XXXX X”.  As such, we’ve captured the necessary requirement, but there’s nothing to formal (read “legal”) to cover either us or the customer.  Generally this isn’t a problem, but we’ve probably all experienced scope-creep and ambiguous (signed) requirements are the genesis of scope creep.

Enter signed Acceptance Tests and Acceptance Test Driven Development (TDD at a higher abstraction).  Acceptance Tests define exactly what the system has to do before the customer will consider it “Done”.  Acceptance Tests don’t just include functional requirements; they also test the non-functional stuff like performance, security, and useability.  If functionally is not covered by the Acceptance Tests, it’s not required by the customer.

 

Now, I’m not saying that no software project should be undertaken before Acceptance Tests are supplied.  That’s clearly unworkable in all but the situations where the customer team has experienced analysts, with a good understanding of the software development process, on board.  What I do think is that an analyst or developer should always be mindful of Acceptance Tests when eliciting requirements from customers during workshops.  Questions like “What are the steps you take when placing an order” are not enough.  “What state will the order be in once it has been successfully placed?”, or “What happens if an order is placed and there is not enough inventory to fulfil it?” are necessary. Hopefully these are already questions that are being asked as part of the user story generation. 

The differences between Acceptance Tests and Use Cases (or OSD’s) are small but significant.  Where an OSD is used by the customer to accept that you understand the requirements of the software under design, Acceptance Tests are used by customer to determine if you supplied what they asked for.  Both OSD and Acceptance Test Document can be generated from the same information at the same time, so it makes sense to generate them at the same time and include both in any formal signed requirements.

 

From a development perspective, you can use user stories/user cases/operation scenarios in tandem with acceptance tests to define the use cases to be developed in each iteration, producing software that is of business use after iteration one and the customer can hook into the feedback loop early in a project.

 

Critically, Acceptance Tests are generally customer supplied artefacts and as such, you as a developer have a higher level of confidence that developing a system to meet Acceptance Tests will result in a successful project.


Thursday, January 29, 2009 #

Scenario/History

There is a bug in our Premise Utility that causes streetid’s to be set to 0 whenever the an existing premise record is saved.  StreetId’s are integral to accurate location information when dispatching ambulances, so getting a fix done was high on the agenda.  The problem is that for whatever reason the bug cannot immediately be fixed in the Premise Utility.  I am instead creating a small app that will be notified when a premise record change an perform a reverse geo code on the supplied latitude and longitude to obtain a streetid and update the premise record.

 

What did I do?

The existing .NET Reverse GeoCode component inherits from System.Windows.Forms and contains an old VB OCX that performs the logic (we’re still in process of converting rather our large code base from VB6/C++ to .NET).  This component is designed to be called from a Windows Forms app, where the user enters the Latitude and Longitude and waits for a response, which is returned by an Event to the caller:

 

private void button1_Click(object sender, EventArgs e)

{

    var geo = new ReverseGeovalidator(100);

    geo.ReverseGeovalidationComplete += new ReverseGeovalidator.ReverseGeovalidationEventHandler(geo_ReverseGeovalidationComplete);

 

    var latitude = 62531667;

    var longitude = 26988333;

 

    geo.ReverseGeovalidate(latitude, longitude);           

}

 

void geo_ReverseGeovalidationComplete(object sender, ReverseGeovalidationEventArgs eventArgs)

{

    // Populate the form with the data received from eventArgs

}

 

My application needed to run unattented (Window Service), monitoring the IPC server for notifications that a Premise had changed.  This meant no windows form and no waiting around for a display to be populated. 

 

I needed my application to accept a geo-coordinate and process reverse geo-code synchronously, so I could associated the resultant streetid with the premise id of the changed premise and then commit to the database.

 

My original thinking was to use ManualResetEvents.WaitOne() to block the caller until the event handler had been called and I could call ManualResetEvents.Set() to continue the operation.

 

public class GeoCoder

{

    ManualResetEvent geoDone;

    ReverseGeovalidationEventArgs data;

 

    public GeoCoder()

    {

        geoDone = new ManualResetEvent(false);                      

    }

 

    public int ReverseGeoCode(int latitude, int longitude)

    {

        geoDone.Reset();

        var geo = new ReverseGeovalidator(1500);

        geo.ReverseGeovalidationComplete += new ReverseGeovalidator.ReverseGeovalidationEventHandler(geo_ReverseGeovalidationComplete);

 

        geo.ReverseGeovalidate(latitude, longitude);

        geoDone.WaitOne();

        if (data != null)

        {

            return data.StreetID;

        }

        return 0;

    }

 

    void geo_ReverseGeovalidationComplete(object sender, ReverseGeovalidationEventArgs eventArgs)

    {

        data = eventArgs;

        geoDone.Set();

    }

}

 

The problem I found was that my unit test would lockup.

 

(Oh yeah, here’s the test)

[TestMethod]

public void ReverseGeoCodeTest()

{                           

    var gc = new GeoCoder();

    var latitude = 62531667;

    var longitude = 26988333;

 

    var streetId = gc.ReverseGeoCode(latitude, longitude);

 

    Assert.AreEqual(275535, streetId);

}

 

 

After a little bit of pondering I realised that geoDone.WaitOne() is blocking on the current thread, which is the same thread the event handler is on.  Result? The event handler will never get called.

 

I needed to put the event handler a separate thread, so it would eventually get called?  But How?

 

After a bit of playing around, I decided (realised?) to put the entire lot on a worker thread, a la Command Pattern.  I created a test Geo Processor that is essentially a job queue that co-ordinates off the queue every 2 seconds and processes them, returning the results when done.

 

public class GeoProcessor

{

    public delegate void GeoCodeEventHandler(object sender, GeoCodeEventArgs e);

    private Queue<GeoCoordinate> workQueue;

    public event GeoCodeEventHandler GeoCodeEventCompleted;

 

    // Timer will pull Coordinates off the work queue and process them.

    System.Timers.Timer workTimer;

 

    public GeoProcessor()

    {

        workQueue = new Queue<GeoCoordinate>();

        workTimer = new System.Timers.Timer();

        workTimer.Elapsed +=new ElapsedEventHandler(workTimer_Elapsed);

        workTimer.Interval = 2000;

    }

 

    // Pulls items from the queue and processes.

    void  workTimer_Elapsed(object sender, ElapsedEventArgs e)

    {

        workTimer.Stop();

 

        if (workQueue.Count > 0)

        {

            var coord = workQueue.Dequeue();

           

            //

            // Do some processing here

            //

 

            // Raise event to notify subscriber that work is done.

            OnGeoCodeComplete(new GeoCodeEventArgs(275535));

            if (workQueue.Count > 0) workTimer.Start();

        }           

    }

 

    // Queues the coordinate up for processing in the work queue

    public void QueueGeo(int latitude, int longitude)

    {

        workQueue.Enqueue(new GeoCoordinate { Latitude = latitude, Longitude = longitude });

        workTimer.Start();

    }

 

    protected void OnGeoCodeComplete(GeoCodeEventArgs e)

    {

        if (GeoCodeEventCompleted != null)

        {

            GeoCodeEventCompleted(this, e);

        }

    }

}

 

Below is the EventArgs sub class I use to pass the “processed” information back, along with the geo coordinate class.

 

public class GeoCodeEventArgs : EventArgs

{

    private int streetId;

 

    public GeoCodeEventArgs(int streetId)

    {

        this.streetId = streetId;

    }

 

    public int StreetId

    {

        get { return this.streetId; }

    }

}

 

public class GeoCoordinate

{

    public int Latitude { get; set; }

    public int Longitude { get; set; }

}

 

This is my command pattern implementation that will run on a separate worker thread and call the GeoProcessor.

 

internal class GeoWorker

{   

    GeoCoder geoCoder;

 

    int latitude;

    int longitude;

       

    public GeoWorker(GeoCoder geoCoder, int latitude, int longitude)

    {               

        this.latitude = latitude;

        this.longitude = longitude;

 

        this.geoCoder = geoCoder;

    }

   

    public void Process()

    {

        var geo = new GeoProcessor();

        geo.GeoCodeEventCompleted += new GeoProcessor.GeoCodeEventHandler(geo_GeoCodeEventCompleted);

       

        geo.QueueGeo(latitude, longitude);

    }

 

    void geo_GeoCodeEventCompleted(object sender, GeoCodeEventArgs e)

    {

        geoCoder.Data = e;

        geoCoder.GeoDone.Set();

    }   

}

 

 

My GeoCoder class is now only responsible for starting the worker thread that will call the GeoProcessor and then waiting for the reset event to be set.  Once set (when the worker handles the event from the GeoProcessor and calls Set() on the shared Reset Event) GeoCoder can continue.

 

public class GeoCoder

{

    ManualResetEvent geoDone;              

    Thread geoWorkerThread;

 

    int latitude;

    int longitude;

   

    public GeoCoder()

    {

        geoDone = new ManualResetEvent(false);      

    }

 

    public GeoCodeEventArgs Data { get; set; }

    public ManualResetEvent GeoDone { get; }

 

    public int ReverseGeoCode(int latitude, int longitude)

    {

        this.latitude = latitude;

        this.longitude = longitude;

 

        GeoDone.Reset();

 

        geoWorkerThread = new Thread(new ThreadStart(this.CallGeoWorker));

        geoWorkerThread.Name = "Reverse Geo Code Worker";

        geoWorkerThread.SetApartmentState(ApartmentState.STA);

        geoWorkerThread.Start();

 

        GeoDone.WaitOne();

        if (Data != null)

        {

            return Data.StreetId;          

        }

        return 0;

    }

 

    private void CallGeoWorker()

    {

        var geoWorker = new GeoWorker(this, latitude, longitude);

        geoWorker.Process();

    }

}

 

The asynchronous (event) call has now been turned into synchronous.

 

The biggest problem is the showstopper though.  COM doesn’t play well using this model, thanks to it’s STA threading (I believe) and the event handler never gets called. 

 

Overall a great learning experience, but I still can’t call the COM wrapper object synchronously and now, admitting defeat, I am rolling my own class to calculate the StreetId.

 

 

 


Thursday, December 11, 2008 #

2008 the SDSI work year was pretty quiet. Most of our energies were turned toward getting all sites live ASAP being able to concentrate on chasing other contracts, with QAS/QFRS live. Engineering work was pretty minimal, except for the occasional bug fix or new feature driven by changing business requirements. We had responded to requests for tender on a few other projects which would have given us a constant flow of work for the second half of year. Unfortunately the timing was right and the work kept getting bumped back. Finally, two weeks ago three of the jobs came in in the same week, all needing their work done by the end of Q1 2009. Not too much problem, we’d been pretty quiet anyway. I went out to the recruitment agencies and got two more engineers onboard to handle the extra work. Yesterday we had a meeting with one of our big clients, who has another large IT project on the go, about to wrap up. This multi-million dollar project going live is contingent upon successful integration with our CAD system. Their deadline? End of Q1 2009. All I could say was LOL, and immediately got on to seeking more developers. There’s an absolutely huge announcement on Monday night, which if swings in our favour, will see our engineering team double again. We’re also chasing a federal grant that will give us the funding we need to re-engineer one of our products from the ground up, adding a few more devs to the team. Whilst it’s fantastic news and sure beats being bored all day, I can’t help but ponder on why does work always seem to come in at once? We’re not in a segment that revolves around particular dates, like if we were in the financial sector and end of financial year was looming. Here’s looking forward to a ground breaking 2009. I go on leave in a week. My first leave this year and to be honest, even though I’ve worked 70 hour weeks for the last 12 months, I’m so pumped I don’t want to holiday. *sigh*

Wednesday, September 10, 2008 #

 

One of the big challenges any developer faces is knowing how to successfully progress up the ladder.  What do we do to make ourselves stand out, and how do we get the skills we need to move to the next level.  For me, this is a problem.  Thus far in my career, I’ve relied on what I consider the most important attribute: hard work.  I believe the best way to get noticed and promoted is to finish projects on time (insomuch as we can control these things J) and as defect free as possible.  Then, rather than relax, go chase more work.  Help others out.  I don’t think it’s long hours, and I don’t look for long hours in my developers, people need balanced lives to be happy and effective. When I allocate work, I do so based on an 8 hour day, or at least what I think can be accomplished in an 8 hours day, even if I’ve been told on more than one occasion by people senior to me that I work faster than most.

If you need to work 2 extra hours a day to keep up, you need to either work smarter or get more skilled.  Perhaps both. 

The career path for a develop goes pretty much like

Developer -> Developer Lead -> Solution Architect -> Enterprise Architect -> (Development Manager) -> CIO.

I’ve put Development Manager in parenthesis because in my experience as Development Lead, Development Lead performs resources scheduling activites and high level oversight roles too. At least, in my small company, I’m the Development Lead and the Development Manager and Solution Architect in one.  Hell, I’m also a Developer.  So there’s a glaring Caveat, the career path depends on the company you work work. 

 

Another thing I’ve noticed is Developers will seek promotion to get paid more.  Many bosses consider promotion to be management, so will promote good programmer to Managers, where they may make really bad managers.  There’s something called the Peter Principle, go check it out http://en.wikipedia.org/wiki/The_Peter_Principle .


Saturday, August 30, 2008 #

As this is my first post in this blog, it’s probably best I introduce myself.   I’m Rob Gray, a team lead at Spectrum Data Systems International (http://www.sdsi.com.au).  We are the Australasian vendor Tritech’s (http://www.tritech.com) VisiCAD computer aided dispatch system for emergency services.

 

We’re a small team but we’re passionate about what we do.  Being a small team, from time to time we all get wear different hats. One day I’ll be developing, another I’ll be supporting a customer, and another I’ll be onsite  gathering requirements or attending high-level management meetings.  I think that’s fairly typical of how small companies operate.

 

I also own my own software dev business, RG Software Solutions, which I work on the side with (paid) help from a friend and former colleague.  We’re developing mobile and satellite phone contract management software. 

I've described myself as a .NET journeyman because I feel I'm no where near the expert I want to be, but I've completed university studies and a few certifications from Microsoft.  I marvel at the awesomeness of so many .NET people out there and I don't feel I'm up to their level.  But I am constant learning and trying new things, so hopefully one day...

We just received our new Dell PowerEdge T300 servers this week.   We’ve got four, two for support and two for development.  Our lab currently has numerous old machines, with specifications and limited as PII 350Mhz CPU’s up to Xeon 2.6Ghz Quad Core Stratus servers.  The PowerEdge’s will replace the older spec machines we’ve had for the last 5 years. 

 

Another benefit of having these machines is that each department can now run multiple virtual machines on each of them.  This will allow us to replicate production environments at customer sites more readily;  at least the database servers.  One of the biggest frustrations we’ve had over the last few years is having to spend 30 mins configure and setup when changing servers.  Because machines were in short supply some servers were playing host to multiple CAD server configurations,  each running different versions of CAD, not just different data.

 

Another improvement by way of Virtual Machines is the use of Virtual development environments. We’ll now have a dev VM for each different client.  This VM will contain the basic dev tools, VS 2003, VS 2008, VB 6, and Sql Server tools, along with reflector and TestDriven.NET .  It was only today that one my of co-works, Kevin, voiced his frustration with the delays in work brought on by having to configure different environments. 

On the RGSS front, I picked up  a “new” server last week so I could move the server out of a VM (it had lived on my media centre!)  to it’s own machine.  Took me a while to get the thing up and running, thanks to peculiarities with the Dlink DSL-502T router I’m using here.  Seemed it would lose the port forwarding if using a static ip.  I changed the ip back to DHCP assigned but tied to the MAC address, and it worked again.