BradAppleton says: What Ive been suggesting is that when adding required functionality, one [should] deliberately [do so] in a way that wont pigeonhole you into needing major surgery for future changes whose nature could have been anticipated.
What makes a needed change difficult in a program? Only one thing, really: the changes needed are pervasive, spread all over the system. If they're localized, it's just no big deal.
That is certainly one thing, but not the only thing. Localized and well-factored code in one place may use well-factored and localized code in another place. There are still dependencies on the interface, and even on the usage model presented by the interface. Something that necessitates serious updating of such protocol affects all the clients of the thing (which in turn may affect clients of those clients, etc.). Even if its well factored, major surgery may still be necessary. (Hmmmn - is that like saying building the thing "right" may still need major surgery if you didnt build the "right" thing?) --BradAppleton
Wouldn't "serious updating of such protocol affect[ing] all the clients" be a case of pervasive changes? As mentioned below, the trick with protocol is having a tool that makes protocol changes non-pervasive. If they are ... deponent sayeth not.
Such a tool works great if the changes to be made are mostly cosmetic, changing a method signature for example. My point is not all pervasive changes can be effected this way. Even when the code in question is well factored, fundamental changes in architectural assumptions and design decisions and constraints and operational metaphors can necessitate pervasive change, no matter how good a refactoring job you do. Such pervasive changes will be more than just cosmetic and are non-trivial, even with VisualWorks
. Good factoring and tools certainly help mitigate this by reducing the likelihood and any rework effort, but they dont make the problem completely
disappear. Hence the benefit of applying YAGNI and OAOO to design assumptions and implementation constraints as well as to functionality. --BradAppleton
What ensures that changes will not be pervasive? These things:
- Ensure that any given capability is implemented OnceAndOnlyOnce. Then, when that capability needs changing, you go to one place and change it.
- Ensure that you have powerful refactoring tools, so that when a method name has to change (which will be pervasive), you can easily find all senders of the old name and replace them with the new.
In a properly-factored object system, no matter how simple, the probability is extremely low that the next change will require major surgery.
The result of this is that investments in future planning, or other forms of building for tomorrow, have an extremely high probability of being wasted. You'd have to be smarter than I am to do it effectively. It is fun, of course, but I'd advise only doing it until you need glasses.
I was almost agreeing with you until that last paragraph. There is no guarantee the investment will be wasted without knowing far more about it. Investments that make the code or the things its assumes "smaller" certainly pay off a great deal of the time. You wouldnt be spending time refactoring to meet OAOO if it didnt. Are you saying its extremely likely that applying YAGNI to implementation decisions and design constraints is a waste? --BradAppleton
We are pro-refactoring, as you know. We recommend against trying to predict the future while implementing today's feature. YAGNI says don't implement functionality for which you do not have an immediate need. Refactoring is changing code (some would say
code) without changing functionality. Refactoring doesn't violate YAGNI.
What we find is that Refactoring, which is a mechanical process involving no prediction of the future, is a more effective way of having the system ready for upcoming changes than is guessing. --RonJeffries
Okay, here is the thing. Applying OAOO and YAGNI to not just features, but also constraints and assumptions is not necessarily guessing or predicting the nature of what will be needed in the future. This is a crucial point. In the same way that precluding a feature helps reduce effort (now and in the future) without guessing if you will need that particular feature, precluding a constraint or assumption can reduce effort without guessing whether or not it is needed in the future. You are simply forcing yourself to more deliberately question which of those assumptions/constraints are
Its easy to code something up and make unconscious decisions and assumptions as you go. You havent necessarily added features
, but you have added, or depended upon information whose necessity and legitimacy needs to be questioned just as much as some new whizbang feature. Its not guessing the specifics about
what will be needed; But it does recognize that change is inevitable. So regardless of whether you are adding functionality, or assumptions/constraints,
you can still know that future evolvability is improved by removing things that arent presently necessary.
The difference is that adding whizbang features tends to be more conscious and deliberate, so you can more easily apply YAGNI before you code. But many assumptions are made unconsciously or subconsciously while you are "in the flow"
(or a coding episode). Hence for these things, YAGNI may not be able to reasonably occur until after the fact. So you remove rather than add.
Something that eases the present implementation might be easy to assume.
The question is whether it is safe to assume for both now and
for the future. It's not guessing the future, it's saying that since you cant, maybe
you shouldnt leave in the assumption or constraint.
In this sense YAGNI balances OAOO (which is a nice piece of synergy to OaooBalancesYagni
): Not only are you ensuring things are said
, you are ensuring that they even need to be said
(or implied or assumed) at all.
At OOPSLA '98, I was having a chat with someone who was arguing that there were "some changes" that you had to think about in advance or your software couldn't possibly cope with them. I was arguing that a properly-factored system is ready for everything.
I swear this is true: he offered a possible change to the payroll. I said we had actually had that one, and here's how we did it. He offered another, I told him how we'd do it. This went on a few times. Finally he said: OK, but what if they came in and told you they wanted an air traffic control system instead of a payroll.
If I were compelled to convert a payroll system to an air traffic control system, I would still rather start with a well-factored
payroll system! --KielHodges
Believe it or not, I've actually had something very similar. The system was
an air traffic control system, and they decided they wanted to sell it as a factory scheduling system. So I can say with some confidence that it doesn't really matter whether your system is well-factored if your business-planning is suicidal. --PeterMerel
Reminds me of a CS professor at the Univ of Utah circa 1974 who, reporting on studies that showed that the number of code lines changed is approximately equal to the number of lines in the program, added, "So, in principle, evidently, you can start with any program and simply evolve it into the program you need :-)" --AlistairCockburn
Why the smiley?
And is it Alistair's or the CS prof's?
...from Alistair:: the smiley is inside the quotes, so it is the prof's. Saying that you can take any 10,000 line program and change 10,000 lines and get the 10,000 line program you need is (obviously?) facetious. My off-the-wall guess would be you would change on the order of 30,000 lines to turn it into the 10,000 line program you want (don't you ever change your mind while programming? ever heard of refactoring?) However, he was trying to make two serious points, one about the funny conclusions that can be reached from statistics, and another, raising the validity of starting from a different, existing program.
I think many of us do start from an existing, different program, and change all the details to end up with the program we want... saving lots of think and debug time by starting from a working structure. It reminds me a lot of a wonderful story by JorgeLuisBorges
about a crime committed and reported to the police, in which only all the facts were different as reported than committed, but the basic offense and sense of humiliation were true and accurate (I no longer have this book, but I'll try to find it). The woman protagonist effectively took the crime, and rebuilt a new crime on the existing structure, changing, as it were, all the lines of code, but leaving the sense and structure intact.
I was thinking about this same thing at OOPSLA98 during all the architecture discussions. Everyone wants to believe that software is infinitely malleable; well it is, but sometimes it costs a lot less to start from scratch. It is an interesting area to consider.. under what circumstances to start from scratch?
The odd idea that I had, because I've been thinking about ValueObject
-s a lot, is that systems have some ValueObject
-ness in an odd way. Consider a Date class. If you change a date object is it the same date changed or another date? Since date objects are typically ValueObject
-s, the answer is the latter. Now consider systems. People who create systems have values, and the values tend to permeate the systems. When the values of the system commissioners change sufficiently, it may be time for another system.
It's a pun on "value", but I think this way. Actually, I could link the values of the commissioners to the values=properties of the system. The values of the system reflect the values of the commissioners. So many of the multiple definitions of words have the same deep root meaning or some linkage. It is fun to find it.
"Ensure that you have powerful refactoring tools"
- you have to watch out if you can't do that. For example, where dependancies cross release boundaries. You can't just rename a method if you must remain compatible with DLLs already in the field which are hardwired to use the old name. You can't mess about with file formats if other programs, outside your control, need to read them.
At the edges of your control, both physical and temporal, you have to use foresight. (Probably the correct way to deal with this is to push the problem up to the UserStory