There is quite a prolific myth that seems to have a grip on the collective software development industry. Often in communication with another software developer, the notion of a “change in requirement” will surface and I am forced to remind that developer that no such thing ever occurs – that the internalisation of this logical fallacy is in fact, quite detrimental to the purpose of creating software, fulfilling software requirements and should be abandoned. Since no such thing as a software requirement change ever occurs, the remainder of the conversation is invalidated making communication very difficult. Who’s responsibility is it to establish a sound knowledge in the fundamental concepts of their selected profession? Not mine – I am forced to cease the conversation. After all, I don’t explain to my doctor what my problem is – only my symptoms – hence, I am not a doctor. But I’ll bet doctors speak in a “language” that is beyond my capability of comprehending (though I have picked up a term or two having lived with a midwife for years
). We software developers should be doing the same, but it does not seem to be the case.
Software is a function from some given inputs to some given outputs (a lambda if you will) and importantly, given some inputs, the same output should always result, always. It should not return some other result; it should not format your hard disk every second time and it shouldn’t cause the universe to implode or anything crazy like that – just consistently return the same value for its given inputs. This property is called referential transparency. Let’s look at a specific piece of software called ‘touch’. It takes as input:
- a file system
- a list of characters
It provides one output – a new file system. If a file of the given name exists on the given file system, return the new file system with that file having an updated modified time stamp, otherwise, return the new file system with an additional empty file of the given name. That’s it – nothing more to it. We might even express this software more formally as follows:
-- "Given a FileSystem,
-- then a list of characters (Char),
-- return a new FileSystem."
-- :: means 'is of type'
touch :: FileSystem -> [Char] -> FileSystem
touch fs filename = if fs `contains` filename
then fs `updateTimestamp` filename
else fs `addEmptyFile` filename
The ‘touch’ software is so fundamental, that most reasonable operating systems provide it without any explicit user intervention – try running it and see.
While you are free to come up with all sorts of wild and complex examples of software, I’m going to simplify a little with a new example. Suppose a client approaches you, a software developer, on Monday, with the following requirement; “given two integers, sum them, multiply by 4 and give me the result.” Here is the software written in Java:
int software(int a, int b) {
return (a + b) * 4;
}
Easy peasey! On a side note, write it in Haskell, then play ’spot the difference’ with the client problem statement and the code. Fun hey?
If the client approaches you on Tuesday with the following, “given two integers, subtract the first from the last. multiply by 4 and give me the result.”, then it is imperative to understand that this is a new requirement. A software requirement holds from inception until the collapse of the universe. You might argue, “but the old one and the same one look almost the same”, in which case, feel free to write the second one by applying your favourite delta compression algorithm, but by no means should the former be replaced by the latter. You might also argue, “but the client said it is a change”. Well I hope my doctor doesn’t follow any advice that I give him on my condition at the time since I am not well versed in medical practice. I do not say, “I have a headache, I need to fix it with these drugs” or if I did, the doctor, being aware of my naivety, would ignore my advice and only consider the symptom (I would hope!). Likewise, clients do not project their false view of software into their final product and instead, trust the better judgment of a more trained practitioner (ideally).
If we take the common (not that I accept its legitimacy in this form) approach of unit testing and I write assert(software(42, 43) == 340), then this statement should hold forever. If it does not, then you have broken your contract to all clients of your software. The typical refutation to this assertion involves the developer ascertaining that no software clients have ever existed and so it is safe to continue. For example, if there is even a slight suspicion of a client, that developer might first deduce that if there is, then those client(s) are in the room. Then that developer asks all people in the room to commit their code so that she can search for occurrences of its use. If none are found, the software is removed, code committed and all persons in the room are instructed to update. Otherwise, if it is found that one or more clients exist, manual contingency measures are engaged.
The regularity of the occurrence of no clients bound to your software is often over-estimated. Just take a look at the mess that Microsoft has itself in with an attempt to ’separate out the components of Windows’ or any of the other countless examples that surround us. You might say, “but I am a consultant with only one client”. This is not true; in fact, the client/provider relationship can often be self-referential. It is entirely coincidental that no clients have bound to your software, but rarely is it so.
“you have broken your contract to all clients of your software”
Using the same logic back at you, the clients should not assume that a new version of your software is compatible with an old version – it is not just a new version, it is a new library. It is entirely coincidental that much of it has remained compatible.
More seriously, it’d be interesting if it was practical to use more than one version of a library simultaneously from the same codebase – I expect with the OSGi model it is.
I agree, except even more so. If you have broken the contract with your clients, then it is not “just a new library”, it is entirely new software. Did you know that Java 1.5 is an entirely new programming language?
Your qualification of “more seriously” is not necessary – it already is serious. The problem is defining the bounds of the client contract. In a leaky language like Java, it is entirely possible that renaming a private class field breaks contract – since clients are free to perform logic based on that name. Even changing a class from non-final to final or adding a different return value for some given inputs e.g. throwing an exception.
Your perspective of “new library” – that I assume aligns with my existing definition of “new software” – is an extremely important aspect of writing software across the axis time. This lack of decomposability of the “software”, when written in poor languages, is exactly the reason why there is now a general tendency towards more powerful (no not Lisp PG! (or Ruby/Python for that matter)) programming languages for expressing that software. I just hope that this tendency reaches a large extreme before one of the big players come along and halts it (as they do) with something like F#.
You should really just write an article about referential transparency and how functional languages are better, instead of dressing it up in confused philosophical arguments. When we say “the requirements changed” we don’t mean “the definition of the concept ’sum two integers and add four’ changed” we mean the client no longer thinks that concept applies to their business situation, and please make sure that computation is not performed for them.
While, formally, you’re correct, I wonder what’s the point in the distinction between “software requirements changed” and “we need to develop a similar product with slightly different features”?
As Dave pointed above, softdev clients don’t think in terms of software (or lambdas). They think in terms of “business problem” that requires a specific “business solution”. When the business solution, they envision, includes a software component, they go to, say, us and ask for a development service. When a business problem (or understanding of a business problem, which are quite the same things) changes (e.g. the solution is thought up in more details) then they ask to develop a little bit different.
So, I don’t see quite much of added benefit in your distinction. Where I can use it? In communication with a client? They simply won’t understand us. Besides we already invested much effor explaining that if they request something new or changed, the whole development plan (and budget, of course) should be changed.
Use to communicate within a team? We already know that what we develop is a seria of snapshots from requirements to the tests to the code. If something changes, we have to make a new snapthot(s)
Please, explain?
You’ve merely redefined the term “requirement” in a manner that makes it useless for talking to actual customers about their problems. “We don’t sell sprockets anymore so you can remove that from the software requirements.”
“I’m sorry: I can’t remove that requirement. Requirements never change and are never removed. You COULD add a new requirement that says that it is now impossible to buy sprockets, but that would put the total requrement set into a state of contradiction. Therefore you must continue to sell sprockets.”
“Likewise, clients do not project their false view of software into their final product and instead, trust the better judgment of a more trained practitioner (ideally).”
I would like to have your clients. Mine just demanded a /different/ database schema two weeks before delivery. I’ll leave it to the philosophers whether it was a new schema or the same one with some grubby patches on it.
Speaking of philosophers, I just read … um… Kant?, who claimed it was immoral for one generation to bind the next into a contract. Not exactly relevant, but an amusing coincidence for me.
Hmm… when I change my mind, is it the same mind, or a completely new one that just looks a bit similar?
– J (Ex banana bender)