| Exploring Solution Spaces © Copyright 2003-2006, by C. Keith Ray | |||||||||||||||||||||||||
|
Archives
Subscribe |
2005.Dec.17 Sat
On Looking for the Next Microsoft
A good tidbit a long piece by Paul Graham. 2005.Dec.14 Wed A bit more on the Humane Interfaces and Minimal Interfaces debate that I blogged about previously. Some people think XP's "YAGNI"(You Aren't Going to Need It) rule that prevents unnecessary work means XPers are in favor of Minimal Interfaces, but that is only to start with. Refactoring away code duplication (and other code smells that the Simplicity Rules are against) tends to create Humane Interfaces. For example, if I was doing test-driven development in an environment that didn't already have a list class, and one was needed, I would test-drive a minimal implementation: maybe all it has to start with is list.size(), list.at(int index), list.add(object), and list.removeAt(int index). However, as the list gets used, I would look for duplicate code. If I refer to the first element using list.at(0) in more than one place, adding list.first() to the list class would remove duplication (the OnceAndOnlyOnce part of the Simplicity Rules). If I have code like this...
... I don't even need duplication to want to refactor that piece of code into a method of the list class. You'll note those three lines of code have the code smell "Many messages to the same object from the same method" (3 is "many" here), or "asking instead of telling" (we need some pithy standardized names for code smells). Refactoring that code into "list.last()" follows the Simplicity Rule "Expresses every idea that we need to express" (aka "Self Documenting Code", aka "Intention-Revealing Code"). Similarly if I search the list, enumerate the list, and so on, the rules of Simple Design would require that I move those features into the list class as well to clearly express intention and avoid duplication. So as the project progresses, the list class moves from being a very minimal interface to being a humane one, without ever having a method that isn't used in the project. Library(framework) design often starts in a vacuum, but wise people recommend using the library in three non-trivial applications before publishing it, precisely so that these kinds of refactorings can "tune" the library into something people will want to use, before publishing it. 2005.Dec.07 Wed
Minimal versus "Humane" Class API Design
Interesting stuff about class API design: good: HumaneInterface.html doesn't get that intention-revealing interfaces are good, "tell, don't ask" is good, and code duplication is bad: cafeaulait reaction to the above: cincomsmalltalk.com/blog/ Don't forget to read the comments. My comment:
more reactions: simplicity rules in the right place java net style vs smalltalk ruby style (though I'm not sure I agree with 437 methods for Smalltalk's Object Class!) 2005.Dec.04 Sun
Test Duration < Iteration Length
Extreme Programming is built on the assumption that completely testing the product under development can be done within a single iteration. If manual testing is the only method of testing, the test-duration will get longer every iteration until it can't be done within a single iteration. This is why automated testing is so important on an XP project. In well-run XP projects, automated testing covering every feature can be executed, at worse, overnight or in one or two days, and at best, as quickly as 20 minutes or faster. And that's two layers of automated tests: programmer/unit tests, and customer/acceptance tests. The reason XP has iterations and automated testing is to increase the speed of feedback on the design. On a large project, a design that meets all the requirements is going to require a long time to be developed. No person or team can come up with an "instant" design for all the requirements. In a phasist approach, a team might spend a long time coming up a design that they think will meet all the requirements, and not have any real feedback on the goodness of that design until they start implementing it and testing it. If it turns out the design is bad, the project may not have enough time to fix the design before shipping deadline. Contrary to some popular misconceptions, XP team do design, but they do it incrementally, creating tests for that design in each iteration. The implementation for that design isn't considered complete until it passes all the tests that the programmers (doing test-driven development) and the testers (creating customer/acceptance tests) think up. Most importantly, refactoring is design, as the design that was appropriate for the last iteration is changed to be appropriate for the current iteration. Fortunately for people doing incremental design work with refactoring, most automated tests do not have to change when the code is changed. The tests are acting as executable specifications for the requirements, so if the requirements haven't changed, then the tests should not have to change. Of course, when the tests are very close to the implementation, they do sometimes need to be change, but the majority of tests should not change when someone need to alter the design to handle requirements being address in the current iteration. If someone is making changes to the code, and the tests fail, or the code isn't even executable or compilable any more, they are not doing refactoring. They are doing something else, like rewriting, Stop them. Teach them how to do refactorings that are in Martin Fowler's book: each refactoring is a small behavior-preserving change that keeps the code and tests working. Run the programmer tests after each refactoring for assurance that no behaviors have been changed unintentionally. Many people are using refactoring tools that can do refactorings that are almost always error-free, though running the tests is still recommended, just in case. Manual refactoring is more prone to error, so be careful and work slowly in order to avoid mistakes (and therefore go faster). Make sure the whole team knows the direction the refactorings are headed in, so you don't get into cycles where one developer undoes the refactorings of another developer and vice-versa. Teams that haven't gotten their specification and testing processes firmly in place will experience what Michael Feathers calls iteration slop and "trailer-hitched QA":
What happens when there are conflicting requirements? In a large project, it's often hard to detect conflicting requirements because you just can't hold them all in your head at the same time. You could spend some time comparing every requirement to every other requirement, but that's tedious and you're still likely to miss the conflicting requirements -- particularly when some requirements are (as yet) unwritten and/or "derived" requirements. On projects without fast automated tests, conflicting requirements show up as a cycle of bug reports, often with long delays between bug reports. Fixing one bug causes another bug to show up, perhaps weeks later when testing gets to that conflicting requirement. Fixing that other bug causes the original bug to re-occur -- but it may be flagged as a new bug if no one reviews the old "fixed" bugs before filing a new bug report. When every high-level requirement is written as an automated customer/acceptance test, and every low-level requirement is written as a programmer/unit test something interesting happens: getting one conflicting requirement/test to pass will cause another test to fail. Getting that other one to pass will cause the original one to fail. If you run all the tests often, and they run quickly, it becomes pretty obvious that a cycle is going on. Many XP teams create new tests whenever they are going to fix a bug. The test fails because of the bug, and will pass when the bug is fixed. In practice (particularly with legacy code), this can be very difficult because to write a failing test, you first have to find the buggy code. However, having that test in place will help detect conflicting requirements if you later fix a bug and cause the previous bug-detecting-test to fail again. What do you do with legacy code? You can segregate it: do test-driven development on new code, avoid changing the old code, move old features into the new tested-code gradually until the legacy code disappears. This assumes the legacy code is mostly bug-free. If you have to fix bugs in legacy code often, your best strategy is to put the old code under test, starting with the parts you are about to modify for bug-fixes. See Working Effectively With Legacy Code by Michael Feathers for techniques on how to introduce testability into legacy code. You will also have to spend more time on manual testing, and probably have to have longer iterations in which to do it. 2005.Dec.03 Sat"Some of my smartest graduate students get freaked out when they discover what our lives are really like. [...] They think science is about asking questions and devising experiments that answer them. They think there is a map to knowledge they can follow by just working hard. But the truth is that we often don't even know what the right questions are. There is no map. Some of my students get so disoriented by this that they have to quit and do something else." -- Jerry Leob, quoted in Rebuilt: how becoming part computer made me more human by Michael Chorost. 2005.Dec.01 ThuThe title of this post is taken from a spam - I thought it sounded like it should be a real word: medication + boon. Check out Kevin Lawrence's tale of a class that "just" was going to represent a class-name, and became so much more. He titled his post "Fight Complexity with Complexity" but by creating an abstraction that centralizes stuff that was spread out everywhere, he actually simplified the code -- according to two of Kent Beck's rules of simplicity: "no duplicate logic" and "expresses the programmer's intentions".
Utility Functions as a Code Smell
Bunches of utility functions, whether bunched together in a "utility class" or not, are often a sign that code isn't object-oriented enough. For example, in Apple's Cocoa implementation, there are a bunch of utility methods for filename-manipulation, in class NSString:
These path-component methods are actually added to the class NSString via a class category, but are viewed as members of the NSString in the documentation and usage. My point here, with this example, is to raise the question: what are these "path" and "path component" methods doing in class NSString? Perhaps they should be members of a PathName or PathComponent class. (And by the way, pathnames in MacOS X not just unicode, they are "decomposed" unicode, so don't forget to call decomposedStringWithCanonicalMapping; a PathName class would take care that of that for you.) Like pathnames, URLs are strings with rules for how they are formatted, encoded, etc., so you might expect that Cocoa deals with them using a bunch of utility methods in NSString. But, being a later addition to Cocoa, they are actually their own class: NSURL. This class encodes the requirements of RFCs 1808, 1738, and 2732. It provides methods to access host, get the base URL, test if the URL refers to a file, get absolute and relative forms of an URL, and so on. Apple is moving to using NSURLs instead of pathnames in various Cocoa APIs, and beginning to deprecate the pathname-based APIs, since NSURLs can refer to local files as well as remote web-based resources. If you've got a bunch of utility methods, look at their names and behaviors to see if they really belong to one or more new classes. If they conceptually belong to an existing class that you can't edit, Objective-C gives you the option to extend that class via a class category. In other languages, you might have to make a subclass, or in the worst case, leave them as utility methods, but put them together in a class that reflects where you would rather put them. For example, if you write a bunch of string utilities in Java, you can't extend the java.lang.String class, so put them as static methods into your own com.yourcompany.StringExtras class. But first, consider if they are actually representing a concept that deserves to be its own class, like Apple did with URLs. Kent Beck Podcast/Interview here 21 minutes. 2005.Nov.28 MonThis is an partial example of how a story-card might be 'translated' into FIT tests -- this example is paraphrased from the middle chapters of "FIT for developing software" by Mugridge & Cunningham. Here's a story card: +--------------------------------------------------------+ | CreateRentalTemplate | | | | A rental template defines a list of rental items. The | | template specifies the proportion of each item per | | person. The user can select a template and specify the| | number of people to rent those items. | | | +--------------------------------------------------------+ To accompany the story, there is discussion (verbal, spoken) between the programmers, testers, and the person that understands the requirements: the "Customer" in XP terminology, "Product Owner", "Domain Expert", etc. I'll leave out estimation and scheduling of stories, which are taken care of by Release Planning and Iteration Planning. In the case of this story, there is discussion of how rounding-up will occur when the number of people specified doesn't exactly match the proportions in the Rental Template, so the test is written to use numbers that test the rounding-up. Using FIT or Fitnesse, one acceptance test for CreateRentalTemplate would look something more or less like this: -------------------------------------------------- | Create rental template | Coffee break | -------------------------------------------------- | one | coffee dispenser | for | 20 | people | -------------------------------------------------- | one | coffee table | for | 40 | people | -------------------------------------------------- | one | cup | for | 0.9 | people | -------------------------------------------------- -------------------------------------------------------- | begin transaction for client | Joanna | -------------------------------------------------------- | fill template | Coffee break | for | 21 | people | -------------------------------------------------------- | pay cash | 65.00 | -------------------------------------------------------- | end transaction | -------------------------------------------------------- | rentals of client | Joanna | ------------------------------- | rental item | count | ------------------------------- | coffee dispenser | 2 | ------------------------------- | coffee table | 1 | ------------------------------- | cup | 24 | ------------------------------- The code to make this work would involve implementation various kinds of fixture classes, that will be instantiated by the FIT framework, and which in turn call the model objects to exercise the application. Initially the FIT tests will fail because the model objects do not exist or do not yet have required functionality. Use test-driven-development to create the model objects with the required behaviors. Then the FIT tests should pass. See http://fit.c2.com/ for info about FIT, see http://fitnesse.org/ for info about Fitnesse. 2005.Nov.12 SatI'm back from the AYE conference. Very informative. Very interesting (people and sessions). Very intense. Very tiring. Veni Vidi Very. I came back with new books, lots of hand-outs, notes, and a head-cold. No t-shirt. I will be blogging about the sessions and the books as I recover. 2005.Nov.11 Fri
Gates vs Jobs in Slide Presentations
Comparison of Steve Jobs's and Bill Gates's slide presentations compared here:
With pictures. Check it out. 2005.Nov.06 SunQuotes from Lasse Koskela: 2005.Nov.04 Fri In pursuit of better health, I have spent literally hours today waiting for a doctor, a chiropractor, a lab tech, and a pharmacist to do what they do, and even more hours driving from one place to another. The doctor was a last-minute appointment, and the lab work required a waiting period to measure certain biological processes correctly. Also if the prescription had been faxed to the pharmacy like usual, there would have been less of a wait there. There was also some time spent looking for an certain doctor-recommended over-the-counter medicine at a combined grocery store/drug store, which didn't carry it, and at a "real" drug-store, which did carry it. Each place I visited may have been very efficient from their own perspective, but all these wait, search, and transit times are signs of inefficiency from my perspective. (See "Theory of Constraints" for more on that.) This situation is not likely to change as long each of these functions is an independently-owned enterprise, and our laws and traditions will work to preserve this independence at a cost of (my) efficiency. If I valued choice less and efficiency more, I suppose a good HMO (or equivalent) with centralized infrastructure would make for a better experience. (Or maybe not.) 2005.Oct.27 Thu
The Return of Freedom of the Press
Today's quote on my google home page is "Freedom of the press is limited to those who own one." - AJ Liebling. In the days of the USA's founding fathers, anyone who wanted to get their views across to other people would hire a press, or rent or buy one, and print their own pamphlets and broadsides to distribute or sell. It was fairly democratic for that time. In the days of television, only a very few could afford to buy television time. Broadcasters could refuse to show material they disagreed with. The common people had to resort to creating newsworthy "demonstrations", marches, speeches, etc., and hope that a sound-byte would be get broadcast by a news organization. Writing to newspapers and magazines also depends on the whim of editors as to whether your views get published. Now just about anyone can buy or rent a web-page, or even get a free page for a blog, to get across their views. Popular or controversial content shows up on search-engines if enough people agree or disagree with it and link to it. We aren't dependent on the whims of TV or newspapers editors anymore. Power to the people! Yay!
You know, we ought to have a national holiday to celebrate our freedoms of speech and press. 2005.Oct.26 WedQuotes from Charles Petzold's Does Visual Studio Rot the Mind?: (bold-facing is my emphasis) 2005.Oct.24 Mon The most common attitude about test-driven development and user-interface code is that the two don't combine well. I have only attempted a small amount of test-driven UI code - on MacOS Carbon and Java Swing - and didn't find it terribly helpful. I may try again with Cocoa and see how that works out. The danger of having GUI code as a coding-area designated "not to be test-driven" is the temptation to write the GUI code first, and to mix in business logic code with the GUI code. Avoid that temptation. Your business logic could have multiple user-interfaces: a Mac UI, a Windows UI, a Unix/Linux UI, a command-line interface, a scripting UI via COM on Windows or AppleScript on MacOS X, or an embedded scripting language, and even a web-interface. Using FIT/Fitnesse means that you also have an acceptance-testing interface. Keep the UI from mixing with the business logic by test-driving the business logic first. This makes writing portable, multi-platform code much easier since UI code is very often single-platform. Even if you don't plan on writing portable code, TDD-ing the business logic before coding the UI still provides a separation of concerns that is a hallmark of good design, and allows better re-use of the business logic within the same project or different projects. Therefore, to avoid mixing business business logic with the GUI code and thus creating code that's unnecessarily coupled and non-cohesive, test-drive the business logic code first, and write the GUI code last. Sometimes a spike might be necessary to find out what the GUI framework requires. Don't forget to throw away the spike code after you discover what you need to learn. 2005.Oct.17 MonBDUF (big design up front) is not the "opposite" of incremental design, it's part of the continuum of how much design precedes coding/refactoring. In a blog entry where Joel wrote a specification up-front contrasting it with his straw-man view of XP's incremental development, he called what he did BDUF, but it was neither Design (it was specification), nor was it big; he was not doing BDUF, and he was doing something many agile methods promote: creating a specification of the requirements before development. In XP the details of the specification would be delayed until later, but the overall requirements are needed to estimate the Release Plan. Waste does not mean failure. Advocates of incremental design say that too much design up-front is wasteful, but that doesn't mean up-front design can't be part of a successful project.
BDUF is not the same as "too much" design up-front, so stop saying BDUF when you mean TMDUF (too much design up-front). 2005.Oct.10 MonRobert C. Martin writes on the benefits of TDD-style unit tests (bold-facing is my emphasis):
Robert C. Martin compares test-driven development to double-entry bookkeeping:
Robert C. Martin reminds us: 2005.Oct.06 Thu The few people who were doing Waterfall development successfully were offended when proponents of Agile methods attacked the usual attempts at a process also called Waterfall as being unworkable. Both sides were using the same name, but for the most part were talking about different things. Now Ken Pugh's book Prefactoring is out, and he does the same thing: attacking something that many inexpert teams do, but which experts don't (usually) do: stopping progress in a project in order to do a large redesign. He calls that refactoring, a term commonly mis-used for that very thing, but which doesn't fit the definition of refactoring as understood and practiced by the experts. Recall that the first XP project with Kent Beck and Ron Jeffries did the same thing: they spent a week or two with acceptance tests failing as they stopped progress to change a core part of their design. It seems like every team doing XP has to learn the hard way that refactoring has to be done as a continuous process, otherwise code-debt piles up until a big overdue change has to be done. Looking at the sample chapter available from O'Reilly, I see Ken advocating the same care with domain-driven design that many agile experts have advocated. Compare this with, for example, what Kevin Rutherford writes: "One of my current bug-bears is the lack of design done by agile teams. I know we all chant 'design is good, so we design all the time', but in reality it just doesn't happen. Switching from legacy habits into test-first thinking and good object design is a really hard jump." And John Roth: "...the fundamental language types, and the basic language libraries, do not, and let me repeat that, do not represent any concepts that your application actually needs. At best they represent bizzare oversimplifications of those concepts that can't be told apart by the type checking mechanism.". Ken Pugh, in this book, wrote: "...avoid using primitive data types. Pretend that ints or doubles do not exist.... Items are priced in Dollars.... The time for a single song... could be stored in a TimePeriod.... A U.S. Zip Code is not just a CommonString either. You can describe it as a NumericString.... as a FormattedString.. or as a ZipCode data type. If any combination of digits was valid, using NumericString or FormattedString might be appropriate. However, declaring the attribute as a ZipCode type allows us to abstract away its actual representation.... Do not combine classes simply for implementation purposes. You can define both SocialSecurityNumbers and PhoneNumbers as strings of digits with two dashes. That does not make them equivalent.... You would never send a Social Security number to be dialed, nor would you attempt to record payroll taxes against a phone number." I had the opportunity to review a draft of Ken's book early this year, but lost track of it with my new job and all sorts of things. I will not judge his book by the cover, but by its content, which seems to have some uncommon "common sense". 2005.Oct.04 TueAutomated acceptance tests are specifications in an agile method. Check out Dave Churchville's blog entry on this subject. Quote:
Since TDD - Test-Driven Development - is a design-implement technique that happens to produce suites of automated tests, people often think it all about testing. BDD - Behavior-Driven-Design - attempts to correct this by using different terminology. See Elizabeth Keogh's blog entry on this. Quotes: 2005.Oct.02 Sun
Fighting Urban Legends about Garbage Collection
Urban performance legends, revisited, by Brian Goetz. Quotes: 2005.Sep.30 Fri Object-Oriented programming, and design patterns in particular, help us get away from the morass of spaghetti-code from olden days of yore (before OO and design patterns). Imagine a ball of tangled cooked spaghetti that's cooled and congealed and maybe has been sleeping in the freezer for a long while, and you want to remove one stand without breaking any other strands or even moving them too much. You can't: that's tightly-coupled. Here are a few things that couple code: global variables, global functions, concrete classes that refer directly to each other, code duplication, data duplication, C/C++ include files. And "manifest types". That last one is where your programming language requires you to declare type for each member variable, parameter, function result, local variable, etc. And then the compiler checks them. Sometimes you know more than the compiler does and therefore you have to use type-casts to tell the compiler the correct type. The worst of manifest typing and "include files" often goes together. You change the return type of a method from "unsigned short" to "short" (or vice versa), perhaps by changing a typedef for that return-type in a header file. Now every source file that includes that header file has to be recompiled, even if they didn't use that type or that method. Also, every method that overrides or calls that method has to be recompiled - and the rest of each class's code gets to be recompiled as well, even though other methods in the class might not be affected. Every class that has a member variable of that typedef has to be recompiled. Every other method that has parameter or local variable of that typedef has to be recompiled. In this case, the size of the type hasn't changed, the operations that legal for that type haven't changed, and though the range of values used for that variable have changed, almost all the code involving that type will not have to change, but massive recompiled has been forced on you. The same kinds of cascades of recompiling can occur when changing a method's argument type to 'const', or changing from one container type like 'list' to 'vector', or many other things that you would hope would have only minor effects. On large projects, those recompiles can cost hours each day. David Buck talks about this on this blog. He references Local Change / Local Effect Principle and Domino Effect. 2005.Sep.28 WedI once needed to make three "filters" works. They each took an image file and several parameters as input, and produced an output PDF file. For most manual tests for various parameter settings, they did not produce correct output. So I refactored the big filter function to do the filtering in two steps (two subroutine calls). The first subroutine works on the parameters to compute the values needed to be passed into the image processing library, and the second subroutine calls the image processing library with the input image, those computed values, and produces the output image. Then I wrote a parameterized unit test that took all the parameters as input to the first subroutine, also took into expected output values, and asserted that the actual output values were equal to the expected output values. Then I wrote 900 one-line unit tests to this parameterized unit test with the various permutations of the input parameters and expected output values. (I actually worked in a spreadsheet for a while and copied-pasted from the spreadsheet to the unit test source code.) The test would also generate the output PDF file, which I could visually examine About 80% of the tests failed. Then I started working on the first subroutine to fix its problems. I got to where only 60% of the tests failed, then only 50%, 30%, 25%, 10%, the only 2 failures, and then 100% passing. Sometimes a change causes more tests to fail than before, and I could examine my latest modification and fix it, or undo it to try something else. That was the first of the three filters. The other two were similar, but with fewer parameters and options, each of those only needed about 600 one-line unit tests. Note that I didn't test EVERY combinations of options and settings. That would have required several billion test cases. By making an intelligent selection of the "input space" I could exercise the filter algorithm along all of its dimensions with a minimum of effort. Fixing all three filters with the aid of automated tests only took a few days each. Trying to make the algorithm work with manual testing would have taken months or years. Ruby, calling a method with a block argument:
anObject.methodTakingBlock { someObj.doSomethingHere }
or
anObject.methodTakingBlock do
someObj.doSomethingHere
end
Smalltalk, calling a method with a block argument:
anObject methodTakingBlock: [ someObj doSomethingHere ].
Ruby, implementing a method that takes a block argument (the lack of a named block variable and the "yield" keyword are a bit confusing, particularly if you associate "yield" with threads, which are not in play here.)
def methodTakingBlock
yield
end
Smalltalk, implementing a method that takes a block argument.
methodTakingBlock: aBlock
aBlock value.
Ruby, calling a method with a block argument that takes two argument itself:
anObject.methodTakingBlock {
|aVar, bVar| someObj.doSomethingHere(aVar, bVar) }
or
anObject.methodTakingBlock do |aVar, bVar|
someObj.doSomethingHere(aVar, bVar)
end
Smalltalk, calling a method with a block argument that takes two arguments itself:
anObject methodTakingBlock: [ :aVar :bVar |
someObj doSomethingHere: aVar andHere: bVar ].
Ruby, implementing a method that takes a block argument, giving the block the values 12 and 13 for the block's own arguments.
def methodTakingBlock
yield 12, 13
end
An Ruby alternative syntax where the block argument is converted to a Proc object and is accessible via a named parameter:
def methodTakingBlock(&aProc)
aProc.call(12,13)
end
Smalltalk, implementing a method that takes a block argument, giving the block values 12 and 13 for its own arguments.
methodTakingBlock: aBlock
aBlock value: 12 value: 13.
2005.Sep.24 Sat
Dear Wil Wheaton (an Open Letter)
I've enjoyed your books, your blog, and your performances on Star Trek Next Generation. In Dancing Barefoot I particularly enjoyed the story of "William F-ing Shatner" and everything that led up to your epiphany at the Las Vegas Star Trek Experience. In the course of that writing, you mention that Deep Space Nine is not one of your favorite Star Trek series [please don't stop reading yet]. I suggest this may be, at least in part, because you are viewing that show as a Star Trek fan. If you view it as a writer and actor, you may see more value to that series. The meat and potatoes for writers and actors is conflict, drama, and comedy. The setting and cast of DS9 is just full of opportunities for conflict, drama, and comedy. The original Star Trek and Star Trek: The Next Generation were more about exploration and sense of wonder (and there's nothing wrong about that). All of the Star Trek shows (except for the Original Series) took at least two seasons to find their "tone", so please don't judge DS9 from its earliest episodes, which may have started even rockier than the others. DS9 has conflicts in its initial setting: The Bajorans, who were until recently oppressed by Cardassians, have to rebuild the world and rebuild their world-view. They still must interact with Cardassians, and each race hates each other, but in many episodes they have to work with each other, and sometimes even come to love each other. Neither race particularly likes the Federation. Many of the Bajorans now involved with governing the planet and the space station were, quite frankly, terrorists during the Cardassian occupation. I sometimes wonder how this show would play in the Middle East if translated into Arabic, Farsi, etc. Would the viewers think of the Cardassians as representing the United States and/or Israel? I imagine they would identify with the Bajorans to some degree. What would the Federation, with its acceptance of tolerance and diversity, represent to them? Watching the show with those ideas in mind provides a twist quite lacking in most episodes of other Star Trek series. Ben Sisco, the Federation man in charge of the space station, considers himself a man of science, and yet finds himself thrust into the role of the Bajoran "Emissary of the Prophets", a religious figure of much importance, in the first episode. During the seven years of the series, he grows into that role. In the early seasons, he calls the mysterious occupants of the wormhole, the "wormhole aliens," and in later seasons more and more often refers to them using the Bajoran term "the Prophets". His dual 'roles' provide conflicts on both professional and personal levels. Like Wesley Crusher on ST:TNG, there is a child growing up on the show: Jake Sisco. Unlike Wesley, he's something of an under-acheiver. Jake has a particularly moving episode in Season 4 where he loses his father to a "wormhole inversion"; he spends his life trying to retrieve his father from this anomaly, and as an old man, realizes that to save his father, he has to kill himself the next time his father temporarily re-appears. And he does! This resets time back to the original incident and Ben Sisco is able to avoid getting trapped in the time-vortex the "second time" around. Nog, other 'child' on the show (which under the makeup I think was played by an adult), starts outs as a truant who often gets Jake into trouble, and ends up becoming an over-enthusiastic Star Fleet cadet and ensign. He has a episode that is also a good character study in Season 7 where he has to come to terms with losing his leg in the war. You don't really see this kind of character growth in individual episodes; you have to view each season as if you're reading a novel. The best dramatic Season 6 episode shows us what life as black science fiction writer of the 1950's might have been like. Avery Brooks portrays a man with a dream, nearly broken by racism, so well that I'm getting misty-eyed just remembering it. Far Beyond The Stars should have won mainstream awards for best TV series dramatic episode, but of course it wasn't considered because it was "just" a science fiction show. :-( There are too many good dramatic episodes to list (poor Chief O'Brien, the "everyman" character: he gets himself wrung through the wringer many times ), but I also love the comedic episodes. The Ferengi, who are mostly used for comedic episodes, poke fun at our capitalistic society. The season 6 episode, The Magnificent Feregni , is one of the funniest because they try to act all macho, which is out of character for their kind (though Quark, Rom and Nog demonstrate honor and quiet bravery in many dramatic episodes). The House of Quark in Season 3 , where Quark temporarily marries a Klingon and saves her house through good accounting and Klingon-esque bravery, is also particularly good. Perhaps the best comedic episode of all builds on the Original Star Trek's The Trouble With Tribbles : Trials and Tribble-ations . For a Star Trek fan, this episode is a must-see because of how detailed their recreations of the original ship model, sets, costumes, and props are. Watch the DVD's "making of" extras to see how much love went into the making of that episode. DS9 probably brought back more actors reprising the characters from the original series than any other. (But they were all Klingons!) "Darvin" from Tribbles, "Kor", "Kang", and "Koloth" in other episodes. So there something there for a Star Trek fan, as well as for someone who appreciates comedy and drama. Check it out. :-) Sincerely, C. Keith Ray 2005.Sep.19 Mon
Using Cocoa's Reflection APIs for Reading/Writing an SQL Database
CoreData is probably doing something like this already. If you don't like CoreData's use of sqlite3/XML or being limited to the Tiger version of MacOS X, you can probably use these APIs to implement your own object-to-database mapping. If you want to work with a database schema that doesn't map simply to your classes, look elsewhere. Also: I'm just getting to know SQL-based databases, so I may be mis-using SQL/DB terminology or concepts. Writing to the database: 1. Call the method "className" to get the object's class-name and map that to a database table. You probably also want to map object addresses to a unique id in that database table. 2. Calls these methods to find keys of the object and map those to column-names (and possibly foreign keys to other tables): "attributeKeys", "toOneRelationshipKeys", "toManyRelationshipKeys" 3. Get values for keys by calling "valueForKey:" or "dictionaryWithValuesForKeys:". There's also "mutableArrayValueForKey:" for dealing with to-many relationships. 4. Assemble SQL from the classname/tablename, key-names/column-names, and values gotten above, and use that to write to the database. You'll probably need to write multiple rows to multiple tables to persist a complex aggregate object, thus you need to use techniques common to object-serialization to detect cycles and avoid writing an object to the db more than once. One of the PLoP books had a good discussion of serialization. Reading from the database is pretty much the reverse of the above, but getting to the class from a classname/tablename requires calling NSBundle's "classNamed:" method or some other method or function I haven't found in my quick search using AppKiDo while writing this. Once you have the values and key-names, set them with the method "setValue:forKey:" or "setValuesForKeysWithDictionary:". Cocoa has bunches of interesting methods like "classForArchiver" and "replacementObjectForArchiver:" that are probably used behind the scenes for implementations of Distributed Objects, reading/writing NIB files (in various NIB file formats, which recently included XML, I believe), and more recently, Core Data. Maybe someday I'll get more familiar with these. Oh, I haven't actually done this (yet), so don't ask me for help if you get stuck. :-) 2005.Sep.09 FriReally cool, funny book teaching Ruby here. Funner than "Mr. Bunny's Guide to Java" and actually educational. Cutter IT Journal apparently published a study "What Metrics Say About XP" by Michael Mah where five XP projects were compared to five waterfall-style projects inside a medical devices company. The XP projects completed 25-30% sooner and had one fourth the defects of the traditional-style projects. John Roth provides an idea to force programmers to create abstractions (hopefully appropriate for their application's domain). Quote: 2005.Sep.08 Thu
The Simplest and Yet a Very Effective Feature Request tracking tool
One that works well for small, co-located teams is the index card for small features/stories. Particularly nice if you are doing iterative development and you can put the index cards on the wall, partitioning the wall in designated areas: "this iteration's stories" (aka "front burner"), "next iterations stories" ("back burner"), other future stories ("refrigerator"), and not-to-be-done-this-release ("the freezer"). A project might have 100 to 200 stories (or more), but only a dozen or two are on the wall, in the 'active' state, at any time. (Kitchen metaphor names courtesy of Net Objective's Alan Shalloway, see http://www.netobjectives.com/events/specific/pr_nca_2005_08_xbrfasd.htm Slides in pdf form here) Bugs can be considered small features/stories as well, and tracked using index cards and the wall as well. Some teams use red index cards for bugs. If you have so many bugs that you need a database... you have too many bugs. It's really nice to take a bug-report-on-paper, talk to the QA tester about the bug, take it to my computer, fix the bug (writing notes on that paper if I need to), and then take the paper to the QA tester and talk to him about the bug and its fix, leaving it for him to verify. It can then go back on the wall with a mark on it indicating that bug-fix is completed, or go back to me for additional work. For remote employees, I've used a wiki to list stories, track who is working on them and when they're completed. 2005.Aug.27 SatBig Design Up-Front (BDUF) supporters claim that XP doesn't do enough design. See Agile Modeling for the opposing view. That book is naming all the kinds of designing (other than test-driven-design) that XP projects do. We need a new acronym for XP's design practices: not SDUF (Small Design Up-Front), even though that's kinda true, but LoDAtT: Lots of Design All the Time. 2005.Aug.17 WedI've been re-reading the book Are Your Lights On? How to figure out what the problem REALLY is by Donald C. Gause and Gerald M. Weinberg. This is the book that should have become popular instead of "Who Moved My Cheese?". It doesn't talk down to the reader, and it's fairly light-hearted. (Of course, it's not the same subject as the Cheese book, but that's besides the point.) [Problem: a bad book like Cheese was popular, but a good book like this isn't (in comparison). We are the Solution: buy it, and blog about it. Tell all your friends. Get your CEO or CIO or the HR department to buy it in bulk.] Sometimes the problem not considered when coming up with solutions is "whose problem is it?". This book considers that question several times in various real-world situations... such as landlords versus tenants, university president versus professors versus students, competing businesses and government regulators, bureaucrats and travelers. In the area of agile programming practices, and their acceptance or lack of acceptance within an organization, consider who "owns" a problem before trying to solve it. If you are considering automated acceptance tests, what problems are they solving? (And, what problems are they creating?) Are people who feel the pain the most solving their own problem? You can also consider this book as an informal prequel to the more serious book Exploring Requirements: Quality Before Design by Gause and Weinberg. Buy both. 2005.Aug.13 SatEssays by Scott Berkun, author of a project management book, The Art of Project Management, about which Kent Beck said "Reading this was like reading the blueprint for how the best projects are managed at Microsoft. I wish we always put these lessons into action!" "How to learn from your mistakes" quotes: You can only learn from a mistake after you admit you’ve made it. ... The larger your ambitions, the more dependent you will be on your ability to overcome and learn from your mistakes. ... success in learning from mistakes often requires involvement from other people, either for advice, training or simply to keep you honest. ... when you can laugh at your own mistakes you know you’ve accepted it and no longer judge yourself on the basis of one single event. Reaching this kind of perspective is very important in avoiding future mistakes. Humor loosens up your psychology and prevents you from obsessing about the past. It’s easy to make new mistakes by spending too much energy protecting against the previous ones. Why smart people defend bad ideas" quotes: ... one thing I did learn after years of studying advanced logic theory is that proficiency in argument can easily be used to overpower others, even when you are dead wrong. ... The problem with smart people is that they like to be right and sometimes will defend ideas to the death rather than admit they’re wrong. ... if they got away with it when they were young ... they’ve probably built an ego around being right ... Until they come face to face with someone who is tenacious enough to dissect their logic, and resilient enough to endure the thinly veiled intellectual abuse they dish out during debate ... they’re never forced to question their ability to defend bad ideas. Check them out. 2005.Aug.04 ThuSeth Godin blogs: 2005.Aug.02 Tue
Good Stuff on "Blaming Organizations" here:
Beyond Blaming: Congruence in Large Systems Development Projects by Jean McLendon and Gerald M. Weinberg (See also www.satir.org and www.geraldmweinberg.com) It is a fairly long article, here's a few small snips:
What would it take for a new language to impress me?
1. painless interoperability with other languages: C in particular, Objective-C, C++, Python, Java, Ruby, SQL, etc. Objects in this new language should be able to use and/or be proxies for objects in other languages. The language should be usable for shared libraries, plugins, device-drivers, and so on, as well as applications and scripting. 2. support for threads and inter-thread / inter-process communication. Java had "synchronized" but that wasn't enough (and maybe not even the right thing to have). Cocoa's Distributed Objects turn out to be fairly easily used for both inter-thread / inter-process communication, and Cocoa also has support for other ways of synchronous/asynchronous communication, but much of Apple's Cocoa isn't thread-safe. Make it easy to use operating-system threads. Also, make it easy for a programmer to set up a million thread-like objects that don't harm each other and which don't thrash a CPU to death. 3. garbage collection. Look to modern Smalltalk environments for how to do it right. Be able to turn it off for specific objects, etc. 4. work with programmer's weaknesses: many programmers forget to initialize local and member variables... make it easy to init them to a good value, and default to initializing them a zero, null, or default-constructor. Also put in warnings for some other common problems. 5. avoid other language/system weaknesses: learn from common problems areas in Java for example: classpath and class-loaders. 6. consider Objective-C's "message-eating null" -- many people who've never used it expect it to cause problems, but it actually makes programming easier and simpler. Only very rarely do you need to check if a variable is null... and you never get "Null Pointer Exceptions" halting your program. 7. and while you're at it, I'd like to use a debugger that understands objects: let me set breakpoints on ONE specific object: any methods (or any methods that I select) called on that object break into the debugger. Let me add logging of all state-changes to an object, or class of objects, while I'm in the debugger, without writing any code. If I set a break point on a method, let me specify which classes that applies to, if the method is inherited by more than one class. Let me browse all instances of a class (or all classes), and optionally find out where each one was created. Note that I don't talk about syntax, or dynamic-type-checking versus static-type-checking (versus type-inferencing). A correct program is correct in spite of the kind of type-checking that a compiler might enforce. I would want help from the compiler, but not hindrances. I've seen a lot of varieties of syntax. I would want something readable (and I don't consider Lisp and Perl - as usually written - to be readable). Pascal might be considered verbose these days, but it was designed for readability and simplicity (a parser for Pascal would be pretty simple, very simple compared to a C++ parser). Smalltalk is very simple, and pretty readable. Objective-C is "unusual" for those used to C++, but it is powerful, and pretty painless in integrating with other languages. I wonder what a combination of Pascal and Objective-C would look like? 2005.Jul.27 WedFrom a paper by Alistair Cockburn and Laurie Williams: Economics. A recent controlled experiment found only a small development cost for adding the second person. However, the resulting code also had fewer defects. The defect removal savings should more than offsets the development cost increase. This paper sites IBM's finding that defects released to end-users cost an average of $8000 to fix. The lower defect rate more than makes up the slightly higher cost of development. Another quote: we zoomed through QA with hardly a hitch. Everyone, myself included, was amazed that it didn't take weeks to debug, especially given that one of the trees had recently spent SIX WEEKS in QA hell. It was obvious that the pairs had dramatically reduced the defect rate. Other effects of pair programming:
Note also that experts can learn from their non-expert partners: 2005.Jul.07 Thu Quoting Dave Astels on Test-Driven Development (aka Behaviour Driven Development):
Brian Marick has been calling this Example-Driven Development. 2005.Jul.06 WedInside Intuit: How the Makers of Quicken Beat Microsoft and Revolutionized an Entire Industry Notice how the founders got requirements from their potential customers before writing and releasing Quicken. Wonderfalls - The Complete Series "The funniest show you've never seen." Killed by Fox after showing only 4 episodes, the DVD set has the entire first season of 13 episodes. It's what "Joan of Arcadia" might be if that series was a romantic comedy and a lot less self-important. Quoting a review: "What sets it apart from other series is the surreal touch and wicked sense of humor. It's never made clear why Jaye hears toys and bookends talking cryptically to her -- is it God? Aliens? Her own mind?" This DVD set is available only because of a letter-writing campaign organized at http://www.savewonderfalls.com/. Go to that site for links to interviews with the cast, episode guide, scripts (which make good reading), etc. 2005.Jul.02 Sat
Cut Your Car Refueling Costs in Half
According to what I've read here, Brazil lessened its dependency on foreign oil by converting, over the course of the last three decades, to gas-alcohol mixtures. Their "flex-fuel" cars and trucks can run on pure gasoline, pure alcohol or a mixture. Currently alcohol fuel in Brazil, made from local sugar-cane, is half the price of pure gas, and 40% of the vehicles are running on it. Most American cars can run on mixture of up to 10% alcohol without modification. There are perhaps 5 million "flex-fuel" vehicles in the U.S. that can burn a mixture as high as 85 percent ethanol. Unfortunately few gas stations can handle "E85" fuel. Adding ethanol to gas has been called a ploy for farm-subsidies, but if the U.S.A committed to it, we could seriously reduce our fuel prices, reduce pollution, and reduce our dependance on foreign oil. If we also gradually convert to hybrid gas-electric cars that can optionally be plugged in for recharging, we further reduce our dependance on oil. Eventually more electric power-plants would be needed for overnight recharging, but few or no power-plants depend on oil. New plants will be more efficient (and thus less polluting) than old ones, so this path will also lead to eventual reductions in pollution.
"One Best Way" versus "Options"
A book I have talks about preferences for 'one best way' (also called 'process') versus 'options'. 'Process' people want one way to do things, want to be told how to do it that way, and want to be told that it is the 'best' or 'right' way. 'Options' people always want to invent a better way. If they're told 'x process' is the 'best way', they'll try to find a better. Options people are good at inventing processes, but not so good at following them. When talking to 'process' people, if you can establish your credentials as someone who should know the 'best' way, explain how following the process of test-code-refactor insures good design. Explain the rules of good design (modularity, encapsulating behavior, cohesiveness, low levels of coupling, etc.) and give examples of how following the test-code-refactor process produces code that meets those criteria and how design-code-(maybe-test) so often fails to produce code that meets those criteria. If you are talking to options people, you can mention how a whiteboard design session before TDD can let you explore options in the design, and how the rules of good design creates flexible code - allowing more options on how to use the code in the future. To get 'process' people to consider options, repeat this phrase: Repeat this saying often. Establish it as a process to follow to avoid traps and dilemmas. 2005.Jun.27 MonIntroduction One Over recent years, in mailing lists and newsgroups, a conversion about refactoring is repeated many times which goes something like this:
A: "Refactoring is [gold-plating | rewriting | unnecessary ]" and so on... and sometimes...
B: "Of course you'll want to have an automated test-suite to insure your refactorings don't accidentally change the intended behavior. Introduction Two Textbooks and courses try to teach good design, almost never show examples of bad design. The only examples I know of are the excellent books Refactoring by Martin Fowler, Refactoring to Patterns by Joshua Kerievsky and Working Effectively With Legacy Code by Michael Feathers. Code smells (names for various categories of bad design) has a chapter in "Refactoring" but could just as easily have a whole series of books. This essay is an attempt to describe one code smell, and how to correct it. The code smell is known as "Long Method" (or Long Function in a non-object oriented language.) The long method represents a lack of proper encapsulation -- it does too much itself, and doesn't delegate that work to the proper authorities. The irony here is the young coders sometimes think putting everything in one method or in one class is encapsulation. A long method often has varying levels of abstraction within a single block of code. Besides having "too much information" in one place, long methods prevent appropriate code re-use: they encourage cut-and-paste code duplication instead of re-use by calling methods and instantiating classes. Long methods are hard to understand. Hard to debug. And they are very hard to test. Here's an example - a 284-line function from an unnamed open-source project:
In this example, the first block of code after the variable declarations starts with a comment "Remove all carriage returns". Any time you see a block of code within a long method that tells what that code is doing, that's a candidate for an Extract Method refactoring. We extract out that code, and use the comment as the name of the extracted method. In this example, 21 lines of code in this method are now replaced with one line of code. And you can bet that somewhere else in our project, we will also want to do something like this - if not removing carriage returns, then maybe removing some other character. This extracted method is also easy to test. The next comment in the code is "Trim all trailing white space" but unfortunately the code to do that is intermingled with code performing other activities. If the original programmer had been programming by intention, he would have created a method named TrimWhiteSpace, which would have been simple to write and simple to test. In this example, understanding this intermingled code and extracting out the TrimWhiteSpace code is going to be difficult enough that I would postpone that refactoring... getting other cleanup done may allow this refactoring to be easier to do, later. Even if you can't get the code as clean as you would like in one pass, doing small amounts of refactoring every day will help increase your productivity, code reuse, and testability See also:
It's long been accepted that software inevitably degrades until it has to be re-written. Advocates of refactoring and iterative/incremental design/implementation reject that idea. Software doesn't degrade unless acted upon by an outside force (most often, programmers). Here's an example of a new requirement, and two ways to satisfy that requirement. One way creates an unsafe "lump" of bad design. The other way modifies the existing design so that the solution-code for the new requirement continues to meet the criteria for good design.
The new requirement, for Apple MacOS X developers using Metrowerks PowerPlant, is Intel support/Universal Binaries. Specifically, the PowerPlant resource files that are in PowerPC (Motorola byte-order) need to work on Intel (Intel byte-order) as well as PowerPC, preferably without modification. These resources are called Ppob resources, and the are stored in binary form. Apple provided a lump of code that flips the bytes in Ppob resources here. Notice how the function _FlipPPob depends on the layout of almost every PowerPlant view/control class. Notice the big switch statement. Note the tons of duplicated code, such as calls to FlipViewData. One unsafe aspect of this _FlipPPob function is that it attempts to work with every view/control class, but there's a danger in that it doesn't really work with every view/control class. The author of this code may have left some view/control classes out, and of course programmers can (and do) define their own subclasses of PowerPlant view/control classes. Let's re-examine the existing design. Ppob resource reading is invoked by UReanimator. It reads enough to identify a view/control class, and creates an instance of that class via a set of nifty templatized factory objects. By using the factory objects, the UReanimator class does not depend on any concrete view/control classes. To read the PPob resource, UReanimator creates an instance of LDataStream, that it passes into the view/control object, which reads the Ppob data from that stream (typed as LStream, the abstract parent class of LDataStream). Each view/control class knows how to read its own data layout from the stream. While we could modify every view/control class to byte-swap the data it reads from the LDataStream, that would be duplicated code. The place to put the byte-swapping is in LDataStream, LStream, or a byte-swapping subclass of LDataStream or LStream. If we are running on an Intel platform, UReanimator can instantiate the new byte-swapping LStream-derived class (or pass in a parameter to LDataStream) The method that reads an int can be transformed from something like this:
LStream & LStream::operator >> (UInt32 & outNum)
{
ReadBlock(& outNum, sizeof(outNum));
return (*this);
}
to something like this:
LStream & LStream::operator >> (UInt32 & outNum)
{
ReadBlock(& outNum, sizeof(outNum));
if ( mByteSwapping )
{
outNum = ByteSwapUInt32( outNum );
}
return (*this);
}
Instead of a lump of poorly designed code (780 lines worth), we have a change in the UReanimator class (probably a one-line change) and a few changes to a existing LStream class; probably less than 100 lines of new code. The new changes are localized. They are not dependent on all the existing PowerPlant view/control classes. Any view/control class that we didn't plan for will still work. We have modified the code to handle a new requirement, without degrading the design. 2005.May.30 MonQuoting Lisa Crispin's Although our [automated acceptance] tests have found regression bugs, over and over the biggest value comes when we (testers and customers) collaborate with the programmers to create the tests. 9 times out of 10, there is a misunderstood assumption or overlooked scenario. Writing those tests before implementing the code to pass the tests helps flesh out those assumptions and scenarios before coding... saving time and avoiding re-work. The tests codify the requirements much better than non-executable documentation does, simply because it can be quickly executed to show if the product is in compliance, whenever the team needs that assurance. 2005.May.26 ThuSince the Smalltalk compiler is just another set of classes within the Smalltalk environment, you can change it if necessary. David Buck shows the the simple changes he made in VisualWorks Smalltalk to enable programmers to write 45200000000 (45.2 billion) as 45.2b, 3000000 (3 million) as 3m, 60000 as 60k and so on. Check it out. 2005.May.24 TueQuoting Enterprise Systems/Stephen Swoyer:
"Bugs" Versus "Defects" and "Issues"
Johanna Rothman, on her blog, recently wrote "I suggested that my client change his naming of 'bug' to 'defect.'" She got several opposing responses. Interestingly, I think one opposing response may have been from a maker of a bug-databases that uses "bug" in the name of its product. The commenters said things like "bug is just a synonym for defect" and "changing the terminology has no benefit". One response said "what would we call the act of removing the defects? Dedefect?". This reveals the idea that the only way to handle bugs is to remove them after-the-fact. Replacing "debugging" with "defect detection and removal" may be wordier, but it provides a different mindset; it allows thinking about "defect prevention" and different ways to do "defect detection" (such as automated tests and other tools in addition to manual debugging). I almost never hear the phrase "bug prevention" but I've heard "defect prevention" fairly often. And we can use the term "defect injection" to talk about how defects get into the code in the first place. Johanna isn't the first to recommend using the term 'defect'. Her most recent blog entry has comments identifying several sources. Another term that can be used instead of "bug" is "issue", as in an "issue-database" instead of a "bug-database". An issue may be a misunderstanding of the requirements. Usually this gets called an "invalid bug", which can be quite discouraging to whomever raised the issue. An issue is something that still needs some resolution, even if it doesn't identify a code defect. In the case of a misunderstood requirement, the resolution may point to the need for better communication and/or better documentation. If I recall correctly, in one of the four volumes of Quality Software Mangement by Gerald M. Weinberg, Jerry recommended both a defect-database and an issue-database, because one issue could be caused by many defects, and one defect could cause many issues. That implies a database that tracks many-to-many relationships between issues and defects. (There probably isn't a commercial product that does this, though the better bug-databases at least allow linking issue-reports to each other.) Extreme Programming aims at keeping defects (and issues) so low that a database isn't necessary. Instead of creating an issue report, an XP team might create a failing automated test. Such as test is better than a simple database entry because: (1) writing automated tests is a way to flush out understanding of the features. (2) A test that is failing because of code defects should start passing when the code defects are fixed. (If not, then this provides feedback that not all of the defects associated with an issue have been fixed yet.) And (3), being automated, that test can be run many times in the future to detect a recurrence of that issue. 2005.May.23 MonThere's an idea that XP is only useful for frequently-released projects, and not so useful for projects that ship annually or less often. I would say that XP is useful for both. Because XP brings up the quality levels for for those teams who adopt practices like automated acceptance tests, test-driven-development, and pair programming, and drives down the numbers of defects produced, XP projects are able to ship more frequently than once a year. (They don't have to.) They're able to release more often because the members of the team and their users can be more confident that what they ship to the user has useful features and few defects. There's an idea that XP is useful only for projects where the requirements are frequently changing. I assert that our understanding of requirements and of the design is frequently changing even on those projects that seem to have "stable" requirements. All the tools of XP can help a project with stable requirements deliver a superior-quality product. "Stable" waterfall projects often don't "demo" or test their features until the end, at which time they don't have time to accept the changes that the users may want: that's an artificial freezing of the understanding of requirements. "Oh well, those changes will have to go into version 2." Iterative/incremental projects demo and test their features as they are implemented, which allows the users (or user-proxy "project managers", testing staff, and the programmers) to update their understanding of the requirements against the functioning code. I went to Google's open house on May 5 (which happened to be 5/05/2005, but that's probably not significant) and listened to a very interesting set of lectures (and a genuine professional "Engineer/Comedian"). One of the interesting bits is a simple algorithmic infrastructure that Google uses for the index of web-pages, and some other purposes: the "MapReduce" programming model. Google indexes the web using a sequence of 24 MapReduce operations. These operations are specified by a custom high level interpreted programming langauge, and distributed across thousands of rack-mounted dual-processor PCs. Using their fault-tolerant parallel processing infrastructure (implemented in C++). The input data is a few thousand terabytes of data represented as files in Google's custom distributed/fault tolerant file system. The output and intermediate output data are several hundred terabytes. As you might expect, the system is i/o bound -- disk i/o and network i/o. Thus the overhead of an interpreted language for map-reduce operations is not the constraint when doing these big jobs. They have to build their own custom gigabit ethernet switches to handle a thousand (or more) rack-mounted PCs -- commercial ethernet switches top out at 64 PCs and cost too much. Their system has to be fault tolerant, since having at least one PC die (or otherwise go out of commission) each day is not unusual for each of their data-centers. 2005.May.21 SatQuoting The Appreciation Gap by Esther Derby: A recent Gallup Poll report quoted this statistic: “…the number-one reason people leave organizations is that they don't feel appreciated, notes the U.S. Department of Labor.” Read the rest of her article on how to offer appreciations, and how not to screw it up. Don't do a praise sandwich or a cheap gift in place of a sincere verbal appreciation. 2005.May.10 TueSeth Godin asks the question "why haven’t you and your team launched as many Purple Cows as you’d like?" and answers it: "Fear". People would rather do the familiar things, and have an acceptable way of failing than risk blame for failing with something different. (Ironic that "fear" only appears once in the wiki page AnAcceptableWayOfFailing that I just linked to. Go to FindPage and search for pages containing the word 'fear'.) Ricard Semler's Semco does everything that other companies fear: there have no managers and no official hierarchy, employees set their own salaries and hours, there is no "core business". Semler says that sticking to a "core business" artificially limits where they can find profits. (Why do most companies avoid branching out into multiple fields? Fear.) Seth writes books about marketing. He champions creating cheap but thoughtful innovations in products, services, and marketing that customers appreciate. But the interesting thing about his book Free Prize Inside! is that half of the book is teaching you, the reader, how to be a change agent in order to get your marketing ideas accepted within your own company. Get this book to learn how to get your company beyond the fear of the new and unfamiliar. 2005.May.01 Sun
A Big Change for a Sustained Change
Quoting Fast Company's "Change Or Die" by Alan Deutschman :
Check it out, and the blog that referred me to this essay: Creating Passionate Users: Change — or lose you mind. This is why I think that an incremental approach to adopting agile practices is less likely to work: people who make partial changes in their work environment can get the "worst" of the agile and non-agile work experiences instead of seeing improvements; it will not encourage them to go further with adopting new practice. 2005.Apr.29 FriI never blog about where I work, but I'll make an exception today. I'm working at Intuit, on QuickBooks for Macintosh. You can buy the 2005 version at Amazon. I can't say what will go into next year's version, but I assure you, that if you need small-business accounting software on MacOS X, it will be worth the purchase or upgrade price. 2005.Apr.23 SatWe all have various rules in our heads, acquired on our life journey from many sources. Learning new techniques sometimes involves "unlearning" (or modifying) some of those rules. On April 5, 2005, in the XP mailing list, Dale Emery wrote of an example of this. He and Elisabeth Hendrickson were teaching a class on unit testing. He wrote:
To surface what rules are in play here, Dale asked a "why" question thusly: "What concerns do you have about making methods public?"
Having gotten their permission, Dale and Elisabeth demonstrated the refactorings Extract Class and Move Method... creating a new class, and moving those private static methods into the new class, making them public along the way. The original class would contain a local or private member variable of the new class type, and would call those public methods using that new local or member variable. Now, as I understand it, the code being used in the training class didn't have security concerns. And, as it turns out, the private methods were static, so they did not mess with the object-state, and therefore making them public would not make the integrity of the object's state vulnerable to misuse. So the last issue of making these private static methods public was that they would "inflate" the class's interface/responsibilities...
Dale and Elisabeth didn't invalidate the rules these people had about private methods, but added a refinement to them... the idea that private methods can be a "design smell" indicating a class has too many responsibilit | ||||||||||||||||||||||||