100% code coverage might not be enough


I was recently working on a project where code coverage slipped and we needed to put some effort to catch up by writing few unit tests. During the process, we came across an interesting case which proved us that having a 100% code coverage was not  a sufficient criteria to stop writing unit tests.

Here is the method we were trying to cover:

public Status RetrieveInitialStatusForTicketType(RetrieveInitialStatusForTicketTypeRequest request)
        {
            try
            {
                var userProfile = base.CheckAuthorizationAndReturnValidUserProfile(request);

                var statusesWorkflow = StatusesRepository.RetrieveStatusesWorkflowForTicketType(userProfile, request.TicketTypeId);

                if (statusesWorkflow == null || statusesWorkflow.Where(s => s.IsInitialStatus).Count() != 1)
                    throw new Exception("Must have one and only one initial status");

                var initialStatuses = statusesWorkflow.Where(s => s.IsInitialStatus);
                Status status = initialStatuses.First();
                Status returnedStatus = (Status)status.Clone();
                returnedStatus.PossibleTransitions = null;
                return returnedStatus;
            }
            catch (SecurityException) { throw; }
            catch (InvalidTokenException) { throw; }
            catch (Exception e) { throw new InternalErrorException(e); }
        }

The whole method was covered except the line 10:

throw new Exception("Must have one and only one initial status");

and line 20 that catches the exception.

So we added a new unit test that expected an exception and in which we mocked the StatusesRepository (line 07) to return a null value. We ran the code coverage analyzer and, yeah!!! 100% of the code was covered. We thought we were done… but wait a minute!!!!

The unit testing is not about testing per se, but rather about validating the requirements implemented by a method. In other words, all the requirements the method implements must be validated by one or more unit tests.

However, by looking at our code, we clearly see, at line 09, that the returned workflow must contain one and only one status which flag “IsInitialStatus” is set to true. However, none of our tests was checking for this rule to be implemented although the code was 100% covered.

We decided to write a new unit test in which the workflow returned by the StatusesRepository (line 07) contains more than one initial status and another in which the returned workflow contains no status marked as the initial status. In both cases, the unit test expects and exception to be thrown. Only then will our code be really 100% covered functionaly speaking.

Along the way, we have split the condition of the line 09 into two different if statements in an attempt to make the code more comprehensive (lines 09 and 12).


public Status RetrieveInitialStatusForTicketType(RetrieveInitialStatusForTicketTypeRequest request)
        {
            try
            {
                var userProfile = base.CheckAuthorizationAndReturnValidUserProfile(request);

                var statusesWorkflow = StatusesRepository.RetrieveStatusesWorkflowForTicketType(userProfile, request.TicketTypeId);

                if (statusesWorkflow == null)
                    throw new Exception("No statuses workflow is defined for the specified ticket type id.");

                if (statusesWorkflow.Where(s => s.IsInitialStatus).Count() != 1)
                    throw new Exception("Must have one and only one initial status");

                var initialStatuses = statusesWorkflow.Where(s => s.IsInitialStatus);
                Status status = initialStatuses.First();
                Status returnedStatus = (Status)status.Clone();
                returnedStatus.PossibleTransitions = null;
                return returnedStatus;
            }
            catch (SecurityException) { throw; }
            catch (InvalidTokenException) { throw; }
            catch (Exception e) { throw new InternalErrorException(e); }
        }

Splitting the condition into two separate conditions had two advantages:

1 – Each condition correpsonds to a particular business rule. Line 09 means that a ticket type MUST be associated with a workflow and line 12 expresses the fact that a workflow must have only and only one initial status. It it now clearer that we do have two business rules and that the condition workflow == null was not a simple check to avoid null object exceptions.
2 – Since the two conditions are separated, we could throw a more specific exception in each case that explains more precisely the reason why it has been thrown (which is very helpful when debugging an application).

Conclusion

While writing unit tests, being in TDD or in TTCUBWWL (tests to catch up because we were lazy – to write them before the code -), we must pay attention to the fact that a method must be covered by unit tests that validate  each implemented requirement in the method and bear in mind that reaching a 100% code coverage of a method is not always a sufficient criteria to stop writing tests for the method.

