CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeffrey Palermo [MVP]

Software management consultant and CTO, Headspring Systems

December 2006 - Posts

  • Driving a "no bugs" team culture - level 200

    I've been reviewing Jim Shore and chromatic's upcoming Agile book, and I especially like the "No Bugs" section here: http://jimshore.textdriven.com/Agile-Book/no_bugs.html

    Large, dusty bug databases abound.  Some teams have a process where this is unavoidable.  Some teams use a bug database as a roadmap *shudder*. 

    Personally, I believer that a "low priority bug" doesn't exist.  If it's a bug, why would you ever think of not fixing it?  If a product manager decides that the current behavior of the system is acceptable for the time being, then why would you call it a bug?  You may find the behavior of the system undesirable, but ultimately, it's the product owner's call what is desirable or not.  Maybe he agrees it's undesirable, but there are other things more undesirable (like the complete absence of several large stories). 

    In my experience, poor management leads to a code-and-fix culture - working off a bug list.  It's "easy" if there is just a running list of bugs.  The team stays busy, and you can track work (kind of).  What is hard is to lay out a prioritized road map and manage the growth of the product.  Managing the product is actual work. 

    Many things can contribute to a low bug count:  proper roadmap planning, good developers, automated tests, good software design, etc.  I think the average life of a bug is also very important for the team culture.

    To drive a culture of "no bugs", bugs cannot be allowed to live long.  If a bug is announced, and nothing happens, the team accepts that the bug is there, and they will fix it "when they get to it".  If someone stops and immediately addresses the bug, then the team knows that bugs are not acceptable.  From a management perspective, I give bugs top priority.  If there is a bug on our wall (red index card), it has top priority - no questions.  The bug is not allowed to live.  This is key for driving a "no bugs" team culture.
     

  • Product Review: VisualSVN, Subversion plugin for Visual Studio - level 200

    As stated on its website, VisualSVN is a plugin for Microsoft Visual Studio for seamless integration with Subversion.

    This is an early review since I've been using it for less than a week.  Many think to Scott Bellware for making me aware of this product because it has made my day just a bit easier.  Up until now I've been using TortoiseSVN, a windows shell extension, for my integration with our Subversion source control repositories. 

    VisualSVN wraps TortoiseSVN and makes all the commands available from within Visual Studio 2003 or 2005.  I'm using VS 2005, so I have not tried it with 2003.  I am very pleased with my experience so far.  VisualSVN puts a colored diamond next to each file (green/yellow/red for unchanged/changed/conflicted).  I can show a diff, revert, etc from the solution explorer.  I can also update commit either from the menu or the Solution root.  The user experience is the same as Tortoise since VisualSVN doesn't introduce a new way to update/commit.  VisualSVN actually pulls up the TortoiseSVN window for update/commit.  TortoiseSVN is actually doing the work, but VisualSVN pulls in access to it into the IDE.  This takes away the need to switch back to Windows Explorer for source code interactions.

    In short, I'm very impressed with my trial so far, and I plan and laying down the $19 for the great tool.  After Christmas, they plan on setting the price back at $49. 

  • Searching in .Net (don't search manually, let .Net do it for you) - level 100

    With .Net 2.0, we have some basic search functionality baked in to the .Net Framework.  With Linq coming out in .Net 3.5, we will have full querying support on IEnumerable types, but we have quite a bit of power with .Net 2.0.  In this short article, I'll outline how to search objects in memory with .Net 2.0 or higher.

    Consider the following class, Person:

    public class Person : IComparable

    {

        private string _firstName;

        private string _lastName;

     

        public Person(string firstName, string lastName)

        {

            _firstName = firstName;

            _lastName = lastName;

        }

     

        public string FirstName

        {

            get { return _firstName; }

        }

     

        public string LastName

        {

            get { return _lastName; }

        }

     

        public string FullName

        {

            get { return string.Format("{0} {1}", FirstName, LastName); }

        }

     

        public int CompareTo(object obj)

        {

            Person person = (Person)obj;

            int compareResult = LastName.CompareTo(person.LastName);

            if(compareResult == 0)

            {

                    compareResult = FirstName.CompareTo(person.FirstName);

            }

     

            return compareResult;

        }

     

        public override string ToString()

        {

            return FullName;

        }

    }


    Consider this a basic domain object within the application.  We have the need to work with many instances of this class in memory.  Assume that there is no need for a relational database for this application.  We pull Person instances in memory and work with them.  Often, we'll need to pare down the set to only include instances that match some criteria.  We could loop through each one and create a new list with matches, but with .Net 2.0 and "Comparison"(s), we have a more elegant way.  Here is an example.

            public void ShouldQueryArray()

            {

                Person[] people = new Person[]{new Person("Homer", "Simpson"), new Person("Jeffrey", "Palermo")};

                Person[] matches = Array.FindAll<Person>(people, delegate(Person obj)

                                                                    {

                                                                        return (obj.FirstName.Contains("ome"));

                                                                    });

     

                foreach(Person person in matches)

                {

                    Console.WriteLine(person);

                }

            }


    In this example, we can just specify the condition that causes an instance to match, and we'll get back a new array with only the matches.  In this case, we get the following output:

    Homer Simpson

    I work with arrays a lot.  In fact, it is my favorite IEnumerable data structure because of it's power, but it isn't the only structure that supports comparisons (and there are other methods similar to FindAll, but FindAll is the most common).  

    public void ShouldQueryIList()

    {

        List<Person> list = new List<Person>();

        list.Add(new Person("Homer", "Simpson"));

        list.Add(new Person("Jeffrey", "Palermo"));

     

        List<Person> matches = list.FindAll(delegate(Person obj)

                                                {

                                                    return (obj.FirstName.Contains("ome"));

                                                });

     

        foreach (Person person in matches)

        {

            Console.WriteLine(person);

        }

    }


    Here, we are using the List<T> class for our search.  The following example is more complex.  This illustrates that the matching criteria can be as complicated as you need it to be.

    public void ShouldQueryArrayWithComplexComparison()

    {

        Person[] people = new Person[] { new Person("Homer", "Simpson"), new Person("Jeffrey", "Palermo") };

        Person[] matches = Array.FindAll<Person>(people, delegate(Person obj)

                                                            {

                                                                return (obj.FirstName.StartsWith("j", StringComparison.InvariantCultureIgnoreCase)

                                                                       && obj.LastName.EndsWith("mo"));

                                                            });

     

        foreach (Person person in matches)

        {

            Console.WriteLine(person);

        }

    }


    By the way, if you aren't familiar with the "delegate" keyword, this is an anonymous method.  I could refactor it into a private method, but I'm using it inline instead.  In short, FindAll just requires a delegate.  However you wish to provide it is fine.




More Posts

This Blog

Syndication