My buddy Adam Williams @aiwilliams tweeted this link., which is, at it's roots, about the dangers of doing more than should be done.
Feature creep is one temptation which leads to overdoing, and thus doing the wrong thing. Another is obsessively trying to remove all defects. As they say "Perfect is the enemy of good."
Which reminds me of an old chestnut from my earler days. I've provided some footnotes for younger readers
The Perfect Program
no program's that perfect" they said with a shrug. "the client is happy-- what's one little bug?" but he was determined the others went home he dug out the flow chart (1) deserted. alone. night passed into morning the room was quite littered with core dumps (2) and punch cards (3). "i'm closer." he tittered. chain smoking, cold coffee. logic, deduction. "i've got it!" he cried. just change one instruction. then change two, then three more as year followed year. and strangers would comment "is that guy still here?" he died at the console (4) of hunger and thirst next day he was buried face down, nine edge first. (5)
Footnotes
- A flow chart was a primitive form of program representation, comprising variously shaped boxes connected by lines and indicating the control-flow of a program. This was in the days when programmers wanted to be like engineers, and we all know that engineers draw stuff on big draft boards before they build anything. This idea lives on in ideas like UML. It's still a bad idea.
- Back in the days before laptops, behavior driven design and continuous testing, programmers would submit their programs to the big computer locked up in a room and attended by priests called operators. The priests would offer the program up to the computer, and return with a reply. If something went wrong, you got back a large stack of paper which showed a picture of what was inside the computer when things went south. Back then the inside of the computer had memory comprised of little magnetic donuts called cores woven into a matrix with wires, so memory was called "core" A core dump was that picture of what was in the computer. This was as close as we had to a debugger back then.
- Punch cards were strangely shaped index cards, the shape was something like a widescreen TV compared to a normal index card, three of the corners were rounded and the upper left corner was beveled.
Each card held 80 whole twelve bit words, or the equivalent of 120 bytes. Each bit was a hole bit since it was represented by the presence or absence of a hole in a particular row or column. Each column was a word. In most cases using all of the bits on a card was rare. Usually each column encoded a character, in some archaic encoding scheme like EBCDIC.
Programs were kept as large stacks of these cards, and these stacks would be fed to the big computer through a machine called a card reader. The holes would be punched into the cards using a kind of typewriter (a primitive kind of laptop/desktop comprising a keyboard and a printer) called a keypunch, which instead of printing what you typed on paper, punched it into cards.
Usually the computer would output to a printer, but some older printers output back to cards through another machine named, strangely enough a card punch. In these cases there was a separate machine detached from the computer which could read decks of cards and print their contents to a printer.
If you were really lucky, the computer would combine the card reader and punch with one input hopper. You would put your program deck into the hopper, followed by (carefully counted) blank cards for the output. One fun experience was to put your program into the hopper when a program was running with insufficient blank cards before you, in which case the machine would happily overpunch part or all of your program with the first guy's output.
For those who fell for static typing, there were specially printed cards for different programming languages, here's a FORTRAN card there were also COBOL cards, although the computer and card readers used duck typing, they really didn't care what was printed on the card, only the holes, the whole holes and nothing but the holes.
- The console was the machine through which the priests, and sometimes programmers, directly interacted with the big computer. It was a rare privilege for a mere programmer to have access to the console, it usually happened late at night on third shift when the priests wanted to sleep. What no, terminals/workstations/laptops you say! ha!
- A reference back to punch cards. Each row of bits on the card had a number. For some good reason, the top two rows had the numbers 12 and 11 and were called zone rows. The non-zone The remaining rows were numbered 0 through 9 with 9 at the bottom. A card deck could be put into a reader, punch, or reader/punch in four orientations. The deck could be inserted face (printed) side down, or face side up, and with the 12 or the 9 edge entering the machine first, hence "face down, nine-edge first." It was important to get this right, even more so when submitting a job than burying a dead programmer.
Gawd how I miss the old days!
NOT
Chris Wanstrath (a.k.a. defunkt) just wrote an article on the github blog about how he cut the time to deploy github itself from fifteen minutes to fourteen seconds.
The starting point was the observation that since the standard Capistrano deployment tasks treat the code repository as a black box plugin, they aren't optimized. They treat git repositories pretty much the same as they do subversion repos.
In a search for a better way, Chris takes on a tour of the various Capistrano alternatives, Vlad the Deployer, Heroku's Rush, and finally Fabric a deployment framework written in Python, before coming full circle back to Capistrano and refactoring the deploy recipes, then rewriting the tasks to setup, update and rollback the code on the server using more "gitty" techniques.
Another thing which slowed the old deploy down was having a separate cap task to make each symlink needed on the server. Each cap task has some overhead, which Chris eliminated by making a single task which made all of the symlinks
The last change was moving the task of minimizing JavaScript and CSS from the machine running cap, where it was repeated for each server, to the servers themselves.
This is a great article, with lots of food for thought on how to use Cap and Git.
A tweet from Ward Cunningham yesterday led me to watch a very interesting video of a lunch time demo of exploratory testing by Elisabeth Hendrickson , while visiting Pivotal Labs. Ms Hendrickson shows how she forms a model of how the software works in order to find issues more productively. During the session someone says that they always heard that testers should be like typical "dumb" users and treat the software as a black box, but Elisabeth says that getting information about the actual implementation from the developers is fine. She also talks about her process of forming hypotheses about how the system works, and doing experiments which confirm or refute those hypotheses.
Does this sound familiar? We're talking about the scientific method here. In an interesting coincidence, I also stumbled upon another web video this weekend, an interview in which Richard Feynman describes science with an analogy of learning the rules of chess, simply by observing games being played, with no explanation.
I found myself thinking about the roles of the exploratory test. It seems to me that one of these roles is to act as a mock for the end user.
And despite the common epigram, users are usually far from dumb. A typical user forms a mental model of how a system operates, and use that to inform their use of the system. The model starts from a base of expectations about how similar systems operate, and the user adapts the model as he/she encounters behavior which doesn't fit. Very much like Feynman's hypothetical chess student, who first observes that Bishops only move to squares of the same color, and Kings move one square, and then has to adapt the model when they encounter a pawn being promoted to a Bishop, or castling.
When the user's model and the implementation model don't align, interesting things can happen, often unpleasant. It's usually desirable to eliminate such misalignments, and the conversations between an exploratory tester and a developer about misapprehensions about how either the system or the user works are valuable in achieving this.
The scientific method is also useful for exploratory development, such as when writing code which interfaces with or implements a complex API or standard, but that's fodder for a future article.