My advise for the team was that they should write their tests before the code; first to avoid another cathing up effort but more importantly, if the developer wrote the code using TDD, he would certainly not miss the unit test to check the method behavior when a workflow has more than one initial status. But this is another debate: the efficiency of TDD in writing clean and more comprehensive code.

My thoughts on team leadership


Before I start, I would like to note that this post is about my thoughts on what is a good leader (nothing based on a particular study or article). It is based on my observation of what I considered good leaders and also what I considered bad leaders. In fact, throughout my career, I realized that I could learn from both types of leaders respectively what to do and what I should never do as a team leader.

Following are simple rules and values that I appreciate in the managers I worked with so far and which I try to apply and tie to as much as I can when I manage a team:

  1. Forget about hierarchy. It is an old concept.
  2. Gain the respect of your team not their fear (of eventually loosing their job)
  3. Be available to spend time out of work with your team (lunches are a very efficient way to strengthen your links with your team members)
  4. Bear in mind that you are not smarter or more competent than your team. You just have enough experience to facilitate the work of a team
  5. Convince and do not impose; if you are good enough, convincing your team is the only way to make them take a direction (the one you believe is the best in the circumstances)
  6. Be honest and transparent with your team.
  7. Never try to bullshit your team members. They are not stupid and every time you try to do so, you are losing a share of their respect
  8. Do not try to be a hero. You will be a hero anyway if your team succeeds and delivers
  9. From your standpoint, your team takes all the credit for successes and you are ultimately responsible of your team failures to deliver
  10. You work for your team and not the other way around
  11. NEVER YELL AT YOUR TEAM MEMBERS. NEITHER IN PRIVATE OR, WORSE, IN PUBLIC
  12. Bashing your team members is the same as someone bashing his kids and family; he does not have values
  13. Get the right people on the bus.
  14. Do not hire only people that think like you; hire people that will challenge you. This will help you to grow.
  15. You must learn from your team. If you believe that you cannot or there is nothing to learn from them, then do an introspection – there is something wrong and more likely with you than with your team –
  16. Help your team members to grow and be successful
  17. The best way to make someone do something for you is to give him the desire to do it (Dale C.)

If you think of some other values and/or rules that you believe are essential to be a team leader or team manager. Please share them.

Automated acceptance tests to get around requirement misunderstanding in a distributed teams


A year ago, I was part of an integration team who’s role was to implement new functionality in a travel web site based on a back end that was developed overseas. The functionality was about providing users with discounts when they select more than one product in their shopping basket. It may seem simple at first glance, but actually the business rules surrounding these discounts were everything but simple. They were based on dates (discounts were only available on certain dates for instance), products’ combinations (some products combined may give a discount but if you add a third product for instance, it will give another discount and the first one must be discarded etc) and destinations (some discounts are available only for certain destinations).

Before starting the development, we were given a business analysis document that described these business rules and we had QA team that was checking whether or not we implemented the right business rules. This functionality was crucial for the company and needed to be sure 100% that it was bug free… ok say 99% (perfect does not exist).

As we started to deliver the first iterations, the QA team started to raise bugs almost every day and these bugs were off 4 reasons:

  1. Business rules misunderstanding by the QA team
  2. Business rules misunderstanding by the development team
  3. Bugs in discounts calculations at the back end level
  4. Data issues (because all the rules were configured in the back end, if someone changed the rules, of course we would have different results and sometimes QA reported that as bugs)

The back end team was delivering a new version every week that was supposed to contain bug fixes and new functionality. However, it also contained few, not to say many, regressions.

This situation was frustrating. We were under the impression that no progress was made and fingers started to point out in all directions. The development team pointed the overseas team, the overseas team the business analysts and these latter the QA team.

We found out that the situation was due to 1 main factor: lack of requirements understanding.

Solution

We decided somewhat to “automate” the communication and comprehension of the requirements.

A senior developer and I started to write a kind of domain language (DSL). Basically it was a list of utility classes that abstracted our domain objects model and the interaction with the back end. These classes had methods such like ‘AddFlightTo(string destination, DateTime departureDate, DateTime returnDate)’ and ‘AddHotel(…)’ etc… and the most important method was ‘CalculateDiscount’. In the meantime, the QA team along with the business analysts were asked to write the requirements in terms of acceptance tests and not business rules. We (developers) asked them to provide us with tables which columns contain values and the expected results expressed with numbers (discount percentage & discount amount). By expressing requirements this way, we reduced the room of misinterpretation of business rules.

