![]() |
Articles Feed |
Categories
Archives
- July 2010 (5)
- June 2010 (4)
- April 2010 (3)
- March 2010 (2)
- February 2010 (2)
- January 2010 (1)
- December 2009 (1)
- October 2009 (2)
- September 2009 (2)
- August 2009 (1)
- July 2009 (5)
- June 2009 (2)
- May 2009 (2)
- April 2009 (8)
- March 2009 (7)
- January 2009 (2)
- December 2008 (3)
- November 2008 (5)
- October 2008 (4)
- September 2008 (6)
- August 2008 (4)
- July 2008 (5)
- June 2008 (5)
- May 2008 (4)
- April 2008 (2)
- February 2008 (4)
- January 2008 (2)
- December 2007 (2)
- November 2007 (2)
- October 2007 (2)
- September 2007 (1)
- August 2007 (3)
- July 2007 (1)
- June 2007 (4)
- May 2007 (7)
- April 2007 (2)
- February 2007 (3)
- January 2007 (3)
- November 2006 (3)
- October 2006 (3)
- September 2006 (17)
- November 2004 (1)
Bug free or Free Bugs
by: paul | April 26th, 2009 | 1 comments »
I will not charge a client for a bug fix. Not a penny. If I make a mistake, it is my professional obligation to fix it.
If something doesn’t work with my car, there could be catastrophic consequences, I would be mad unless the company has a solution and offers to fix it for free. As a car owner, I expect nothing less. If a doctor makes a mistake, the patient has the right to sue the doctor for malpractice. Why is it if I make a mistake, I get off scot free? Or worse, I get paid to fix my own mistakes.
I know sometimes the devil is in the details. What is a bug? This is not an easy question to answer, but if there are customer written acceptance tests for the system, the items that fall through are fewer. These are general rules I go by when deciding if something is a bug or not.
One: I made a mistake. These are usually easy to notice, due to the redness in my face when the bug is reported. It is clear to me, I made a mistake, and it is clear I need to fix the bug. It should not cost the customer any money and the process should be transparent.
Two: There is a mistake that both development and the customer team should have caught. A scenario the customer should have specified and during development I should have noticed and brought to their attention. The fault goes on both parties and can be fixed at some version of half price (i.e. every other or a half price story).
Three: The application doesn’t behave correctly due to a missed specification. a scenario that the customer missed or a piece of the business logic that wasn’t fully correct. Almost everyone makes some mistakes. This is not a bug to me, but a feature enhancement. It can be written up as a story and completed.
I think it is important to build faith with the customer that there is a team of developers that are accountable. When you create the culture of accountability, it spreads. The customer team is willing to be accountable when they make a mistake. When no one is afraid to make or admit to a mistake, the projects quality is positively affected. Finally, by taking financial ownership of a bug, I build a trust with my stakeholders of the project.
Craftsman Swap Day 5
by: jim | April 19th, 2009 | 2 comments »
Today turned out to be little coding and quite a bit of retrospective. I was able to pair with Nate Jackson for a little bit on the iPhone application that he’s working on. I also tested a Webrat patch on a brand new Rails application via a cucumber test to wrap that up.
Over some excellent Greek food, we had an informal retrospective. A few notes from the discussion:
- The duration of the swap seems just about right. As Dave said, the law of diminishing returns likely kicks in after a few days or a week, and the cost of another week would likely outweigh the benefits.
- Kevin Taylor brought up the idea of more planning in response to my admission that I was a bit nervous Monday morning. The source of my nervousness was really due to not knowing exactly what to expect. I think that the lack of planning was actually extremely beneficial to the flow of the week. I was able to pair with many different people, and the flow between pairs was very organic and natural. Having a plan to follow probably would have changed this dynamic for the worse.
- The feeling that the swap was successful was unanimous. We all felt that we benefitted from my being onsite for the past week. I knew that I was learning a ton and having a great experience, but I was extremely happy to hear similar feelings vocalized by the Obtivians.
This was an absolutely fantastic experience. It gave me a chance to step away from client work for an entire week and explore new technologies, techniques and ideas, work with different people, and learn a lot. I believe that both 8th Light and Obtiva have compared this time away from client work to spending a week at a conference. From my experience this past week, this was better than any conference I’ve attended. Listening to speakers talk about new ideas is one thing; actually pairing with great developers and learning hands-on is in a different league.
Craftsman Swap Day 4
by: jim | April 17th, 2009 | 1 comments »
Dave Hoover and I got back to the Mad Mimi feature implementation first thing in the morning. Dave had left a failing test the previous afternoon, which was meant to serve as a reminder for where we left off. This technique is great when you need to stop in the middle of what you’re doing, and it works well as long as you know what the next step needs to be. This should help you reestablish the context you were holding when you stopped. Of course, if you’re at a natural break point and the next step isn’t clearly defined, I tend to avoid writing a failing test. Often when I come back to code after a break, I will find a simpler way to approach the problem at hand. I don’t like short-circuiting that process by leading myself towards a solution I saw when I was leaving the code. Anyway, in this case, it was clear where we needed to go, and the failing test got us right back to it.
Mad Mimi has grown organically without any traditional marketing or advertising efforts. That is not to say that there aren’t a lot of users and a lot of data. There are a few database tables that are quite large, and optimizations have been put into place in order for the system to remain responsive. Optimizations are usually going to make a system more complex from a code standpoint. The common advice to avoid premature optimizations is valid exactly because of this. In addition, you’re almost always going to be wrong about where the bottlenecks will occur in your application. If you were to do some premature optimization, you’ll likely find yourself with overly complex code that is still slow. Mimi has been optimized only as needed, and the code base is better for it. One of the optimizations that has been made is limited use of stored procedures.
Stored procedures are not easy to test. The feature we were working on required us to pass an additional parameter into the existing stored procedure. Once we dropped into stored procedure land, we lost the safety net of our tests. So we approached the problem stepwise, in a way “test driving” the stored procedure from the user interface. Fortunately, the stored procedure isn’t overly complex, and executing a couple manual test cases made us confident that the change was correct.
After we finished the feature, it was time to deploy so the customer could test and provide feedback. The deployment process for this project is different than just about every other project I’ve ever been on. Dave has taught the customer, who is not a programmer, how to update from Subversion, run data migrations, start/restart his local web server and BackgroudRB services, etc.. With just a little help, the customer has the system running in development mode on his Mac! Not only that, the customer does the design work for the site. We just added the appropriate view files while implementing the feature with a note for the customer to come through and style the HTML and provide the correct content. The customer is heavily involved in the process, and clearly cares very much about the product. Dave is extremely lucky to have this kind of customer for this project.
Craftsman Swap Day 3
by: jim | April 16th, 2009 | 0 comments »
I spent my day pairing with Dave Hoover on Mad Mimi. The first thing I noticed during our pairing session is that Dave has many more responsibilities within Obtiva than just coding. The opportunities for distractions seem to increase exponentially the closer you are to the details of running a business. The context switching involved in handling interruptions while coding is something that I really struggle with, but Dave is quite adept at it. Having a pair helps with ignoring these distractions, for better or for worse. I’ve been using the Pomodoro technique for the past couple months with some success to try to help stay focused and deal with distractions, but I’ve found that the best way to avoid distractions is to pair program. Dave “thanked” me at the end of the day for helping him ignore his growing list of unread emails, which he would have to catch up on later. The upshot of this is that we got some concentrated work done without a ton of distractions taking our attention away from the task.
At the start of the day, we picked up a new Mad Mimi feature and started discussing it. As is usually the case when beginning to dig into a feature, we quickly came up with some questions for which we would need the customer’s input. Unfortunately, the customer wasn’t available; after making note of the questions, we started talking about a change that will be rolled out sometime in the near future, but should not be added just yet. Dave accurately identified the need to create a branch for the feature. I’ve recently had a similar need for this kind of feature branch, and I’ve done this a couple times in the past few months. Dave hadn’t created a branch in Subversion before, so here was a chance to teach him something.
There are a few different reasons why you might want to use this technique, but this is the situation I’ve been running into lately. There are (at least) two different types of branches: bug fix / release, and feature. When releasing a new version of the system, a release branch can be created in order to allow bug fixes to be quickly and safely developed and deployed. A feature branch, on the other hand, comes in handy in a number of situations. One situation is when you want to develop a new feature that needs to be deployed as an update to the release. If you were to develop the feature on the release branch, you would run the risk of making deploying the branch at a moment’s notice difficult or impossible. By developing on a feature branch, you can develop and test the feature in isolation, then merge the changes back to the release branch (and/or the trunk). In Dave’s case, he releases from the trunk, but he wanted this particular feature to not be deployed until he’s ready. A feature branch works great in this case, and he’ll only have to merge back to the trunk since there isn’t a need for a release branch. Once that merge is done, the branch can (and should) be killed; the key to keeping your branches manageable is to limit the number of branches that you ever need to think about. The goal should be for each branch to have the shortest lifetime as possible. For a decent branching strategy, see this article.
After Dave successfully created his branch (and played around with merging to understand how that would work), we went ahead and started implementing this new feature on the branch. Of course, we soon were able to get the information we needed from the customer to continue on the original (higher priority) feature. Since we were working on the branch, we didn’t need to abandon our work in order to start on the other feature. We just opened up the trunk’s code in our editor and away we went. This is another advantage of working in a feature branch: it’s simple and cheap to jump around. We spent the rest of the day working on this feature, though we ended the day without completing it. We will finish it up on day 4.
Dependency Inversion Principle and iPhone
by: eric | April 16th, 2009 | 0 comments »
Vertical Dependency
While working on the slides for our upcoming* talk on TDD for iPhone I asked Eric Meyer why we need the Dependency Inversion Principle. He eagerly answered, “Testing!” which made me laugh because I wasn’t giving him a trivia quiz, and I love Eric’s enthusiasm. He’s right, but the answer is circular. TDD is great because it gives us better designs that correspond to the DIP which enables TDD. Ha! I win, would you like to buy my consulting services? I have plenty of three letter acronyms where those came from.
Fortunately this isn’t the case, as the DIP existed well before TDD was popularized, but recently there was a bit of a dust-up over the SOLID principles. Many developers don’t understand why dependency inversion is important, so let’s demonstrate the usefulness in the Objective-C language on the iPhone.
The Dependency Inversion Principle (DIP) reads as follows:
A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
So your code should depend on interfaces not on concrete classes, but why? Let’s look at a useful example in the Objective-C built in classes.
- [NSString stringWithFormat:@"Object is: %@", object];
Do you see the DIP at work? It’s subtle here, but look at the method stringWithFormat, which takes a format string and then a list of parameters to fill in the format specifiers. The format specifier here is “%@” which takes any Objective-C object and calls descriptionWithLocale on it, or description otherwise. The object that is passed in here could be anything because all the stringWithFormat method cares about is that the object provides at least one of the two methods above. It’s depending on the interface, and not the concrete class. Let’s imagine a world where this didn’t exist, and the NSString method could only be composed of other strings. Suddenly you’d have something like:
- return [NSString stringWithFormat:@"Object is %s", [object toString]];
That’s not bad at all is it? Well what if there are different objects.
- return [NSString stringWithFormat:@"Object is %s %s %s", [object1 toString],
- [object2 toS],
- [object3 stringify]];
Are you starting to see the problem? Suddenly I can’t use this method without being cognizant of the specifics of the object I passed in. The minute one of these interfaces changes I have to change my code. I stole this method from some real code, so let’s look at it with some context. Once upon a time I was writing a mock object for an iPhone application. Now iPhone doesn’t have a good mock object framework, at least that I’m aware of, so I was rolling my own mock. The mock logged calls to it which I then queried. To construct the string I used this method:
- +(NSString *) createStringFrom: (id) cell at: (CGPoint) point
- sizeOf: (CGRect) rect
- {
- return [NSString stringWithFormat:@"%@ %f %f %f %f", cell,
- point.x,
- point.y,
- rect.size.width,
- rect.size.height];
- }
Don’t sweat the details right now, they’re not important. Instead look at how you can see the DIP succeeding and failing in the same code here. The first parameter to createStringFrom is a cell object, but it’s of type id, so it could really be anything. That’s good because I wasn’t sure when I wrote this mock what type the cell would be and I’d generally prefer that my mock object not have to know the details. In fact the cell did change type several times which is why insulating yourself from change is so important. There’s an assumption by many developers that you should only worry about the things you’ll be changing in the future, but the best gauge of future changes is present changes, and if you’re changing a module frequently during development as you get it right you’ve found a likely change point. Even if you haven’t, you’ll make your life simpler in the present by insulating yourself from that change, speeding up your development cycles. I digress.
The rest of this method isn’t so lucky. You see CGPoint and CGRect are C structures, and I am left to write these as floats in the string (%f). If I was to change the parameter types frequently I would probably introduce some way to insulate myself from that change as well, possibly by wrapping them in objects. Since those parameters have not changed I haven’t done that, although I’m thinking about it.
Our current apprentice Colin has given a first-hand account of refactoring to DIP here as well.
How do I do it?
Eric and I are working on a program to run Conway’s Game of Life. On the iPhone screen you see Rounded Rect Buttons that change color based on whether a cell is alive or dead. The problem is that we don’t really want to drag and drop 300+ tiny buttons on the screen with interface builder. Instead we want to create them programatically, when the GameOfLifeViewController is loaded. Again I don’t want to overload you with extraneous details, so just take my word for it that there is a GameOfLifeViewController, and it needs to generate buttons. Here’s what my first pass at a test looked like in rough pseudocode:
- for (int row = 0; row < 15; row++)
- {
- for (int column = 0; column < 15; column++)
- {
- button = GetButtonViewAt(row, column)
- STAssertEquals(expectedSize, button.size)
- STAssertEquals(expectedLocation, button.location)
- STAssertAction(expectedAction, button.action)
- }
- }
There’s a lot wrong with this test. The most obvious is the hard coded 15. What happens when we want to change the number of rows and columns? Now we have to change code in multiple places. Naturally we did in fact change this, so I would have paid for this decision quickly if I hadn’t already refactored it away. The second is that there’s going to be more than one test for creation, each of which will have to perform the same 15x15 loop. The annoyance of constantly changing this will reveal the root the problem, the GameOfLifeViewController should really only be in charge of GameOfLifeView and really doesn’t need to know the details of the creation of its subviews. It’s violating the Single Responsibility Principle. We can fix that with a factory object, and will do so, but that will violate the Dependency Inversion Principle. So we’ve got one DIP violation, an SRP violation, and a potential second DIP violation, all demonstrated in 6 lines of test code.
Fortunately we’re going to introduce some interfaces to clean that up. The first is an interface to the board object. Why the board? Well the board should know the number of rows and columns it has, and if we can inject a fake version of the board we can actually set those to be 1 and 1. That’s will make our test focus on what we are truly concerned about. The second thing we’re going to do is introduce the Abstract Factory pattern to create our button views. It’s a bit of a heavyweight solution but its going to pay off by cleaning up our client code dramatically. We’ll have a View Controller concerned with controlling the view, and a creation factory concerned with creation, as well as a board that keeps track of rows. Let’s take a look at the test now, again in rough pseudocode.
- -(void) setUp
- {
- controller = [[GameOfLifeViewController alloc] init];
- controller.board = [[MockBoard alloc] init];
- controller.buttonControllerFactory = [[MockButtonFactory alloc] init];
- }
- -(void) testCreationOfButtons
- {
- [controller viewDidLoad];
- bool called = [controller.board calledWith: params that: I want: true];
- STAssertTrue(called);
- }
So what’s going on here? In the setup we load up the board with a fake board, one that returns 1 for its rows and columns, and we load up its buttonControllerFactory with a fake buttonController Factory. In our test we call the viewDidLoad method, which is called on controllers after their view is loaded. Duh. That’s the method where we use the buttonControllerFactory. Then we call a method on the mock factory calledWith: that takes the parameters I expected the creation factory to receive. Finally we check that it was called with the assertion. So far I’ve only been showing test code, because I wanted to demonstrate just how much cleaner our tests have become. Now there’s no loop, no hard coded constants, and no testing the internals of the creation factory in the view controller. That isn’t to say we don’t test the creation, we just do that in a different test case, the one that corresponds to the ConcreteButtonFactory class. As a rule if our test code is cleaner, our implemenation code is cleaner, and that’s the case here as well.
Let’s now look at the real class, and how to enable Dependency Inversion on it. We have to start with Objective-C protocols. Protocols are very similar to Java interfaces, and just as useful, but are strangely underrepresented in XCode. As best I can tell** just create an NSObject subclass and remove the .m file and any imports from the .h. Here’s the protocol from the unfortunately named ButtonControllerFactoryProtocol.h:
- #import "ButtonController.h"
- @protocol ButtonControllerFactoryProtocol
- -(ButtonController*) createButtonControllerForCell: (id) cell
- at: (CGPoint) point
- sizeOf: (CGRect) rect;
- @end
As you can see the interface only defines one message, createButtonControllerForCell. It returns the ButtonController object, although it could return an object conforming to a protocol. Indeed one could argue it should, as that would streamline my mock object. Hmmm… I see a refactoring in my future. Now let’s look at the class definition for the ConcreteButtonControllerFactory, which is the object that is actually used when the real app is running.
- #import <uikit />
- #import "ButtonController.h"
- #import "ButtonControllerFactoryProtocol.h"
- @interface ConcreteButtonControllerFactory :
- NSObject<buttoncontrollerfactoryprotocol> {
- }
- @end
Not much here is there. The important part to look at is NSObject
- #import "ConcreteButtonControllerFactory.h"
- @implementation ConcreteButtonControllerFactory
- -(ButtonController*) createButtonControllerForCell: (id) cell
- at: (CGPoint) point
- sizeOf: (CGRect) rect;
- {
- ButtonController *controller =
- [[ButtonController alloc] initWithCell:cell];
- UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
- [button setFrame:rect];
- [button setCenter:point];
- [button setImage:[UIImage imageNamed:@"dead_cell.png"]
- forState:UIControlStateNormal];
- [button addTarget:controller
- action:@selector(bringToLife:)
- forControlEvents:UIControlEventTouchUpInside];
- controller.view = button;
- return controller;
- }
- @end
This isn’t an article on UIKit so I’m not going to get into the details of creating the button. You can see again that I’ve ignored the DIP for creating the UIButton and the ButtonController. Since this is a factory I’m okay with this violation. I could have created abstract factories for the UIButton and ButtonController and passed them in here but I don’t believe I’d gain any benefit from that implementation. That’s verified by my testing, as writing tests for the creation of these objects was hard before I introduced this factory but isn’t any longer. If it becomes difficult then I’ll consider making the change to two factories. Now we need to inject the concrete dependency. In a Java project you could do this in a main method or with a DI framework like Spring. We’re going to be using my favorite DI framework of all time: Interface Builder!*
It’s a common misconception that IB is just a GUI builder. What’s great about it is that it doesn’t just generate a bunch of code, but instead “freeze dries” real objects to be instantiated when the program loads the NIB file. Furthermore while the most common Outlets are UI elements there’s no reason other objects can’t be used. Let’s take a look at my NIB document window for the GameOfLifeViewController:
As you can see I’ve got objects for the Game, ConcreteButtonControllerFactory, and the Board. That’s because all three of the objects are injected at runtime, while the code depends on protocols. To add your own objects to a NIB file simply by selecting it from the Library:
It’s inspector window will have a class drop down, so set this object’s object to your class. Finally you need to connect the outlets to actual outlets in your code. Let’s look at the Outlets tab in IB for my controller (the File’s Owner):
Notice how the controller still thinks of them all as NSObject? So if I change the objects it doesn’t care, as long as I conform to the interface? Finally let’s take a peek at the controller interface file:
- #import "BoardProtocol.h"
- #import "ButtonControllerFactoryProtocol.h"
- #import "GameProtocol.h"
- @interface GameOfLifeViewController : UIViewController {
- IBOutlet NSObject<boardprotocol> *board;
- IBOutlet NSObject<buttoncontrollerfactoryprotocol> *buttonFactory;
- IBOutlet NSObject<gameprotocol> *game;
- }
- @property(nonatomic, retain) NSObject<boardprotocol> *board;
- @property(nonatomic, retain) NSObject<buttoncontrollerfactoryprotocol> *buttonFactory;
- @property(nonatomic, retain) NSObject<gameprotocol> *game;
See any dependencies on the actual objects? Nope - just protocols. There are properties for the objects so that I can inject the dependencies through the tests, all of which pass in fake versions as various times depending on how it suits the test. It may be unhealthy - but I find this incredibly cool.
Summary
What I love about Interface Builder is setting this up is extremely simple. Writing tests for the controller that is based on these dependencies is also extremely simple, far simpler than just depending on the concrete objects. It’s funny how developers often complain about the time wasted writing tests or “over-engineering” in this way, but once I broke these dependencies I got things done a lot faster. I’m writing working code without loading the actual simulator. I write my tests first, making progress, and when I’m done for the session I run the app once or twice just to verify I haven’t done anything like forgotten to inject the dependency. Compare that to running the simulator constantly, each time hoping your code is functional. Who’s wasting their time now?
FootNotes:
* Uh yeah we gave that talk already. These take a while!
** I’m 99% positive there’s a better way, and that I haven’t found it. Tell me if there is!
*** To be fair it’s the only one I’ve actually used.
Craftsman Swap Day 2
by: jim | April 15th, 2009 | 0 comments »
Hackfest
Every week, Obtiva sets aside time for what they call Hackfest (I believe this grew out of Geekfest, which is what I’d describe as a Lunch ‘n Learn). Everyone is encouraged to take some time over lunch to work on some non-client code, and the emphasis is on “doing” instead of just talking. I really like the idea of HackFest. By encouraging employees to spend time on side projects, the company (and employee) can only benefit. Client-work is necessary in order for us to get paid, but the passion for the craft cannot flourish if the only projects one is working on is for someone else. There is something selfish about software development, and that isn’t a bad thing. In order for you to enjoy your job, I believe that you have to get satisfaction out of what you’re doing every day. I think that’s why I’m such a fan of Test Driven Development. There is always a goal, usually only a maximum of a couple minutes out: either get the current test to pass, or write a failing test (don’t forget to refactor). This constant flow is encouraging because every time you get your tests to green, there is a feeling of accomplishment.
So for Hackfest, I joined Andy Maleh on his project, which is in the really early concept phase. We worked through some mockups and designs for a social website that he has in the works. Basically, we designed and drew up paper mockups of a critical feature. Paper prototyping is intended to help developers create a user experience that meets the user’s needs. One possible mistake that you can make, as I certainly have, is to forget that you have AJAX effects at your disposal in your web application, and that your prototypes should be built with that in mind. Instead of every user input resulting in a new (paper) page being put down in front of the user, consider simulating the slide effect that would occur when you click an “add” button which dynamically inserts a new form into the page. By just replacing the entire page in front of the user, you’re really missing the user experience improvements that AJAX can give you. Andy’s mockups did this well, and I think that they will result in more successful user testing.
Afternoon Pairing
I paired with Nate Jackson for the good part of the rest of the afternoon. Nate was Obtiva’s third apprentice and is now a consultant. We were working on a really cool iPhone application, but mainly I just slowed Nate down. One thing that bothers me about developing for the iPhone is how difficult it is to test the code that touches the framework; in particular, all of the delegates in the controllers. I would like to gain a better understanding of how the framework really works so that maybe I can find some better seams for testing purposes. As a result of this inherent difficulty, we didn’t test drive the code that was written, and I noticed that the rhythm of the pairing session was distinctly different from the ping-pong sessions that I’ve grown accustomed to. Without the failing test/passing test rhythm, there aren’t distinct break points where the keyboard naturally gets passed. So though the pairing session was successful, it definitely felt different.
Craftsman Swap Day 1
by: jim | April 14th, 2009 | 2 comments »
My initial impression of the Obtiva office is the relaxed atmosphere. It consists of a big, open work area with lots of pairing stations. At the beginning of the day, I jumped right into an internal stand up meeting with a few of the Obtiva folks. It was short, sweet and to the point. One interesting difference between this and the way I’m used to doing stand ups is items that would normally be handled as followups were discussed during the individual updates. The discussions never wandered far, so this worked quite well. I was paired with Tom Kersten most of the day on some client work.
While working with Tom, I was introduced to a few tools that I hadn’t used before.
- Fluid, a Site Specific Browser that basically allows you to sandbox a web application in a dedicated browser process. You can Command-Tab to an icon that you assign, and if (or more accurately, when) your browser crashes, Fluid does not.
- GNU Screen, a window manager that removes the latency from remote pairing. Of course, you need to use an editor like vi for it to work for you.
- Text Expander, which does exactly what it says: you specify shortcuts, e.g. brb, and it will expand it to the full text, e.g. Be right back.
- Rak, a Ruby replacement for grep. It gives you the line number of the matched text, which regular grep can do too but it always takes me 5 minutes to figure out the right option to pass to get it to work.
Tom’s editor of choice (even for Rails development, which is what we were working on) is VI. I was a little worried at first, but I picked up a few tricks and it didn’t slow me down quite to a crawl like I had anticipated. We were also using Cucumber to test-drive the features we were working on; this was Acceptance Test Driven Development (ATDD) in that the test represents the behavior that the software should have. I have been test-driving my Rails code at a lower level (think model and controller), and I think that there is a ton of value in moving up to a higher level to define the behavior.
As we were implementing a feature, the following problem presented itself. We’re using Webrat in the step definitions of the Cucumber tests. The way Webrat makes you click on buttons/links is slightly problematic. You can use the text or the id of the link to tell it to click. The text is probably the least permanent part of the entire link, so by relying on the text your tests are inherently fragile. By adding an id for all the links in your application, you are cluttering up the DOM. Adding an id to all your elements is a valid solution, and one that have fallen back to in the past. But this seemed backwards; the testing is forcing us to clutter up our HTML. The testing framework should be working for us, making it easier to write valid, uncluttered HTML. Perhaps we could use the href attribute of the anchor tag to click a button? Unfortunately, Webrat doesn’t support that, but it is open-source so I ended up forking and adding this feature. However, after more discussion, I’m not convinced that this approach is indeed better than using the id approach.
Ethical approach to Software Bugs
by: paul | April 8th, 2009 | 8 comments »
Software bugs are errors or omissions in the work we create. They are our mistakes as software developers. I am going to leave defining and dealing with bugs to a blog post I wrote last summer. I would like to take a closer look at software bugs, using an ethics metaphor to examine some of the rationale. For the sake of this blog, we fall into three camps: Software Utility, Software Relativism, and Software Craftsmanship.
Software Utility
“Fixing bugs is only important when the value of having the bug fixed exceeds the cost of the fixing it.” -- Joel Spolsky
This is the camp of software utilitarianism. I understand applying this rubric in terms of return on investment might have J.S. Mill turning over in his grave, but there is a strong correlation here. According to wikipedia, utilitarianism “is the idea that the moral worth of an action is determined solely by its contribution to overall utility.”
Let’s take an example using Mr. Spolsky’s argument. We need to be able to quantify in dollars the value of Bug X. It is worth $1,000 to the company, because the ramifications of not fixing it means we only lose 2% of our user base. The cost of fixing this bug is $3,000 worth of development time. According to Software Utility, this is a no-brainer. Let the bug go, find something more important and more financially inviting to work on.
The problem here is there are so many unquantifiable variables in the equation. How do you put a dollar amount on instability? When someone tries to reuse the code that has a bug in it, you are spreading the bug throughout the system, literally, like a disease. You can deal with smaller known bugs now, or you can have them bubble up as diseases later on, where the price of fixing them will grow much higher. The software craftsmanship approach means you don’t have to get into the tricky business of quantifying everything.
Software Relativism
I have heard that, “my team doesn’t test drive their code, and so I don’t.” This is software relativism. Famous moral relativist J. L. Mackie “argu[ed] against the objective existence of right and wrong as intrinsically normative entities on fundamental grounds unsure what kinds of things such entities would be, if they existed.” In software, this is saying that whatever works, works.
Or, “We try to keep the bug list low.” Specific practices will be chosen due to their ease or convenience, rather than due to their inherent worth. If I join a team that is making money writing software without testing it, I shouldn’t judge, but do the same. There are no objective rights and wrongs, just a series of things that will work for specific circumstances.
One of the problems is when you get into a negative feedback loop. When everyone is sprinting for the finish line and there are no objective standards, you are going to end up with junk at the other end. We need a threshold of quality we will not fall below. Otherwise you will end up with a system of piecemeal quality, with parts that are stable and parts that are not stable. This is arguably worse than an unstable system. Either way, if becomes costly to maintain or just plain unmaintainable. The software craftsmanship approach avoids this problem of relativity by placing minimum objective standards.
Software Craftsmanship
I am calling the third section software craftsmanship because it fits conveniently in line with the software craftsmanship movement. It is based on the ethics of Immanuel Kant, specifically his first formulation of moral theory. It stated, “Always act according to that maxim whose universality as a law you can at the same time will.” The classic example is dealing with lying. Can lying for the sake of some other good be considered moral? Kant says to ask yourself: “If everyone lied, would the maxim still hold?” No, our society functions based on a presumption of honesty. Well, if everyone overlooked bugs or shipped with known bugs would the maxim still hold? No, software functions based on the presumption of quality. The user expects the software to work. To me, this means as an aspiring software craftsman, I can not ship with any known bugs. If you make lying acceptable, your neighbor will start to lock their doors. I am afraid that many software users have already locked their doors. So, a software craftsman can not live with bugs in their system.
I ship with no known bugs. As Thomas Jefferson says, “In matters of principle, stand like a rock; in matters of taste, swim with the current.” Shipping with no bugs and cleaning up my own messes is a matter of pride and principle. It can’t always be done with ease or convenience. Sometimes it means fixing some obscure feature you are sure no one will use.
#



