 |
|
 |
 |
Past the Point of Simplicity: Has the Agile Movement Gone Too Far? |
|
"When Agile Development was introduced many were shocked at the 'bare bones' nature of it all. Gone
were many of the 'high ceremony' steps that managers and developers alike had become accustomed to.
Instead, like an onion, layers were peeled away..."
-Damon W. Carr
June 14, 2005
|
|
History Speaks |
|
When Agile Development was introduced many were shocked at the 'bare bones' nature of it all. Gone were many of
the 'high ceremony' steps that managers and developers alike had become accustomed to. Instead, like an onion,
layers were peeled away that were declared 'waste' and discarded until Agile reached what it believed was the
core of what developers and teams really needed at the most bare and essential level to successfully deliver
working software.
After all, working software is the only real thing that your business stakeholders will judge your efforts as
successful or not (they could care less about the internal structure – a huge problem in that it creates a
negative psychological environment for the development team and an issue I deal directly with in this article).
What about maintainability, scalability, code reuse, internal robustness, component use, etc. etc. etc.? I will
address this head on in this article. Has the Agile movement gone to far in its near obsessive love of
simplicity and reduction of ceremony at the expense of unwittingly encouraging teams to throw out common sense
good object oriented design in an effort to 'do the easiest thing that works'?
I am, first and foremost, a practitioner, trainer and coach on Agile Development (I happen to implement in the
.NET/C# environment mostly in Financial Services). However in my 6 years of 'seeing the Agile light' I have
evolved a sense of wanting in the past few years for some of the necessary and missing elements of the Software
Development Lifecycle (SDLC) that Agile removed. Most of it I was glad to see go (like up front Analysis at the
micro level – A sure disaster in almost all situations). I have found myself 'adding back in' some new elements
into my company's Agile processes and I will share one of the most important here in this article.
These additions are a combination of some of the old and well known items, but mostly these are new concepts
that we have invented and found add dramatically to the effectiveness of Agile Development. These are all
covered in our upcoming book so if this article was useful to you, the book contains many more great add-ins to
Agile (a process we call 'agilefactor'). For example, Metrics in Software have gone though period of being
'in style' and not. I have always been a big fan of metrics if they can be measured with a minimum amount of
disruption and can add significantly to the managers (and the developers) understanding of the status of their project (or of a collection of projects).
|
Necessity - The Mother of Invention |
|
Before I explain one of the most important new metrics that we have invented and firmly entrenched into our
Agile process let me explain why it was needed (necessity being the mother of invention). What I have found
over the years on many Agile projects is that Agile projects (even Agile projects that embrace Pair Programming,
a practice that unfortunately, due to the ignorance of management typically, or a development team that does not
understand Pair Programming) and falls victims to its many myths (to fix this I highly recommend the book 'Pair
Programming Illuminated by Dr. Laurie Williams and Robert Kessler - Addison-Wesley Professional; 1st edition
(June 28, 2002) ISBN: 0201745763) tend to evolve their code over iterations into a state where concrete class
after concrete class is created in an effort to 'do the simplest thing that works', an Agile axiom. This is
often done with a disregard for code reuse and proper Object-Oriented design.
What they are doing is NOT the easiest thing that works in the long run (perhaps it might be in the short run)
because they are often creating havoc inside the system in by throwing design principles out the window. Even
when you have collective code ownership and a pair knows that there is another similar class that could be
leveraged, and code reused, and that they really should create an abstract parent containing all that shared
code, it all seems like too much work so they create another concrete as a stand alone class with no inheritance
or polymorphism (when they should of moved the common code into that new abstract parent, assuming it was a
semantically correct thing to do, and inherit these two classes from that abstract parent, overriding
functionality where necessary).
Polymorphism's magic will take care of the rest so when you call the method 'GetDiscount' it will know (for
example) that a class called 'Corporations' (a concrete class) returns a different result then the GetDiscount
present on 'Individuals', another concrete class inheriting from the same abstract parent 'Customer'. Perhaps
'Customer' has a method called GetDiscount marked abstract. This is a terribly simply example but usually what
happens is the developers are lazy and execute something to the effect of:
“Lets just create another class called Individual and we can add Switch/Case statements in our code by checking
the type of the object to get the functionality we need.”
This is so wrong, I don't know where to begin. This problem is disastrous and it is the exception, not the rule,
where an Agile team 'does the right thing' and bites the bullet, refactoring their code in Design Patterns as
soon as the need arises.
In my book, this is how I determine a team can reach the holy grail of the promised Agile 'Flat Cost Curve'
where the cost of change over time is a near constant. They achieve it by a near obsessive focus on 'Refactoring
to Design Patterns' and 'Refactoring' in general to good Object Oriented Design. The extra work now pays off
massively later as I will show. The reason almost no Agile teams can even come close to the flat cost curve is
the fact that they:
- Have not instituted a mandatory 'Design Pattern' mastery in each team member
- Do not have a systemic way to 'Refactor to Patterns' when the opportunity presents itself in each iteration in a near obsessive manner
|
Scenario One |
|
Here is a perfect example: Your team is following all the main tenants of Agile. You are using TDD, you are Pair
Programming, but your average team member does not know the difference between a 'Strategy' and a 'Decorator',
both design patterns popularized in the book 'Design Patterns' Addison-Wesley Professional; 1st edition (January
15, 1995) ISBN: 0201633612'.
Is this harder? Perhaps at first. Is this more intrusive on your system and does it have the chance to break
more code? Absolutely. But that is why you have TDD and Continuous Integration. These practices are precisely
why Agile works! They allow you to make these kind of sweeping but necessary changes to your technical design.
If you break something you will know immediately due to red TDD tests and you will be able to fix them
immediately.
With Continuous Integration (see Agile in the Trenches for a discussion of this)
you will know if there are problems at the System level with the build. This is why Agile beats the other
Software Development Processes in almost all cases (as measure by Scope, Cost, Time and Quality) because you
can 'embrace change' and make the hard changes, immediately knowing where you need to fix problems. Other
processes offer you no such 'safety net'.
The problem is that so many Agile team are not engaging in the very thing Agile is meant to facilitate because
they don't have the proper knowledge and if they do, they don't put in the effort to do the right thing. I can
say this with confidence as I have seen it in countless Agile projects all over the world. Radical and sometimes
dramatic change to your system without hesitation should be the rule, not the exception when it is required. You
have your safety nets to allow you to bite the bullet and do the right thing so why are so many teams creating
horrible technical designs that degrade dramatically over time?
This is a simple example but it illustrates the problem very well. Instead of coding proper object oriented
code, teams are 'banging out' code as fast as they can with no regard for proper design and no regard for code
reuse, in many cases copying and pasting code between classes (always a sure sign of a problem). They feel
justified by the 'Do the simplest thing that works' mantra of Agile, not understanding that this is not a free
pass to destroy the integrity of your architecture. In fact this is NOT the easiest thing that works, as you
will pay dearly down the line (and this is why the flat cost curve is a myth for almost every Agile team I have
not integrated these new metrics for which I will describe shortly).
|
Scenario Two |
|
Another example would be the 'Strategy' pattern where an algorithm is abstracted into a class and logic is used to
determine the appropriate instance to create and the appropriate method that implements the 'strategy' algorithm.
Imagine you have a fairly complex algorithm for life expectancy. You start with 'Single Man Non Smokers'. Later a
revised algorithm is defined for 'Married Men who are Smokers'. The combinations can be endless.
This can be solved also (and perhaps more elegantly) by the Decorator pattern. The problem is, in my experience it
is very rare to find teams that have a collective knowledge of design patterns where any member can even explain to
you the difference between these patterns, so how can they be expected to 'do the right thing' and do the
refactoring into the pattern when the need arises? The answer: Require a mastery of Design Patterns for all of your
team members.
It is inexcusable to allow a team in an object-oriented environment to exist without requiring all members to master
these patterns, now over a decade old. You are throwing money away if you ignore this and there is no other way but
to bite the bullet and buy them books, send them to training, hire a consultant, or whatever it takes. In my book I
determine this is the #1 most important critical success factor to Agile development.
Once Design Patterns are learned, require your team to master the excellent book 'Refactoring to Patterns' by Joshua
Kerievsky (Addison-Wesley Professional (August 5, 2004) ISBN: 0321213351). If this is too much for your team,
appoint a 'Lead Architecture Sub-Team' to master these concepts which is a subset containing your best people. In my
research I show that this is the only way I am aware of for your team to routinely reach a flat cost curve: the
obsessive refactoring to patterns as a core part of your Agile process, iteration by iteration. If you are unwilling
to put in the learning then you might as well resign yourself to throwing money away and building systems of far
less quality, as statistically that is what will happen with 90% probability.
OK so let's say you have mastered patterns, you are refactoring to patterns and you want to see how your system is
evolving over time (or devolving). How can you measure your progress, iteration to iteration, and know you are on
the right track?
|
The Answer |
|
The 'Core Agility Ratio' or CAR, the very topic of this article is the answer. This is a metric invented by
agilefactor and used extensively on the Agile projects we help manage and execute. It is a deceptively simply metric
and again, goes a bit against the Agile grain which seems to shun metrics. This is a metric I do not believe you can
do without after you become accustomed to using it.
Now that I have explained why you need it, let me explain how you can calculate it and use it. The CAR breaks things
down (from a UML perspective) into 'bad' collaborations between items (in UML these are relationships and
dependencies) and 'good' collaborations. A good collaboration is when a concrete class has a relationship or
association to an abstract class or an Interface. It is very much in line with the concept of 'design by contract'.
When you 'Refactor to Patterns' (and you should not perform refactorings until they are necessary – a key point to
keep in mind – don't try to predict the future). For example, if you knew that Individuals were coming later on in
your project, you should NOT create the abstract class immediately. You should only refactor into this proper Object
Oriented Design on the first occurrence that would necessitate it. Anything else would be predicting the future. How
do you know that Individuals will later on be removed from this system based on an executive decision down the line?
You don't, so don't try to predict the future.
There has been some lively debate between myself and some industry luminaries around 'should you perform a refactoring on
the first occurrence that would require it, the second occurrence, or which? Ron Jeffries for example has been an
incredible sounding board for me on this topic and I am in his debt for his insight and amazing wisdom around these
issues.
In my experience, in over 80% of the cases, when a first occurrence emerges which would necessitate a 'hard'
redesign', there will be a second occurrence later on. If you are sharp you might be saying 'hold on a minute...
Aren't YOU predicting the future?' In a way, yes and in a way no.
The refactoring will benefit the system NOW. Even if that third occurrence never comes, we have still benefited.
However the odds say it WILL occur. So I am doing something NOW that improves the system and has a statistical
likelihood to bring major benefits in the future. If they never come, we have not lost anything. We have simply
spent time improving our architecture.
The debate is 'what if that refactoring was overkill'. This is up to the people 'in the trenches' to decide if the
refactoring is worth it. Perhaps you know 99.9% that there will never be that third occurrence. It might be OK to
not perform the refactoring. Like most things in life 'it depends', however as a rule of thumb you SHOULD perform
the refactoring if you are unsure as the statistics speak for themselves.
Therefore I firmly recommend you perform the refactoring on the first occurrence that would require it. This is
important because adding a participant to an existing pattern, or properly implement object oriented design, can be
as much as ¼ the effort of creating the item from scratch. This is due to the code reuse you are leveraging (i.e.
PROPER OBJECT ORIENTED DESIGN). This is the secret to the flat cost curve. I believe I am the first to crack this
puzzle and provide a solution that works more times then it doesn't in getting you to that flat cost curve.
Let's take a worst case scenario using a totally different design. Imagine the following class structure in UML for
a system that generates Invoices and notifications based on information from a ledger. All of the classes know about
the other classes (a design that appears to be abysmal, but you never know when a design like this is mandatory for
reasons we are not aware of).
Here we have a design where any change to any class has the potential of breaking ALL of the other three classes.
This is hardly a robust design capable of withstanding the rigors of production and on-going maintenance without a
lot of heartache. We have a combination of one bidirectional UML relationship and five bidirectional dependencies.
All of these classes are concrete and clearly appears to not be an acceptable design. What can we do?
Design Patterns to the rescue! The Mediator pattern is documented and common knowledge among those that have kept
pace with Software Engineering improvements. It addresses just this sort of problem. The Mediator will eliminate
these dependencies and relationships and create a single 'Mediator' class that oversees the communication between
all of these classes so they do not need to be aware of each other (information hiding is good).
Before I move on let me define the Core Agility Ratio (CAR) and what it would calculate as in the example above.
Keep in mind, our goal with the CAR is to keep it below 1 or as close to 1 as possible (or as low as possible).
|
CAR Definition |
The definition of the CAR is as follows:
- SUM the total number of UML dependencies and UML relationships between concrete classes. Bidirectional counts as 2. There are “Bad Relations”. We do not count 'concrete to Abstract Class or Interface'. We also do not count composition relationships, aggregations, or generalizations; this is the numerator of our ratio.
- SUM the total number of participants in our system that have non-trivial behavior (for example, an enumeration would not be included). This is the denominator. We include Abstract Classes and Interfaces as well as concrete classes (and other elements like structures).
So using this definition, here are the answers:
1) Total Number of 'Bad' Collaborations = 12 (Remember bidirectional counts as 2)
2) Total Number of Participants = 4
As the CAR is a ratio it is:
Given our stated goal of having a CAR below one (or close to it) we have failed miserably. And that is good, as it
shows the ratio is working as we can look at this design and know it is bad.
So how does this look when we refactor it using a design pattern?
Here is the new and improved design using a Mediator:
All information that might be necessary from an early method call in a later call is persisted in the mediator. For
example, if the 'Total Account Balance' is needed by the 'Notification Engine' that value could be returned in the
call that is made from the 'Transaction Mediator' to the 'Ledger'. Then, when required, the value could be passed on
as a method parameter to the Notification Engine.
NOTE: You are cheating if you pass 'this' or an instance of an object as a parameter. You then create a
bidirectional dependency as you would not pass it if you were not going to perform a call back, creating a
dependency between the called class making the callback. The collaboration just became bidirectional.
This is a simplified example but try it yourself. Run though some refactoring to pattern examples and see for
yourself how the CAR is lowered. It is really nothing incredibly new as it simply encourages a 'design and code by
contract approach' or the introduction of classes to dramatically reduce coupling and increase cohesion (remember
those two key concepts)?
I almost forgot. Let's see what our new CAR ratio is with our 'Refactoring to a Design Pattern' (in this case a
Mediator).
I purposefully used an example that did not involve a refactoring to an abstract class or an interface as I wanted
to illustrate how you can lower your CAR by adding MORE concrete classes in some cases if they reduce or eliminate
your bad collaborations.
In closing I will illustrate our UML example discussed previously with Customers.
Here is the concrete class that lacks the proper refactoring:
NOTE: I added UML Cardinality to this model. The 0..* shows that any of the Customers can have zero, one or many
addresses and zero, one or many Invoices.
So why is the numerator zero? There are no 'Bad' relationships. Remember, we do NOT count relationships or
dependencies between concrete classes and Abstract Classes or Interfaces. The other three relations are 'Generalize'
relations (for the three children of Customer) and are not counted in the CAR.
|
Summary |
|
In summary, Agile IS about eliminating waste and getting things done much faster. But remember that your
architectural integrity is critical to achieving the 'flat cost curve' of change over time. Just imagine the
difference in adding a new Customer type compared to adding a new concrete class with no parent.
For some reason Agile Development has caused many to abandon common sense object oriented design principles. Don't
let that happen to you, and use the CAR ratio to help you achieve your project goals and ensure your system's
architecture is not degrading over time. Also require your team (or a subset) to master Design Patterns and
'Refactoring to Patterns' using the resources listed here. As always feel free to contact me directly
for more information.
|
About the Author |
Damon W. Carr
CEO and Chief Technologist
agilefactor.
Damon Carr is the CEO and Chief Technologist of agilefactor, based in NY, New York. The company continues its growth
via Microsoft.NET Leadership, Software Engineering best practices Agile Process Evolution and the new book by Mr.
Carr covering "Agile Software Factories" to be published in early 2006 by Addison-Wesley. Prior to joining
agilefactor, Damon was the CTO and Co-Founder of Monetaire, a global company with offices in Europe and the US,
where he spearheaded all technical innovation, including their flagship product which was named a 'top three
solution' in Europe by Forrester Research. Damon continues to speak at the software industry's most prestigious
conferences including the upcoming 'Better Software Conference' (http://www.sqe.com/bettersoftwareconf/) where he
is a featured speaker on 'Agile Processes and Software Factory Evolution', he also leads various high profile global
user groups.
Contact the author.
|
|
|
|
|
|