Sunday, January 28, 2007
Value and Learning
We develop software to deliver value (to the customer and to our own company). Unfortunately, that value is often not well defined, measured, and planned.
By planned, I mean that our modeling, implementation, testing activities should be somewhat arranged so that at any moment, if we have to stop and deliver, we'll deliver the maximum value. This is easier said than done (see also another old post on features Vs. value. In many cases, the planning (if there is any planning) works exactly against this aim. Time is initially spent on some (hopefully) important infrastructure, so a value / time chart would look more like

than
.In a sense, a lot of well-intentioned agile guys are just aiming at bringing the value chart closer to fig. 2. However, I see many cases where this is simply not possible, as there is no sensible way to split your task in smaller chunks with a measurable value for the customer. We can pretend that it is not true, and shoehorn those projects into a process built around quick delivery of small features; I don't think that's particularly clever.
What should we do then? Well, my current answer would be: if you can't optimize your plan for value, optimize for learning.
Now, again, a very common stance in the agile camp is that learning comes from frequent releases and user feedback. In this sense, optimizing for learning won't help much when optimizing for value is not an option, as you need to deliver something to the user (therefore some value) to learn anything.
However, learning doesn't come uniquely from the users. It's just a matter of what you need to learn. If you need to learn about requirements, or more broadly about the problem, users' feedback is certainly a good way. Still, you may have something to learn on the technological side as well. For instance, as we have very few workable quantitative methods, performances are often determined experimentally. Therefore, optimizing your plan for learning requires that you invest your time to get, as soon as possible, a rough approximation of performance figures. In other cases, we're not after performance but after precision, or we need to familiarize with some COTS components or libraries, or with legacy code, and so on.
There are many opportunities to revisit a plan to improve the learning curve. So, even in some worst-case scenario, you may find your value curve to look like fig. 1, but you can improve your learning curve to look more like fig. 2. That won't be so bad after all :-).
Note that all this makes sense only if you (and your company) accept the idea that a plan is a working tool, not a promise cast in stone. Otherwise, learning won't help make better decisions, steering the project, and taking chances along the road to optimize value.
Some homework: what is the role of design in different value/time and learning/time scenarios? Where, and how, it is ideally expected to take place? How is all this stuff related to the dog / skunk / colt / cow / bull classification of projects from Todd Little? Can we use it to rethink a bull project as a colt + cow?
Monday, January 15, 2007
Leaky Abstractions and Tacit Infrastructures
Although Joel's law ("All non-trivial abstractions, to some degree, are leaky") is not exactly the same as mine, the consequences of what we said are remarkably similar.
Now, there seems to be a contradiction between the idea that abstractions are leaky, and therefore you have to know what's below them anyway, and the idea that a true infrastructure must completely hide what's below. I could simply call H.L. Mencken to the rescue :-), but I'll try to dig a little deeper on the subject.
I believe an abstraction can leak in 3 fundamental ways (with some middle ground I'll cover later).
1) Functional leak
It's when you reasonably expect something to work, but it doesn't. Joel's example of a string class in C++, not allowing "foo" + "bar", is obviously about a functional leak.
2) Non-functional leak
It's when through the use of an abstraction, you change some non-functional property, like performance. My example of virtual memory (which I still consider a good abstraction/infrastructure) surely has non-functional leaks. When Joel talks about iterating over a matrix, or using the best SQL statement, that's again about non-functional leaks.
3) Debugging leak
It's when you can forget about the internals of an abstraction/infrastructure as long as it's working, but you have to know what's going on under the hood to understand faulty code. Joel's examples of ASP, VB, COM, etc. all fall here, and it's indeed a very common kind of leakage. Code generators are prime candidates for debugging leaks.
Now, in most cases, non-functional leaks are to be expected even in good abstractions/infrastructures. Ideally, they will be documented. Ideally, we would have a quantitative model for performance figures and so on (even a coarse one would do). In practice, they are often undocumented but still accepted in many modern systems.
Functional leaks are different. From the "Listen to your Tools and Materials" perspective, a functional leak is your material telling you "fix me". If you can't, maybe it's your tool telling you "fix me". For instance, there is no conceptual (or even practical) barrier that prevents C++ to be extended, by relaxing some constraints on operator overloading, so that
std::string operator +( const char*, const char* )
could be made legal (of course, that's not legal C++ today)
Debugging leaks are the main reason behind my "We can safely abstract away what we know, not what we don't know". They are by far the most common leak.
Where is the middle ground? Quality of service issues are difficult to classify as functional or non-functional, so you'll need some judgment call there.
Note that some of Joel's examples (TCP, the file system) do not really look like leaky abstractions to me. In both cases, the abstraction is actually promising you to do something OR give you back an error. Of course, you have to handle the error :-).
To recap:
- Even a good abstraction can be leaky. However, it's still useful to have those abstractions, because they will lower your cognitive load in many cases.
- Some leaks are worse than other. Functional leaks add to your cognitive load while coding. Non-functional leaks, depending on your application domain, can be quite harmless or may add to your cognitive load during design AND coding (I've more to say on this, but it will have to wait). Debugging leaks add to your cognitive load while debugging, and in a sense while testing too.
- Back to the idea of infrastructure, we want a real infrastructure to go largely unnoticed. Therefore, functional leaks are bad, and debugging leaks are bad too. Depending on the infrastructure, some non-functional leaks can be tolerated, or even expected.
Sunday, January 14, 2007
The tacit dimension of Infrastructure
In a previous post, I argued that real infrastructures should rapidly disappear from conscious processes. To a large extent, we should be able to write code without any concern about what is going on under the infrastructural layer. I also argued that we often call infrastructure something that lacks this highly desirable property. However, as a smart reader noticed, I didn't explain why information hiding is so important at the infrastructural level. Readers beware, reasons tend to lean quite a bit on the philosophical side :-).
Let's start with a definition of infrastructure. I won't use a dictionary or the wikipedia here; instead, I'll quote a simple but effective definition from Donald Norman (in "The Invisible Computer"): the term infrastructure refers to the basic services and foundations required for a system to function. Norman then refers to the everyday life infrastructure we all take for granted in modern times, like sewers, power lines, paved roads, and so on.
I should say we usually take them for granted. Nobody is usually concerned about the process of producing and distributing power. We just plug something in, and we expect it to work, hassle-free. The infrastructure largely disappears from our thought processes. As Mark Weiser said (in "The Computer for the 21st Century", Scientific American, September 1991 [draft here], The most profound technologies are those that disappear. They weave themselves into the fabric of everyday life until they are indistinguishable from it.
There are times, however, when we do notice. It's when the infrastructure does not work. A little diversion may clarify this further.
In an interview with John Bennett (Reflective Conversation with Materials [which largely inspired me to learn more about Schon's work and ultimately lead to my paper "Listen to Your Tools and Materials"], Donald Schon talks about Michael Polanyi and his concept of tacitness. He speaks of the way we use tools, and how they go largely unnoticed: I'm writing on this pad now with a pen. As I guide the pen along the paper, I am not paying attention to the pressure of my fingertips on the pen. [...] I'm paying attention to the content of what I am writing, rather than to the process of writing. [...] When my pen begins to run out of ink and I have to press it, then I become aware of the interaction between me and the pen.
The technology disappears, until it stops working.
What's wrong with computers? Here is Norman again, from the same book above: In many of our technologies, the infrastructure is in the way, it is always present, and it seems to always go wrong. I would say that it's because it's not an infrastructure, but a visible superstructure. This is true for the end user (consider a program that fails to install, can't be properly uninstalled, prevents the previous version from working or being reinstalled, etc.) and is true for programmers as well, whenever they use the wrong "infrastructure".
Consider a library like MFC. It does a very poor job of abstracting details of the underlying platform, so the programmer is usually concerned with a) the OO-design (sort of :-) of MFC, like "which methods should be overridden in a derived class", and b) what is going on under the surface, like "is what I'm trying to do really acceptable for the underlying Windows API". Indeed, MFC can't be really qualified as an infrastructure for GUI development. It is just a thin veneer over the API. It's mostly a superstructure.
Consider virtual memory. Most programmers are not really concerned about the details of paging, or how the CPU supports virtual memory, and how the operating system plays along with that. They just expect it to work. They notice in some cases (trashing), but even in those cases, the solution is usually found elsewhere, not by messing with the inner workings of virtual memory. There are exceptions, as usual, but virtual memory can be considered a real infrastructure.
It is important to understand that there is nothing wrong in creating a superstructure. It is just plain wrong to call it an infrastructure (Weinberg would call this a lack of congruence between what is said and what is done). We must expect an infrastructure to disappear, or we'll be overloaded by a myriad of details, most of which are out of our control anyway (that's a well-known recipe for high stress and low productivity). By constantly exposing the underlying details of a so-called infrastructure, we basically set up the same scenario of a faulty infrastructure: our users have to notice. That's very much like a self-fulfilling expectation, and not a particularly good one :-).
Let's start with a definition of infrastructure. I won't use a dictionary or the wikipedia here; instead, I'll quote a simple but effective definition from Donald Norman (in "The Invisible Computer"): the term infrastructure refers to the basic services and foundations required for a system to function. Norman then refers to the everyday life infrastructure we all take for granted in modern times, like sewers, power lines, paved roads, and so on.
I should say we usually take them for granted. Nobody is usually concerned about the process of producing and distributing power. We just plug something in, and we expect it to work, hassle-free. The infrastructure largely disappears from our thought processes. As Mark Weiser said (in "The Computer for the 21st Century", Scientific American, September 1991 [draft here], The most profound technologies are those that disappear. They weave themselves into the fabric of everyday life until they are indistinguishable from it.
There are times, however, when we do notice. It's when the infrastructure does not work. A little diversion may clarify this further.
In an interview with John Bennett (Reflective Conversation with Materials [which largely inspired me to learn more about Schon's work and ultimately lead to my paper "Listen to Your Tools and Materials"], Donald Schon talks about Michael Polanyi and his concept of tacitness. He speaks of the way we use tools, and how they go largely unnoticed: I'm writing on this pad now with a pen. As I guide the pen along the paper, I am not paying attention to the pressure of my fingertips on the pen. [...] I'm paying attention to the content of what I am writing, rather than to the process of writing. [...] When my pen begins to run out of ink and I have to press it, then I become aware of the interaction between me and the pen.
The technology disappears, until it stops working.
What's wrong with computers? Here is Norman again, from the same book above: In many of our technologies, the infrastructure is in the way, it is always present, and it seems to always go wrong. I would say that it's because it's not an infrastructure, but a visible superstructure. This is true for the end user (consider a program that fails to install, can't be properly uninstalled, prevents the previous version from working or being reinstalled, etc.) and is true for programmers as well, whenever they use the wrong "infrastructure".
Consider a library like MFC. It does a very poor job of abstracting details of the underlying platform, so the programmer is usually concerned with a) the OO-design (sort of :-) of MFC, like "which methods should be overridden in a derived class", and b) what is going on under the surface, like "is what I'm trying to do really acceptable for the underlying Windows API". Indeed, MFC can't be really qualified as an infrastructure for GUI development. It is just a thin veneer over the API. It's mostly a superstructure.
Consider virtual memory. Most programmers are not really concerned about the details of paging, or how the CPU supports virtual memory, and how the operating system plays along with that. They just expect it to work. They notice in some cases (trashing), but even in those cases, the solution is usually found elsewhere, not by messing with the inner workings of virtual memory. There are exceptions, as usual, but virtual memory can be considered a real infrastructure.
It is important to understand that there is nothing wrong in creating a superstructure. It is just plain wrong to call it an infrastructure (Weinberg would call this a lack of congruence between what is said and what is done). We must expect an infrastructure to disappear, or we'll be overloaded by a myriad of details, most of which are out of our control anyway (that's a well-known recipe for high stress and low productivity). By constantly exposing the underlying details of a so-called infrastructure, we basically set up the same scenario of a faulty infrastructure: our users have to notice. That's very much like a self-fulfilling expectation, and not a particularly good one :-).