Using the DSL made writing automated tests far easiest and less time consuming in fact. We could write a bunch of scenarios according to the acceptance tests provided by the BA and QA teams and automated them using MS UNIT. These tests were scheduled to run as part of our night build which made regression bugs detection easiest and we no longer deployed a new version of the software than did not pass all the tests (note that we already had unit tests but they did not validate the requirements as these particular requirements were implemented on the third party back end).

We also provided the compiled automated tests to the overseas team ensuring that they always have the latest version. The back end team started to run them before they deliver a new version discovering regressions and misunderstanding of requirements far earlier than before. More important, they did no longer deliver bugs (especially regression bugs) or at least less regressions which had an impact not only on the productivity, since our back end had always a relatively stable version, but also it had an impact on the back end team reputation as well. Note that for safety reasons, we were validating back end deliveries by running our tests battery. If the number of bugs was not acceptable, the new version was simply not installed on our servers avoiding situation were developers and testers were blocked because of an unstable version.

Once all this in place, we started to see less frustration and more progress in the project. Of course, we still had some communication issues, sometime misunderstanding business rules, but it had nothing to do with the situation before the automated acceptance tests. We basically reduced the number of bugs from a dozen for each delivery to something like 2 to 3 bugs; it was a huge shift.

Conclusion

Even though we were not TDD oriented team and we never practiced acceptance tests driven development, we found out that a test is much more efficient to validate our understanding of the requirements than lengthy discussions and meetings. Making these tests automated took us less than 2 weeks efforts for 2 developers but saved us much more than the invested 2 weeks; it restored the confidence between different technical stakeholders.

The acceptance tests became the contract that bound the teams together and automating them removed any room for misunderstanding. Either the test passes and we could deploy or it did not pass and then we had either to fix the bug or discuss the test itself with the QA and BA teams. In both cases, the situation was clear.

Guidelines for consistent exception handling


Exception handling is an essential concept in object oriented programming. It permits us to indicate program or method failure in way that it does not affect the method signature and thus, not affecting the overall design of our methods and classes.

However, exception handling is maybe the most misunderstood part of OOP as far as I have seen. Some think that exceptions are an evil we must live with; an evil that oblige us to decorate our code with a ‘Try/catch’ block whenever we think that an exception may arose. I do think though that exceptions, when used appropriately, can be our ally in making our code more robust and error proof. Exception handling is also a major part of an application design and we should give it the necessary thinking time that it deserves.

In the following article, I hopefully try to put some light on good practices in throwing, catching and designing methods to deal with exceptions.

What is considered an execution failure?

Often, we wrongly consider that we should throw an exception only at exceptional circumstances. The rule actually is “If the method does not what its names suggests (aka what it is supposed to do), it should throw an exception to notify it” (1). A good example is the .NET framework : if you try to open a file for writing but the file is a readonly file, the method will throw an exception because it could not open the file as the method name suggests it. That is said, it is not an unhandled case in terms of the code of the Open method, but it is not a normal execution path neither.

Exception handling versus returning error codes

This is probably a wide topic but I am going to give the main reasons why we should not use error codes instead or even in conjunction with exceptions (by using an error code to identify the nature of the exception).

  • When using error code, we must usually find a way to also return whatever the method is supposed to return. That either makes us change the signature and use (out) parameters or (worse) embed the error code and the returned type into another class/structure.
  • Another reason, and probably most important one, is that whenever we call a method that returns an error code, we must test it otherwise, we may let the code execute in an unstable state and eventually, propagate the error to upper layers of the application. On the other hand, if we throw an exception, it should be either explicitly caught or the application will simply terminate. This makes the code more robust since even if we forget to catch an exception, we will notice it while testing which is not the case if we use error codes and forget to check returned codes.
  • An exception contains much more information than an error code. It contains the exact location of the encountered issue and it should contain, in the message, the exact reason of failure and, for well designed exceptions, how to fix the problem. We no longer need to document each error code and manage error codes duplications. The exception contains all the information developers need to know.
  • If we want an error to propagate to upper callers in the stack, we just let the exception unhandled and it will be propagated to upper layers. Whereas using codes, we must test for the code, return it and do this for all the callers of the method at all levels.
  • Exceptions can be instrumented and monitored using standard applications in Windows.
  • When debugging, we can set the debugger to start when a certain type of exception is thrown.

