Wednesday, December 17, 2008

The Build Process is central to the business of delivering software.  In my opinion, the Build Process IS your business.  It is crucial to the development of high quality software - from both a developer and end-user perspective.

In our organisation, we have a build pipeline which can effectively be separated into 3 distinct stages:

 

Stage 1 - Development

 

When the Developer starts the build the code is first run through our automated source analysis tools.  If there are no breaking changes here (the policy can be defined) then the source is built.  If this successfully builds then the required unit testing suites are run.  It is important to realise here that we do not build THE PRODUCT, every time.  We could, but most of the time we will be building only one specific component of the product.  These can be chained together in the CI system. 

Note: Most CI processes will commit the source FIRST, then perform Unit Testing.  While this still has the advantage of catching build errors early if performed regularly, it still results in broken builds.  The CI system we use, TeamCity, allows us to perform "Remote Personal Builds", which merges the source to be committed to a *unique* location.  If there is a build error or unit test failure then the defective source is NOT committed to VCS.  Therefore there will never be a broken build.

Of course, what this means is that you must set up your build scripts to work with RELATIVE PATHS ONLY, which could be a considerable change for legacy systems.  If using SVN, the Externals feature is crucial to this.  I hope you would agree with me that the result here more than justifies the cost in setting it up. 

 

Stage 2 - Automated Testing and Deployment

If Stage 1 has been successful, the Installation is built.  Note that we are talking about A PRODUCT DELIVERABLE in this case (although there can be several of these that comprise THE PRODUCT).

Virtualisation and Automation are two key foundation stones that a CI system is built on.  It is always worthwhile to remember that the majority cost of developing software isn't in the coding, but in the testing.  EVERY test that has to be performed manually, because it has to be repeated again and again by someone who needs to be paid, is throwing your money away.  Therefore it is much more prudent to develop automate as much of the test effort as you can.

The Build Process now deploys the installation to the designated virtual machines.  These comprise a number of different operating systems with different configurations.  Again, with the number of different versions of OS on the market, we can get as much coverage as we desire here with a fraction of the cost involved in doing this manually.

Once this has been deployed, the designated auto-tests are run on each configuration.  The results log of these tests are linked back into the appropriate build in the CI system, so it is fully traceable.  The final task of this stage is to roll back the virtual machines to their pre-test state - this allows anyone who wants to look at that particular install do so without having to install it themselves.

Stage 3 - QA

Because we have automated so much of the 'grunt work' we can concentrate on the elements of the system which cannot easily be automated, if at all. 

Any manual QA is performed here, along with Design Reviews of the product.  This can give the goal donor and other stakeholders insight into the product at every stage in its delivery, and of course the earlier that change is managed the easier it is to inject into the product.

 

 

There are so many benefits from setting up a successful build pipeline, and it really must be done at the start, not half-way, and certainly not at the end, to get the most out of it. 

 

Wednesday, December 17, 2008 9:13:56 PM (GMT Standard Time, UTC+00:00) | Comments [0] | Build Process#
Thursday, December 11, 2008

By default, the XtraPivotGrid renders the column headers horizontally.  This is fine if we only have a few columns, but chances are there will be too many columns to fit into the limited space available, without resorting to scrolling. 

It would be much better, in terms of functionality and aesthetic appeal, if we could render the text at a 45 degree angle, thereby maximizing the number of columns we can fit into a confined space.

Unfortunately the control does not support this directly, but we can work around this. 

Firstly, set the ColumnValueLineCount property of the Field being displayed in the Column Area to 2 or 3.  This will increase the header band so we have space to render our text diagonally.

Hook into the CustomDrawFieldValue event like so:

private void pivotGrid_CustomDrawFieldValue(object sender, 
            DevExpress.XtraPivotGrid.PivotCustomDrawFieldValueEventArgs e)
        {
            HeaderObjectPainter newPainter = e.Painter;

            string c = e.Info.Caption;

            e.Info.Caption = "";
            newPainter.DrawObject(e.Info);

            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
            if (IsColumnHeaderHorizontal(e.Field))
            {
                Font newFont = new Font(e.Appearance.Font.FontFamily, 8);
                e.Appearance.Font = newFont;

                StringFormat fmt = new StringFormat();
                fmt.Alignment = StringAlignment.Far;
                fmt.Trimming = StringTrimming.EllipsisCharacter;
                fmt.FormatFlags |= StringFormatFlags.NoWrap;

                e.GraphicsCache.DrawString(c, e.Appearance.Font,
                    e.Appearance.GetForeBrush(e.GraphicsCache),
                    e.Info.CaptionRect, fmt);
            }
            else
            {
                Rectangle newRect = new Rectangle();
                newRect = e.Bounds;
                newRect.X += newRect.Width;
                newRect.Y += newRect.Height;

                newRect.Width *= 7;
                newRect.Width /= 5;

                newRect.Height *= 7;
                newRect.Height /= 5;

                newRect.Y -= 8;
                newRect.Height -= 8;

                StringFormat fmt = new StringFormat();
                fmt.Alignment = StringAlignment.Far;
                fmt.Trimming = StringTrimming.EllipsisCharacter;
                fmt.FormatFlags |= StringFormatFlags.NoWrap;

                e.GraphicsCache.DrawVString(c, e.Appearance.Font, 
                    e.Appearance.GetForeBrush(e.GraphicsCache), 
                    newRect, fmt, 45);
            }

            e.Info.InnerElements.DrawObjects(e.Info, e.Info.Cache, Point.Empty);
            e.Handled = true;
        }

We also need to determine whether to render this field horizontally or not:

      private bool IsColumnHeaderHorizontal(PivotGridField field)
        {
            if (field == null)
            {
                return true;
            }
            if (field.Area == PivotArea.RowArea)
            {
                return true;
            }
            return false;
        }

 

 

 

 

 

 

 

 

 

Thursday, December 11, 2008 11:50:41 PM (GMT Standard Time, UTC+00:00) | Comments [0] | c# | DevExpress#

If your Assembly has included the

[assembly: CLSCompliantAttribute(true)]
attribute in it's AssemblyInfo, then you may encounter the above error.  It's not one of the most useful, given that you then 
have to scour the Common Language Specification rules to find out what exactly has gone wrong.
Even worse, is that it can send you in completely the wrong direction, as it did with me the other day.
If one assembly, which has the above attribute marked as true, refers to another assembly which does not include 
that attribute (or has it marked as false), then you will receive the above error message, telling you that Type such-and-such 
is not CLS-Compliant.  Even if it doesn't break any of the rules!
So, one to bear in mind for the future.
Thursday, December 11, 2008 5:36:57 PM (GMT Standard Time, UTC+00:00) | Comments [0] | c##
Monday, December 08, 2008

I love DevExpress controls.  They do so much for you.... of course, the flip side is that there are so many properties and methods that even the simplest tasks can be a bit daunting.

One of the most common tasks when dealing with grids would be to set up a Master-Detail relationship.  For my own future reference I've written down the properties and methods involved in setting this up at design-time.

  • Add Grid [Grid_Master]
  • Set up columns
  • Grid_Master.SettingsDetail.ShowDetailRow = true
  • Smart Tag -> Edit Template -> Detail Row.
  • Add Detail Grid here [Grid_Detail]
  • Grid_Detail.SettingsDetail.IsDetailGrid = true

If using custom databinding, hook up Grid_Detail.Databinding event.  In Grid_Detail.Databinding, add similar code to the following:

            //The detail data is the assets that make up each Purchase Order
            ASPxGridView detailGrid = (ASPxGridView)sender;
            detailGrid.KeyFieldName = "ID";
            int id = (int)detailGrid.GetMasterRowKeyValue();
            DataTable table = new DataTable();
            table.Columns.Add("ID", typeof(int));
            table.Columns.Add("AssetTypeName", typeof(string));
            table.Columns.Add("SerialNumber", typeof(string));
            table.Columns.Add("SalesPrice", typeof(float));
            table.Columns.Add("Location", typeof (string));
            table.Columns.Add("Discount", typeof (float));

            Invoice invoice = InvoiceModel.GetInvoiceForID(id);
            if (invoice != null)
            {
                foreach (AssetItem asset in invoice.MyInvoiceItems)
                {
                    table.Rows.Add(new object[] { asset.ID, 
                      asset.AssetTypeName, asset.SerialNumber, asset.SalesPrice, 
                    asset.Location, asset.Discount});
                }

                detailGrid.DataSource = table;
            }

 

