Regarding KentBecksChangeCostXpArticle,
Issue #1: Eliminating functionality does not disprove the exponential cost-of-change curve.
The example given in the article demonstrates that in a well-designed system with good test cases and high reuse, you can disable existing functionality in constant time. This has nothing to do with measuring the substantial cost of adding functionality to meet requirements that were missed early in the project. It doesn't even include all the costs, like the time to change the interfaces and refactor.
Consider what would have happened if you had limited the system to one credit and one debit at the beginning, and discovered during a month-end closing that "N line" transactions were needed to post subsidiary ledgers. The change would not be nearly as easy then. And, in the panic of the moment, one would likely be forced to implement a crude hacked-up "solution" that corrupts and devalues the data - like splitting the transaction into a bunch of little transactions that comply with the "two line" limit. Compromising the data often leads to serious problems later, that can't be fixed by refactoring code. -- JeffGrigg
This assertion is not borne out in C3's three years of experience enhancing well-factored code. I would expect the cost of change to be much higher in a system that did not adhere so well to OnceAndOnlyOnce. -- RonJeffries
We fixed a requirement error. The requirement stated that there were two 1-N relationships between Transaction and TransactionComponent?, one for debits and one for credits. This was just plain wrong. The exponential curve predicts that the change we made should take 40 weeks. We did it in 30 minutes.
P.S. Once we made that change, we were able to inline TransactionComponent? entirely in Transaction and remove duplicate information, then extract a MoneyObject, which made the Transactions capable of supporting UnitLinkedLifeInsurance? where a debit or credit could be shares of a MutualFund? instead of money. Each of these subsequent changes also took about the same amount of time. However, we weren't smart enough to see all three changes as a unit.
-- KentBeck
Disabling functionality is a change. However, it is a special case: It's easier to comment out or disable functionality than design and code new functionality. If you had implemented the 2-line limit, and later discovered it invalid, you could not have gotten away with making a change that did not change the interface at all.
To go from 2 to N lines, you'd have to provide a more general interface, and then find all the places that need the new interface and upgrade them. Fortunately, in a well-factored OO system, few of the existing users of the interface will need to be upgraded. But with "N" classes in the system using the existing interface, how could you find the few needing an upgrade without looking at them all? -- JeffGrigg
You couldn't: you'd look at them all. Or your refactoring browser would. Or you'd implement a new N-way interface, feed the two-way into it, and let the existing two-way guys stick with two. And of course, you would [rightfully] trust your tests, new and old, to find problems. -- RonJeffries
Going from 1-1 to 1-N in this case is just the reverse of what we did. I would change the interface first to accommodate 1-N and leave the implementation alone. Then I would change the implementation. Then I would start using the new interface. It wouldn't cost any more or less than what we did.
Think for a minute about what all the refactorings together accomplished. We went from an accounting system that only handled currencies, and only one per account, to an accounting system that handled all manner of securities, including any number per account, and we did it in a few hours at low risk with a running system and hundreds of thousands of existing transactions. -- KentBeck