Sometimes we see developers returning an exception instead of an error code (return an exception type as the return parameter of a method and not throwing an exception) as in the following code.

     public InvalidPayDateException CalculatePayAmount(out double pay) {}

Actually, this type of error handling defeats most of exceptions’ benefits that we just have seen. Thus, it is better not to return exceptions instead of error codes. That is pretty much the same thing as returning a simple code.

 

Throwing exceptions

As mentioned before, an exception should be thrown whenever the method can not complete its execution normally even if the scenario could be predictable in some way. Opening a file that does not exist should raise an exception for example even though we probably are testing for the existence of the file before opening it. Throwing an exception is like saying ‘Everybody above! There is an issue here and I cannot continue to execute normally!’ and this message will go up until someone (code) knows what to do about it to stabilize the code and continue.

A good place to raise exceptions is in the method pre conditions. Pre conditions are a set of tests on the passed parameters (or execution context) to ensure that all parameters are valid and we can continue to execute the method.  If we look at the following code, chances are that the method is called with an employee object set to null which will cause an exception thrown by the CLR.

    public class PayManager
    {
        public double CalculatePayAmount(Employee employee)
        {
            double payAmount = 0;
            if (employee.PayMode == PayMode.BiWeekly &&
                employee.LastPaid > DateTime.Now.AddDays(-14))
                throw new InvalidPayDateException("This employee is not supposed to be paid this week");
        
            payAmount = 2000; // of course, not everyone is paid 2000$
            return payAmount;
        }
    }

Now the same method with pre-conditions will throw an exception with more details (customized details that could contain much more information for developers).

     public class PayManager
    {
        public double CalculatePayAmount(Employee employee)
        {
            if (employee == null)
                throw new ArgumentNullException("The employee object must never be null");
            double payAmount = 0;
            if (employee.PayMode == PayMode.BiWeekly &&
                employee.LastPaid > DateTime.Now.AddDays(-14))
                throw new InvalidPayDateException("This employee is not supposed to be paid this week");
           
            payAmount = 2000; // of course, not everyone is paid 2000$

            return payAmount;
        }
    }

Of course, this is only valid for things we can check before the method does what it should do. In the core of the method, we may need to throw exceptions as well and it is very important to pick up the right exception type to throw being it an existing type or a custom type we create for the purpose of the application.

Choosing the right type to throw

One way to choose the right type of exception to throw is to use the caller perspective. In other words, we need to answer the question  “what type of information should the caller know in order to be able to do something if it catches the exception?”.

For instance, a method can throw several custom exceptions to indicate different issues and, on the caller side, even though these exceptions will be caught separatly (each type has its own catch statement), the treatment will be exactly the same. In such scenario, we will end up with a lot of code duplication as shown in the following code.

        public void DoSomething()
        {
            try
            {
                callMethod();
            }
            catch(customOneException ex)
            {
                log(ex);
                RollBackTransaction();
            }
            catch(customTwoException ex)
            {
                log(ex);
                RollBackTransaction();
            }
            catch(customThreeException ex)
            {
                log(ex);
                RollBackTransaction();
            }
        }

If we have considered the caller in our ‘doThisThing()’ method design, we would have thrown only one type of exception instead of the three and the caller code would be cleaner. This is not always possible, however we should always keep in mind how the caller will catch and treat different exceptions we may throw.

Creating custom Exceptions

I like a definition I have read in the ‘Framework Design Guidelines’ book: “Do not create an exception type if it can not be handled differently from existing .NET framework exceptions’. That makes the focus on how the exception is caught and not only on the nature of the exception as we may tend to do usually. In other words, if there is no added value for the caller in having a new exception type, do not create one and use standard exception types instead.

There are many “generic” exception types that can be used in different scenarios:

  • ArgumentInvalidException, ArgumentNullException and ArgumentOutOfRangeException
  • InvalidOperationException

If we consider these exceptions, we figure out that they may cover many cases of invalid arguments or, for the latest, an operation that we attempt to execute on an object in an invalid state for instance.

