-
Git sucks. Git rocks
I'm currently working with a client who use subversion. Having moved to git (via darcs), it's hard to go back. To make things worse, the subversion server is based in Australia over a link which is sometimes slow. Luckily, git svn comes to the rescue.
Today, however, it stopped working. I made some changes in a checked out subversion copy — I had to do this as an old version of Piston is used to manage external dependencies. I pushed my changes, and then used
git svn rebaseto pick up the changes. Nothing changed. Odd. I rangit svn fetchand sure enough it could see the changes, butgit svn rebasedidn't think there were any changes to apply. At this point I (rather dumbly) thought "oh well, the changes are in remotes/trunk so I'll just merge them". Sure enough this worked and the tests started passing again and all was happy.Later, I tried to push my new changes.
git svn dcommitrevealed something interesting — it thought I was on a tag in subversion rather than on trunk. I have no idea why. This wasn't the case a couple of days ago and I'm not sure what changed. I also had a commit that I couldn't push to trunk. Git had gotten me into a mess and I couldn't see why. It can be very difficult to understand what is going with git sometimes. Complexity sucks.However, git is powerful as well as complex. I knew I had a crappy master branch with a change I needed. All I needed to do was throw away master, recreate it and cherry-pick my change (now floating in the ether) back in. So all was resolved in four simple commands:
<master> $ git checkout remotes/trunk Note: moving to 'remotes/trunk' which isn't a local branch If you want to create a new branch from this checkout, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b <new_branch_name> HEAD is now at 4a9b02a... $ git branch -D master Deleted branch master (was 50fa555). $ git checkout -b master Switched to a new branch 'master' <master> $ git cherry-pick 50fa55504c9bcc50232d2f7269c3d5fdde9d291c Finished one cherry-pick. [master eba5a7d] ... 4 files changed, 19 insertions(+), 4 deletions(-)Complexity still sucks, but I'm more likely to let it off when it comes with awesome powerfulness.
26thFeb2010
-
When nothing works
It's interesting when you come across people who say "I tried scrum but it didn't work, and I also tried XP and that didn't work either, and I tried RUP and that didn't work, and pair programming didn't produce better code, and TDD didn't reduce bugs and..." If nothing works then here is a clue: you're doing it wrong.
Of course sometimes particular methodologies don't work for a given situation or a given team, but when nothing works then something is obviously up. There are only two possible factors which could remain constant throughout all the failures: the individual/team for whom nothing works and the environment in which they are trying different methodologies. In reality the two are likely to be intertwined. People rarely complain about approaches not working when things are going well, so in reality what you are likely to find is a team and environment which is never going to succeed until either or both change.
One of the easiest things to do in this kind of situation is to change methodology. However, unless you also address the underlying issues then changing methodologies is going to do no more than offer a different way to fail. What needs to happen is to address the underlying problems. Introducing a different methodology can act as a catalyst to bring about this change, but it should never be used as a diversion from it.
So the next time somebody lists multiple practices that don't work for them, try to find out what it is they're not addressing. Dysfunctional teams have dysfunctional projects. Chaotic environments have chaotic environments. If you want to do things better, focus on fixing these issues first.
25thJun2009
-
Stubbing Stole My Focus
It's getting late and I need to be up for work tomorrow, but I just thought I'd share this quickly before I forget it. I was writing some billing code for Simply Agile and I wanted to check that for the initial payment previously authenticated charge was being captured and for subsequent payments a Repeat charge was being created. This code originally only dealt with repeat charges, lived in the Organisation model and looked a little like:
def take_payment repeat = Repeat.create!( :authorization => payment_plan.repeat_billing_authorization, :amount => payment_plan.amount, :description => name ) end(There's more to it than this, but that was the bit I was trying to replace.)
The specs for this were pretty straightforward:
it "should create a new repeat payment" do repeat = Repeat.new Repeat.should_receive(:new).and_return(repeat) @organisation.take_payment end it "should pass in the amount from the plan" do repeat = Repeat.new Repeat.should_receive(:new).with do |params| params[:amount].should == @payment_plan.amount end.and_return(repeat) @organisation.take_payment end it "should pass in a description" do repeat = Repeat.new Repeat.should_receive(:new).with do |params| params[:description].should == @organisation.name end.and_return(repeat) @organisation.take_payment endAs part of the change I was moving I wanted to move the code which creates the Repeat payment out of the Organisation model and into a method in Payment called
capture_or_repeat. I wrote some specs for Payment to make sure this new method behaves how I expect and then went back to change the Organisation. I started by changing the specs to make sure that instead of creating a Repeat payment,Organisation#take_paymentcalledPayment#capture_or_repeat. It went thus:it "should call Payment#capture_or_repeat" do @initial_payment.should_receive(:capture_or_repeat).and_return(@mock_payment) @organisation.take_payment endOf course, this doesn't work.
@initial_paymentis an object in my spec and is not the same object that is being instantiated inside Organisation. To get this to work, I need to stub Organisation's retrieval to use the same instantiated object. This isn't quite so simple though, as Organisation is linked to the initial payment through an intermediary model called PaymentMethod. So I started to write code like this:before :each do ... @initial_payment = @payment_method.initial_payment @payment_method.stub!(:initial_payment).and_return(@initial_payment) @organisation.stub!(:payment_method).and_return(@payment_method) endAlarm bells went off at this point. I'm sure the above can be rewritten more cleanly, but I noticed the process of stubbing was causing me to focus on the relationship between Organisation and Payment, and on the technical problem of stubbing in the right place and caring about which object was instantiated where. In reality, however, this doesn't solve my problem. What I really want to care about is whether an Organisation creates a Capture or a Repeat when I call
#take_payment.I took out the stubbing code, removed any check that
#capture_or_repeatwas called and replaced it with checks that either Repeat or Capture was called. This results in quite a bit of duplication between the specs forOrganisation#take_paymentandPayment#capture_or_repeat. I'm OK with that, because I really expect them to do the same thing. The fact that one calls the other is somewhat irrelevant when I'm thinking about the behaviour of these methods.The 'spec/mock' vs 'call something and then check state' debate will run forever, and I'm not trying to say that the 'spec/mock' approach is definitively bad. I just thought I'd share an example of where I decided that calling something and then checking the state helped me to focus on the problem in hand rather than on the details of how something was implemented, and how it lead to some specs that I think are easier to understand.
23rdJun2009
-
How I Track Defects
When we did an early beta release of Simply Agile, I asked Simon, a friend of mine, to try it out and give us some feedback. He managed to generate a 500 error in about 5 minutes. Hardly an auspicious start, but this is why we test. When feeding back the problem he asked "why don't you have a bug tracker!?" It's a fair question. I'm starting to think that bug trackers discourage developers from producing high quality code.
Fundamentally, the problem with bug trackers is that they create an easy answer to the question "what shall I do with this bug report" but they provide a less easy answer to the question "when is this bug going to get fixed". In practice, I've found that they provide a handy place to store problems while you get on with adding new features. But this is the wrong approach. Instead, I am trying to adopt a zero tolerance bug policy.
This is how I track bugs:
$ git stash save <some message about what I was working on> $ git checkout master <fix bug> $ git commit -a $ git push $ cap deploy # This goes to the staging site <test on staging> $ git checkout <old branch I was working on> $ git stash popAt some point I'll deploy the code into production too. The timing of this depends upon the severity of the bug and the impact of the fix.
The astute amongst you will have noticed that I didn't actually track the bug. I interrupted what I was doing, fixed it right away, and then went back to what I was previously working on. It's a great approach.
There are probably lots of arugments about why this won't work for you. After all, you work in the real world where people have deadlines and you can't just drop everything every time a bug report comes in. However, I think that most of these turn out to be less important than the initially appear.
I've addressed some of the key issues below. Most of these objections I've stolen from Simon.
Objection
1: My head would explode if I didn't have somewhere to track things!
This is the point where I first concede that not all bugs can be tracked in this way. Some 'bugs' are actually feature requests. Typically these start with people saying 'but it doesn't do X'. These feature requests get turned into stories and added to Simply Agile.
Some bugs also require architecture changes. However, most of these are linked to changes in features and can be turned into stories. Serious bugs — where it becomes apparent that you got some design seriously wrong — should still be dealt with straight away.
Tracking is the wrong issue to focus on here. A bug tracked is a bug unfixed. Tracking is solving the wrong problem.
Objection
2: Does this mean you don't get to go home until all bugs are fixed?
No. It means that you don't get to work on new features until all the bugs are fixed. Dealing with bugs straight away doesn't mean you have more work to do. You were going to fix them all anyway, right?
Objection
3: How will you remember all the bugs between now and tomorrow morning?
There are two parts to this. The first is easy. Sometimes I write stuff down on a scrap of paper or in a notebook. This, I think, is different from having a bug tracking system. It (intentionally) doesn't scale up.
The second part is more difficult. I try to test slower. Gradually increasing your audience means that there won't be a sudden onslaught of bug reports. If you get the speed right, bug reports will be slow enough that you can fix them straight away. If there is a significant imbalance between the speed at which you are releasing new features and the speed at which they are being tested then you have a problem. Hiding it behind a bug tracking system doesn't help solve this problem.
Objection
4: With multiple people finding bugs you quickly need somewhere to provide you a list of things to work through.
You're doing it wrong.
If you have so many bugs coming in that you need to start tracking, prioritising and scheduling them then this means: (1) your quality control isn't up to scratch and (2) you're releasing it to too many people too quickly. Slow down. Get it right. Stagger the rate of release to keep quality managable.
Objection
5: Interrupting my work like this slows me down too much.
The belief that the best measure of progress is kLOC or function points is simply wrong. There is no point in writing hundreds of lines of code per day if it is all crap.
The fact that this approach slows you down is an intentional feature, not some unfortunate side effect. It helps shift this balance back toward quality.
Objection
6: This is all very well, but in the real world I have deadlines and I don't control the release schedule. This approach can't work for me.
I am aware that everything I have said might sound sanctimonious and unrealistic. When you are not working on your own project then you will most likely be under pressure to add new functionality quickly. But most of the time, if you stop to listen, your company will probably care about quality too.
The real issue comes down to "will we fix these bugs anyway?" If the answer is yes, then I don't think that fixing them immediately will be an unrealistic goal. If the answer is no then just turn them in to stories so that they can be scheduled later if necessary. Tracking bugs doesn't change anything. Bugs fall into either "fix now" or "fix later" categories. If it is the latter, turn them into stories and schedule the fix with everything else.
Writing good quality software and dealing with inevitable bug reports is never going to be easy. The approach that I am trying to adopt relies on two assumptions. First, I am assuming that bugs need to be fixed at some point. Second, I am assuming that you want to build a great product. If either of these assumptions are wrong then bug tracking software is probably going to make you feel better about yourself. I imagine that Microsoft have great bug tracking software. If, however, you'd rather write great quality software then drop everything and fix that bug now. It will make things better in the long run.
20thApr2009
-
Story Smells
Just as there are code smells, so too are there Story Smells. As with code smells, these don't necessarily mean that there is a problem. They do, however, quickly identify stories that you probably want to look at with a critical eye to see if they could be better. Below are some of the Story Smells I have noticed. It's not a complete list, but if you notice any of these kinds of stories I recommend you look at them again and see if it could be rewritten.
No person mentioned in the story
I want to implement scrum
In order improve productivity
I think that the appropriateness of any solution will differ depending on whether this story is written from the viewpoint of a CTO or from the viewpoint of a developer. In any case, if you didn't know who wanted the benefit how would you know who to ask for clarification?
Systems, rather than real people, mentioned in the story
As a billing system
I want to be able to check an order has fulfilled
So that I know when to charge the customer
Where is the benefit to the customer here? Presumably it is not being charged before an order is fulfilled. This is what the customer will care about, not that it is implemented in some specific way.
Benefits which don't match the person mentioned
As a user
I want the config module rewritten
To support the new config file layout
Why would the user care? How should a user prioritise this story? Tear these stories up.
Benefits which don't match the requirement
As a development team leader
I would like to have a story workshop
So that developers are more productive
I can see the connection between having a story workshop and having developers who are more productive, but the two are really independent. What would most likely happen is that the workshop would happen and developers would get better at writing stories, but they would not immediately be more productive. This does not necessarily mean failure, although it could mean failing to satisfy either the benefit or the requirement.
Overly prescriptive requirements
As a user
I would like the password to be SHA1 hashed before being stored in the database
So that it is secure
Would it matter if the password was hashed using SHA256 or MD5 instead? The story leaves no room for alternatives, but the benefit is not dependent on any particular hashing algorithm.
Benefits which include implementation details
As a user
I would like the User model to include the standard security library
So that the password is SHA1 hashed
The implementation is not the same as the benefit. It would be possible to satisfy this story but deliver no benefit to the user.
Benefits which repeat the requirement
As a user
I would like the application to process my request quickly
So that there is no delay
"process ... quickly ... no delay". Why are we doing this story? What is the benefit? Does anyone really understand? Stories like this tend to be too ambiguous. They also suffer from...
Not identifying an achievable benefit
As a user
I would like the application to process my request quickly
So that there is no delay
No delay? What, really no delay? Not even the time it takes to process the request? How can anyone know if this benefit has been provided?
Not identifying a real business need
As a developer
I would like the billing application to be rewritten
Because it is crap
This kind of requirement is fine if you are writing software for its own sake. But if you need to balance developer cost against revenue what is the point in working on this story?
Very long stories
As a person who uses the system
I would like the billing module to make sure that it updates the central accounting system within a 24 hour period, unless it is a weekend in which case it should be within 24 hours of the next working day
So that Sue from accounts can run the report on time on her own and doesn't have to keep coming down here and getting one of us to extract the data for her
Anyone else confused and a little bored? Did anyone even read all of the words?
(This article was originally published on my personal blog in February 2009)
8thApr2009