Monday, December 08, 2008 9:52:45 PM (GMT Standard Time, UTC+00:00) | Comments [0] | c##
Saturday, December 06, 2008

When it's time to start implementing a new project, where do you begin?  Once the requirements have been gathered and the first Sprint planned, it's time to start coding, right? 

I'm not so sure.

Something that has dawned on me over a number of projects, is the seemingly back-to-front mantra of start at the end.

By this, I mean that you should always start at your build process.  Examine it, and make sure that before Feature One has been committed to source control, you have a fully functioning build system in operation.  That is, there should be a fully automated system in place that will build the latest sources, create any necessary installation files and deploy to pre-designated release locations, complete with versioning.  And this should be in place before you've got anything more than a single form in your application.

The reason for this is simple - the build process is critical to the success of your project.  It is too easy to underestimate the amount of work that has to go in to making a release of software.  You can lose days tangled up in build scripts when someone adds a new component, or worse - maybe there is a wrong version of an assembly being referenced.  A number of years ago, I remember we used to do product release builds inside the IDE, and then manually copy the files to a location on the network.  Thankfully those days are well behind us!

Of course, the other reason is that the build scripts can get very complicated - very quickly.  Like other, non-functional requirements of software such as exception management, localization and documentation - if you don't keep on top of them you will lose control. 

Key to the build process is the concept of Continuous Integration.  This is the idea that changes are integrated into the main build continously - we no longer wait on kicking off builds manually.  Builds are usually triggered as a result of source files being committed to the repository, or on hourly schedules.  The thinking behind this is that the earlier we catch an error, the easier it will be to fix.  Like most good ideas regarding software development - this is just common sense.

However, you don't get it for free.  But there are tools which take on a lot of this burden, and it's really up to you how complex you want your build process to be.  Do you want automatic code reviews to be run (such as FxCop), Unit Test coverage reports, automated deployment to virtual machines for automated testing?  Or else your build process could simply consist of a source control fetch followed by a build, followed by a copy to a location on the network entitled 'Releases'.  Still better than doing it yourself!

Over the next few weeks I'll be documenting my ideal approach that I believe would lead to the successful and timely delivery of software projects. 

 

Saturday, December 06, 2008 7:57:19 PM (GMT Standard Time, UTC+00:00) | Comments [0] | Build Process#
Monday, December 01, 2008

One of the most valuable lessons I ever learned during my time at university, was the methodology of Divide-And-Conquer.  This can be applied to anything, but in Computer Science is vital for dissecting those complex problems into their component parts.

The only problem was that, like a lot of things I did at uni, it wasn't really put into practice once the coursework and exams were over.  When you start work it's all about delivery - you don't have time to really think things through in this way, especially when you only have a couple of days until what you're working on is in the product, finished and looking shiny.

Or at least, you don't think you have the time.  But turns out the lecturers and academics are right after all.  Taking the time up-front to fully understand a component, whether it be for internal use or a UI control, saves a lot of hassle in the long-run.  Think incomplete specifications, unexpected surprises, and debugging particular scenarios during a defect cycle.

I've been thinking about this recently as I have to get to grips with a new control, and slot it into an existing application.  Whenever I start to work with a control that I'm unfamiliar with, I like to build myself a little sandbox and give it a good going overmaking sure that I know what's going on in an isolated environment, without having to worry about the rest of the codebase affecting it.   I might not get as much time on it as I'd like, but there's always that balance. 

What this lets me do is get to grips with what I'll be using the control for, how it binds to data, how it can be controlled via it's interface rather than using the 'Smart' Tags to configure it.   

I've been putting this technique through it's paces for a couple of years now, and it's clear when I look back at some older code how much of a difference it makes.  The solutions might take a little longer to develop, but the result is a cleaner implementation, for the reasons outlined above.

If I was President, I'd like to see a lot more of this sort of thing.  Whenever a new component was being developed, it should have an associated driver (prototype/sandbox - call it what you will) which configures and simulates the component in a real-world environment, but completely isolated of other parts. 

Of course, what this ultimately results in is a suite of Unit Tests for your newly developed component.  So this is hardly a revolutionary idea, but supports that we are on the right track.

 

 

 

Monday, December 01, 2008 5:59:29 PM (GMT Standard Time, UTC+00:00) | Comments [0] | c# | Patterns#
Search
Archive
Links
Categories
Admin Login
Sign In
Blogroll