There are other exceptions that we should avoid using such as those thrown by the CLR (OutOfMemoryException, StackOverflowException and alike). Only the CLR should be able to throw these types. Another particular case is the System.Exception: this exception should not be thrown because it does not tell anything about the encountered issue. Remember that the type of the exception is actually the first indication of what caused it and using one custom type for the whole application is as inefficient as using the System.Exception base class.

Performance considerations

We must be careful not to have very common scenarios that provoque too many exceptions which will impact the performance. In such case, we should use one of the following two patterns:

Tester-Doer Pattern

Let’s say we do have a pay calculator that calculates the pay amount for an employee. To make things a bit more complicated, let’s consider that we do have weekly and bi-weekly paid employees. The PayManager may throw an exception in the case we try to calculate a pay for an employee that is not supposed to be paid that day.

Now, if we look at the following code:

    public enum PayMode
    {
        Weekly,
        BiWeekly
    }

    public class Employee
    {
        public DateTime LastPaid
        {
            get;
            set;
        }

        public PayMode PayMode
        {
            get;
            set;
        }
    }
   
    public class InvalidPayDateException : Exception
    {
        public InvalidPayDateException() : base() { }
        public InvalidPayDateException(string message) : base(message) { }
    }

    public class PayManager
    {
        public double CalculatePayAmount(Employee employee)
        {
            double payAmount = 0;
            if (employee.PayMode == PayMode.BiWeekly &&
                employee.LastPaid > DateTime.Now.AddDays(-14))
                throw new InvalidPayDateException("This employee is not supposed to be paid this week");
           
            payAmount = 2000; // of course, not everyone is paid 2000$

            return payAmount;

        }       
       
        public void CalculateEmployeesPay(ICollection<Employee> employees)
        {
            foreach(var employee in employees)
            {
                try
                {
                    var pay = CalculatePayAmount(employee);
                    //do something with the pay.
                }
                catch(InvalidPayDateException)
                {
                    //Do something
                }
            }
        }
    }

The first thing we notice is that every two weeks, there will be a number of cases where the InvalidPayDateException will be thrown because many employees should only be paid on a bi-weekly basis. This may have an impact on the performance and since it is a predictable scenario, we certainly should do something to avoid it.

One solution would be adding a method ‘IsValidPayDay(Employee)’ which will indicate whether or not the current date is valid for the current employee. Then, we will modify the method CalculateEmployeesPay in order to first check whether the current date is valid for the current employee and only in this case, do a pay calculation.


    ...
    public class PayManager
    {
        public double CalculatePayAmount(Employee employee)
        {
            double payAmount = 0;
            if (!IsValidPayDate(employee))
                throw new InvalidPayDateException("This employee is not supposed to be paid this week");
           
            payAmount = 2000; // of course, not everyone is paid 2000$

            return payAmount;
        }

        public void CalculateEmployeesPay(ICollection<Employee> employees)
        {
            foreach(var employee in employees)
            {
                if (IsValidPayDate(employee))
                {
                    var pay = CalculatePayAmount(employee);
                    //do something with the pay.
                }               
            }
        }
      
        public bool IsValidPayDate(Employee employee)
        {
            if (employee.PayMode == PayMode.BiWeekly &&
                employee.LastPaid > DateTime.Now.AddDays(-14))
                  return false;

            return true;
        }

   }

In this case, using the tester-doer pattern is legitimate and in fact, it may save us a number of unnecessary exceptions. However, there might be cases where the tester will be too expensive in itself; for instance a tester that will have to grab information from the database. Eventually, such tester will cost more than throwing exceptions and one way to avoid both, too many exceptions and an expensive tester, we should use the Try-Parse pattern.

Try-Parse Pattern

The Try Parse pattern consists in exposing a method and an exception safe version of the same method so that other programmers will be able to call it without paying attention to the exception. In .NET framework such pattern is used for the DateTime.Parse method. In fact, there is another version of this method called DateTime.TryParse which returns a boolean indicating whether the method succeeded or not and an OUT parameter that contains the resulting DateTime object in case it is successfull.

Generally speaking, when we use this pattern, we should provide both methods: with and without exceptions and both methods have the same name with a “Try” prefix for the exception safe one.

In our example, if we use the Try-Parse pattern, we add a TryCalculatePayAmount (same name with the Try as prefix) and as a result we will have the following code:

    public class PayManager
    {
        public double CalculatePayAmount(Employee employee)
        {
            double payAmount = 0;
            if (employee.PayMode == PayMode.BiWeekly &&
                employee.LastPaid > DateTime.Now.AddDays(-14)))
                throw new InvalidPayDateException("This employee is not supposed to be paid this week");

            payAmount = 2000; // of course, not everyone is paid 2000$

            return payAmount;
        }

        public bool TryCalculatePayAmount(Employee employee, out double pay)
        {
            bool result = true;
            try
            {
                pay = CalculatePayAmount(employee);
            }
            catch (InvalidPayDateException)
            {
                result = false;
            }

            return result;
        }

        public void CalculateEmployeesPay(ICollection<Employee> employees)
        {
            foreach (var employee in employees)
            {
                    double pay = 0;
                    if (TryCalculatePayAmount(employee, out pay))
                    {
                        //do something with the pay.
                    }
            }
        }
    }

Catching exceptions

Now, let’s talk about the other side of the exceptions. The catching. There are three main points to consider:

  • When do we catch an exception
  • What exceptions’ types should we catch
  • What should we consider when rethrowing and wrapping exceptions.

When do we catch?

We catch an exception only when we do something about it. The something about it can be anything like:

  • Solving the problem so that the program can continue to run.
  • Wrap the exception in another exception type to add more details about the context and rethrow it.
  • Stabilize the program and rethrow the same exception
  • Log the exception and continue as if nothing happened.

That is said, we should always avoid a Try-Catch-Finalize block with an empty Catch section. This will only hide potential issues. However, having a catch section that just rethrows the same exception can only be useful if the finalize section does something otherwise, we better remove the try-catch block at all.

What do we catch?

We should never catch the System.Exception exception (too generic) neither should we catch CLR exceptions (StackOverflowException, OutofMemoryException etc…). For the first, the reason is that catching the exception base type does not give us any information about what really happened. It is ‘blind’ catching which will hide all other sorts of exceptions (including CLR ones). For the second, the reason is pretty obvious: we cannot do anything to solve a stack overflow problem for example. In very rare circumstances, we may need to catch an OutofMemoryException but in most scenarios, we should not do it and let the exception go up.

The most precise in our catch statement we are, the more robust is our code because it will show us, in testing, what are the potential issues (exception types) that may arise from a particular method. If we ‘swalow’ all the exceptions, despite their type, we may feel that the code is robust but in reality, we’re just hiding real potential issues.

Rethrowing and wrapping

  • When rethrowing the same exception, we better use ‘throw’ (without arguments) otherwise, we will loose stack information.
  • When wrapping an exception, it might be a good idea to include the original exception as the inner exception (to keep some information about the original context).
  • Consider wrapping exceptions to give more details about the context. For instance, a caching component (that does file caching) may have a FileNotFoundException thrown when it tries to access a cache file. However, for its users, that might not have enough information to understand the exception. In this case, we should wrap it in a CacheFileNotFoundException (custom type) that contains information on the query we have done to the caching component for instance.

Conclusion

Exception handling is an important part of any software programming and it is very important to take the time to think about an approach before starting a new project. In this article I tried to go through the most important aspects we should take into consideration without going in too much details in some cases. For this reason I added the references below of two excellent books that, among other things, cover exception handling and programming rules in general.

References

  • (1) Framework Design Guidelines Second Edition (Conventions, Idioms, and Patterns for Reusable .NET Libraries) – Addison Wesley –
  • Clean Code (A Handbook of Agile Software Craftsmanship) – Prentice HALL –

The S in SOLID – The hell of Helper classes and helper namespaces and helper…


I recently came across a very interesting podcast about SOLID principles and wanted to write a very brief blog entry regarding one particular principe. The Single Responsibility principle which is the S of SOLID.

Sometimes, even experienced developers seem to forget about some OOP basics and one of the most common behavior I have seen is the lazyness (or something else… don’t know) to correctly name a class, a namespace or a project. Have you ever seen someone adding an Utilities project? Or another developer creating a “Helpers” namespace that contains “Helper” classes…

Well, if you look at these classes, they usually contain everything and their opposites. While I agree that finding good names is not always straightforward, I admit that my hair stand when I come across such classes or, worse,  whole projects because it is the starting point for the mess… “I could not find a container for my method, I put it in the XYZHelper class. After all, it helps achieving a goal, so it pertains to a helper class. I never came across a HELPER namesace or class in the whole .NET framework even though, there are helper methods which are not isolated in a helper class).” 🙂

The Single Responsibility principle helps to solve this common issue. Simply, check your class and find out what are the reasons that can make it change. If you found more than one reason, then, you should split it into two or more classes. And once split, the name of the new classes is more obvious because you have split the methods into subsets based on their responsibilities which makes them have something in common (which is generally a good starting point for naming a class).

That’s the S principle (very simplified).

For more information, check out the podcast on this link: http://www.hanselminutes.com/default.aspx?showID=163

How SCRUM helped our team


In 2008, I began work with a client on a new project. The client was a airline and travel agency that needed to rewrite, from scratch, their online travel booking application. The new website had the following primary requirements:

  • Improve end user experience, including performance and security issues;
  • Offer new set of products on the website (Hotel booking, car hiring and insurance purchase); and
  • The new application had to connect to a brand new back-end system.

After learning about their process and project, I suggested that we try a new approach: Scrum. My clients did not know much about Scrum. In fact, the only Scrum-like practice my client had tried was daily meetings. I insisted that using Scrum could help us build software more quickly and build it with higher quality. This was not easy to sell. The client had a number of questions, such as:

  • How can you accurately estimate a project with an iterative process?
  • How can you determine the delivery date of a project if you re-estimate it after every sprint?
  • How can your customer agree on an analysis and “sign it” if you do not have an analysis phase?
  • Isn’t Scrum just a cowboy development process since we do not have a detailed design phase?

I answered their questions or worked with them to find answers. At the same time, I did not pretend that Scrum could solve all of their problems. I did point out what they already knew:  their existing waterfall methodology, with its detailed estimates and phases, only gave the illusion that it could deliver a high quality product with all required features on budget and on time. Scrum, on the other hand, could mitigate some of these risks. After much debate, we decided to give Scrum a try.

Read more… (http://www.scrumalliance.org/articles/128-how-scrum-helped-ourteam)

Le développement itératif (French only)


 

Définition

 Le développement itératif consiste à livrer des parties d’un système ou d’une application à des intervalles réguliers. Ces intervalles sont appelés Itérations. Une itération est donc une succession d’activités couvrant l’analyse des besoins, la conception des parties du système, leur implémentation ainsi que leurs tests qui, activités, aboutissent à la livraison d’une ou plusieurs fonctionnalités qui feront partie du produit final.  

Approche classique (par étapes ou Waterfall en anglais) comparée à l’approche par itérations

 Par exemple, imaginons que nous avons un projet de développement d’une application en ligne qui offre 20 fonctionnalités différentes (20 scénarios). Dans une approche par étapes : 

  • On effectue une analyse complète pour élaborer et détailler tout les scénarios,
  • L’architecte livre une architecture détaillée de toutes les composantes de l’application,
  • L’analyse fonctionnelle et le document d’architecture sont transmis aux développeurs qui implémentent la totalité des 20 scénarios
  • On effectue les tests d’assurance qualité sur les 20 scénarios
  • On livre le produit au client pour des tests d’acceptation,
  • On fait les changements demandés par le client,
  • On livre le produit final

 Notez l’avant dernier point : Pour passer au produit final, il est rare que le client ne demande pas des changements. Cela souvent provoque des retards dans la livraison ou/et des fins de semaines sacrifiées à travailler sur les dernières demandes du client. Dans une approche itérative, on garde les mêmes étapes que celle de l’approche précedente sauf que ces dernières se produisent en dedans d’une itération dont la durée est fixe et de ce fait, se répètent autant de fois qu’il y a d’itérations. Par exemple, on pourra décider que les scénarios 1, 10 et 15 vont être développés dans l’itération 1. Pour l’itération 2, nous aurons probablement des correctifs sur les scénarios 1, 10 et 15 plus quelques autres scénarios tirés de la liste complète etc… 

Avantages du développement itératif

 L’approche de développement par itérations offre les avantages suivants : 

  • Elle s’adapte mieux aux changements. En fait cette approche considère le changement comme faisant partie du cycle de développement d’une application et non pas comme un événement intempestif,
  • Elle nous permet de détecter les risques très tôt dans la vie du projet,
  • Elle permet d’ajuster les choix en termes d’architecture ou de conception graphique par exemple, très tôt dans le processus et non pas après que ces derniers aient été complètement réalisés (et donc les heures déjà consommées),
  • Chaque itération est une expérience qui nous permet d’apprendre d’avantage sur les challenges que représente le projet. Par exemple, il est fréquent de revoir les estimations faites au début du projet après la fin des premières itérations,
  • On donne la chance au client de visualiser le résultat des itérations et donc l’occasion pour lui d’exprimer des ajustements au fur et à mesure que le projet avance et non pas à la fin uniquement lors des tests d’acceptation,
  • Le contrôle de la qualité se fait à la fin de chaque itération.
  • Les développeurs restent concentrés sur une partie des fonctionnalités qui font partie de l’itération courante. Tout changement ou correction qui s’ajoute à la liste des tâches, doit être planifié dans les itérations subséquentes. Comme la durée d’une itération est relativement courte, généralement, les clients et chefs de projets acceptent d’attendre ce délai
  • Le client est rassuré car il peut voir concrètement la progression du projet à travers la manipulation ou l’exécution de cas d’utilisations réels de sont produit

  

Règles de gestion des itérations

 Afin de gérer au mieux les itérations, il est important d’observer quelques règles dont les plus importantes sont : 

  • Fixer la durée des itérations au début du projet : Une itération doit avoir une durée entre 2 et 3 semaines. Il est fortement conseillé que la durée soit calculée en semaines pour que ca soit facile à mémoriser
  • Au début de chaque itération, tout les intervenants dans le projet, y compris le client, doivent se réunir pour discuter des l’expérience de l’itération précédente et déterminer le contenu de la prochaine itération
  • l’équipe de production doit présenter un produit à la fin de l’itération. On entend par produit, un ensemble de fonctions qui seraient utilisables telles quelles même si, dans la plupart des cas, on n’ira pas en production sans le reste des fonctions. La présentation se fait en utilisant l’application (il ne s’agit pas de présenter des Power Point par exemple)

 Évidement, il existe des exceptions à ces règles, surtout en ce qui concerne la dernière, dans le cas par exemple du développement d’une application serveur qui ne présente pas d’interface utilisateur et qui serait difficile à présenter partiellement. Aussi, typiquement, la première itération « Set Up » et la dernière « Livraison » sont un peu différentes des autres itérations. Dans la première, le nombre de rencontres entre les différents intervenants est souvent élevé et les livrables sont de type « documentation ». Dans la dernière itération, on s’afférera à corriger les derniers bogues et focaliser sur les procédures de déploiements (création d’une application de déploiement par exemple).   

Conclusion


Les avantages d’une approche par itérations sont évidents mais l’application d’une telle méthodologie nécessite plus de discipline qu’une approche classique ou les intervenants (équipe de production) ont une certaine durée pour livrer la totalité d’un produit, et en dedans de cette durée, il n’y a pas de moyen de mesurer de manière précise la progression du projet.

 À la question du gestionnaire de projet « Êtes-vous dans les temps? » les développeurs vont avoir deux réponses, dépendamment du temps restant. Soit un « Oui » si on est encore loin de la date de livraison. Soit un « Non » si on est à quelques jours seulement de la livraison. La marge de manœuvre est alors quasi nulle et il est trop tard pour agir ou négocier des délais supplémentaires avec le client. En se mettant des points de contrôles à la fin des itérations, le gestionnaire de projet peut évaluer lui-même la progression du projet et sa marge de manœuvre sera d’autant plus grande qu’il aura détecté les dépassements dans les premières étapes du projet. Aussi, cette approche permet de mieux intégrer les demandes de changements et les commentaires du client. Le fait de ne pas les recevoir tous d’un coup à la fin des tests d’acceptation permet au gestionnaire du projet de mieux planifier leur impacte sur la date de livraison du produit final.  

Lire aussi l’impact qu’a eu SCRUM sur notre équipe: https://bellouti.wordpress.com/2008/12/04/how-scrum-helped-our-team/