I realise I’m a bit late to the party, but following the example of JP, psd, Eastmad and Casablanca, I thought I’d have a stab at the 25 words challenge. Not feeling sufficiently inspired to come up with anyhing original and profound, here’s my attempt at boiling the principles of agile software development down into 25 words:
Author: Kerry Buckley
Avoiding Merge Commits in Git
[Update, 18/8/2008] If you’ve shared the branch with anyone else, or are pushing it to a clone of the repository, do not rebase, but use merge instead. From the man page:
When you rebase a branch, you are changing its history in a way that will cause problems for anyone who already has a copy of the branch in their repository and tries to pull updates from you. You should understand the implications of using git rebase on a repository that you share.
Do you get annoyed by seeing things like this in your git history?
commit a0b46a7c57e37f5dc43373ba9167ad2da32c1ec5 Merge: c2d8046... 73e0e15... Author: Fred BloggsDate: Tue Jun 17 17:30:49 2008 +0100 Merge branch 'master' into new_feature commit c2d8046c038d47940944e5b343d281b1d0c4d2b3 Author: Fred Bloggs Date: Tue Jun 17 17:30:43 2008 +0100 Added cool new feature
This happens when you use merge instead of rebase to keep a development branch up-to-date with master. Let’s watch what happens in each case.
The wrong way (merge)
You’re working on a cool new feature in your new_feature branch. You’ve committed two changes, and in the meantime there have been three other changes on master. Here’s the branch visualisation from gitk --all
:
You use merge to pull in those other three changes to your branch:
$ git merge master Merge made by recursive. file_1 | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
Now what this has actually done is added the changes from master as a new commit in new_feature. You can see this in the history:
myproj(new_feature) $ git log -1 commit cbc97a909641d3c325c6023a2459e556e62182e6 Merge: 024dc64... 0a71c4c... Author: Kerry BuckleyDate: Wed Jun 18 18:52:48 2008 +0100 Merge branch 'master' into new_feature
And also in the gitk
graph:
Now let’s say your new feature is all finished, so you merge it into master:
myproj(new_feature) $ git checkout master Switched to branch "master" myproj(master) $ git merge new_feature Updating 0a71c4c..cbc97a9 Fast forward file_3 | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
Let’s see what that’s done to the history:
myproj(master) $ git log commit cbc97a909641d3c325c6023a2459e556e62182e6 Merge: 024dc64... 0a71c4c... Author: Kerry BuckleyDate: Wed Jun 18 18:52:48 2008 +0100 Merge branch 'master' into new_feature commit 0a71c4c90aee5eeb60d15f199c4f8151756a8ae8 Author: Kerry Buckley Date: Wed Jun 18 18:48:19 2008 +0100 Third change in master commit 9970c0b72e7741804fc07bba50450b3d512e5572 Author: Kerry Buckley Date: Wed Jun 18 18:48:10 2008 +0100 Second change in master commit c44af4dee449082adf6741540d2f9e70968cf41e Author: Kerry Buckley Date: Wed Jun 18 18:46:33 2008 +0100 First change in master commit 024dc64022932a5a7b56c4fd7c7cf4a59d72e825 Author: Kerry Buckley Date: Wed Jun 18 18:45:15 2008 +0100 New feature finished commit eb4f05fb8ef8b93cf639b1e06528ef075f19f323 Author: Kerry Buckley Date: Wed Jun 18 18:45:01 2008 +0100 New feature partly done commit b4ffa1d35f808cc38e5f74fb2592224dd6f0e027 Author: Kerry Buckley Date: Wed Jun 18 18:44:02 2008 +0100 Last commit before creating branch
Or graphically:
The problem here is that the merge has applied all of the commits which were on new_feature but not on master, including the merge commit. That’s just ugly.
The right way (rebase)
Now, from exactly the same point, we’ll use rebase instead:
myproj(new_feature) $ git rebase master First, rewinding head to replay your work on top of it... HEAD is now at a644c41 Third change in master Applying New feature partly done Applying New feature finished
Basically, as it says, this has rewound all your changes since the new_feature branch diverged from master, moved the branch point up to the tip of master, then replayed your changes on top. The effect of this is as if you had created the branch from the current latest master, so no separate merge commit is required. Again, gitk --all
can confirm this visually:
Now merge the changes into master exactly as before:
myproj(new_feature) $ git checkout master Switched to branch "master" myproj(master) $ git merge new_feature Updating a644c41..d7d2233 Fast forward file_3 | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
No merge commit in the history this time, and a nice simple graph:
So there you have it. No excuse for polluting your history with merges any more!
[tags]git, merge, rebase, branch[/tags]
I consider myself fortunate that the previous two projects I worked on (the BT Web21C Portal and Mojo) were Rails-based (actually it wan’t just luck in the former case, as I had a part in selecting the framework). I love the expressiveness and flexibility of the Ruby language, the power and relative simplicity of the Rails framework, and the all-round awesomeness of tools like RSpec and Capistrano, and I don’t particularly relish the thought of going back to Java (although I’m told that Spring is much nicer than last time I used it).
At our recent release planning session, I was assigned to a new project, which involves (among other things) exposing a CLI-based configuration interface as a web service. In our initial discussions, the four of us more-or-less agreed on a few initial decisions:
- The exposure should be REST. Fortunately the people developing the upstream system shared this opinion.
- Rails is an ideal framework for RESTful web services.
- Ruby seemed like a good fit for parsing the command responses too.
- Asynchronous behaviour would be handled using queues (probably ActiveMQ).
Since we’d all been working on Rails projects when the new team was formed, we assumed that this wouldn’t be a particularly contentious route to go down, but unfortunately our director/architect/boss didn’t see things quite the same way. He had two main objections:
- Rails may make sense for GUI applications, but why on earth would you use it for a service? All our other [SOAP] services are written in Java.
- At some point the application will need to go into support, and we don’t have support/operations people with Ruby or Rails experience
I think the first point’s easier to address, as I’d argue it’s based on a misunderstanding: Rails isn’t really anything to do with GUIs, but is a framework for creating MVC web applications. Virtually all the heavy lifting Rails takes care of is in the controller and model areas, with the creation of the actual visible GUI being left to the developer to take care of with the usual mix of HTML, CSS and Javascript. The only thing Rails adds is the ability to insert dynamic content using ERB – similar to the role of JSP in Java EE.
A RESTful web service is, to all intents and purposes, the same as a normal web application, but (potentially) without the HTML. All the power that Rails brings to web application development is also harnessed when creating RESTful services.
The second point represents a much more fundamental strategy choice. If the company makes the decision that all development is going to use Java (the language as well as the platform), then we inevitably lose the flexibility to choose what may appear (in a local context) to be the right tool for the job. Personally I think that would be a shortsighted and ill-informed decision: if that were the strategy, we’d presumably all still be developing in C, or COBOL, or Assembler. Or we’d have gone bust. But then I’m not an architect (incidentally, according to Peter Gillard-Moss, that’s reason number 10 why I don’t deserve to be fired), so what do I know?
However, if Ruby is considered an acceptable technology choice for “normal” web applications, we’ll still need people with appropriate skills to support those, so the problem doesn’t go away. I suspect even for a Java specialist, supporting a well-written Rails application with good test coverage is probably easier than supporting some of the spaghetti-coded Java I’ve seen.
Anyway, our arguments obviously weren’t totally unconvincing, because we were given a couple of weeks to show what we could produce before getting a final decision. That time runs out on Monday, so if I’m unnaturally grumpy after that it’ll be because we’ve been told to chuck all our work so far away and start from scratch in Java. Or possibly FORTRAN.
Update, 16 June Well we made our case, and we get to stick with Rails. Celebration all round!
Git quote of the day
From Amy Hoy on Slash7:
If
svn
is your sometimes catankerous but serviceable steed,git
is like a chimera crossed with a unicorn with handy built-in saddle bags plus a sword.
[tags]git, svn, quote, qotd, slash7[/tags]
On being beaten with carrots
Traditional approaches to motivation tend to fall into one of two camps: the carrot (“do this and you’ll be rewarded”) or the stick (“do this or you’ll suffer the consequences”).
I guess I’m fortunate to work for a company (and in a country) where by and large there isn’t a culture of firing people who don’t meet performance targets – instead we have a system where an ever-increasing proportion of people’s pay packet is made up of a performance-related bonus, rather than a fixed salary.
So, how do you go about getting your bonus each quarter? Simple: just meet your agreed targets.
Of course, nothing in a large corporation can ever be that simple, so in practice there’s a complex tiered system of company, business unit, programme, project, team and individual targets, which are combined in a magic spreadsheet to generate how much bonus everyone gets. Each of these targets, and the performance against them, has to be agreed, monitored, quantified, audited and levelled. At each stage politics comes into play. Those that enjoy playing systems try to set targets they know they can achieve. People concentrate on meeting the letter of the objectives, possibly to the detriment of other activities, like helping colleagues or making process improvements.
Now step away from this corporate dystopia for a moment, into the world of Agile Software Development. A world where we value Individuals and interactions over processes and tools, and Responding to change over following a plan. A world where we strive to build projects around motivated individuals, give them the environment and support they need, and trust them to get the job done. Where working software is the primary measure of progress. Where at regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly. Where the self-organising team, accountable directly to it customer or product owner, is responsible for its own delivery and processes.
Now, when agile teams in large organisations are forced to jump through the externally-imposed hoops of objectives, development action plans and post-implementation reviews (which add little visible benefit and take up time that could be spent delivering business value), is it any wonder that the carrot of performance-related pay feels like it’s more of a punishment than an incentive?
[tags]agile,performance management,enterprise,corporate,hr[/tags]
Public domain photo from Wikimedia Commons.
Python II: Fibonacci Sequence
Tim’s second exercise (here’s the first):
Write a recursive function that calculates the value of Fibonacci numbers. These are your acceptance criteria:
- Calculating fib(250) must return 7896325826131730509282738943634332893686268675876375
- The function must use recursion. No intermediary data structures, etc.
- The implementation must be written in pure python – no C extension modules, that’s cheating.
- The function must calculate the 250th Fibonacci number in under one second.
You will get extra points if:
- You can also demonstrate a proof for the Reciprocal Fibonacci constant, meeting the following conditions
- Your proof must also run in under one second
- Your proof must not duplicate any of the concerns addressed by your original Fibonacci function implementation.
- Your proof is allowed to call into the Fibonacci function though!
The Reciprocal Fibonacci constant is defined as:
= = 3.35988566…
Where F is a Fibonacci number
Here’s a couple of hints about things to look for:
- Memoization
- Function decorators
I haven’t tried the extra credit section yet, but this is what I came up with. Again, starting with a naive solution with some doctest tests:
#!/usr/bin/env python """ >>> fib(1) 1 >>> fib(2) 1 >>> fib(3) 2 >>> fib(4) 3 >>> fib(5) 5 """ def fib(n): if n <= 2: return 1 else: return fib(n-2) + fib(n-1) def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test()
Well that seems to work OK. Now, try adding a test for the 250th number:
>>> fib(250) 7896325826131730509282738943634332893686268675876375
And wait … and wait …
No, doesn't look like that's going to return any time soon. Of course when you look at what it's doing, the complexity's increasing in the order of 2n or something, which isn't good.
OK, I guess this is where memoisation comes in. After much head-scratching on the train this morning, I came up with this:
fibs = {} def fib(n): if n <= 2: return 1 elif fibs.has_key(n): return fibs[n] else: fibs[n] = fib(n-1) + fib(n-2) return fibs[n]
A little tweak to allow it to be called from the command line with the value of n passed as an argument, and here's the final version:
#!/usr/bin/env python import sys fibs = {} def fib(n): if n <= 2: return 1 elif fibs.has_key(n): return fibs[n] else: fibs[n] = fib(n-1) + fib(n-2) return fibs[n] def _test(): import doctest doctest.testmod() if len(sys.argv) > 1: print fib(int(sys.argv[1])) if __name__ == "__main__": _test()
fib(master) $ time ./fib.py 250 7896325826131730509282738943634332893686268675876375 real 0m0.280s user 0m0.161s sys 0m0.047s
Sorted!
[tags]python, web21c, fibonacci[/tags]
First stab at Python
“It is an ex parrot. It has ceased to be.”
No, of course not. I’m talking about the other Python. Not the one with the silly walks, dead parrot and singing lumberjacks, but the one with the idiosyncratic approach to whitespace.
One of the APIs available for the Web21C SDK is Python, and there was recently some concern that we didn’t have enough expertise in the team to continue providing technical support for the Python SDK. A bunch of us have volunteered to start learning the language, and as a first step, Tim set us some homework:
Write a program that processes a list of numbers from 1 to 100. For each number, if the number is a multiple of 3, print “FIZZ”; if the number is a multiple of 5, print “BANG”; otherwise, print the number.
You are *NOT* allowed to use any *IF/ELSE* statements in your code. You can use the list-accessing ternary operator hack, but whilst I’ll accept your homework if you do, you’ll miss out on the prize (alcoholic), which goes to the most concise code (not including whitespace).
Now I have no idea what this mysterious ‘list-accessing ternary operator hack’ might be, but it sounds painful (as does missing out on an alcoholic prize), so it looks like I need to eschew ifs completely.
Since I’ve never written a line of Python in my life, I decided the easiest way to proceed would be to start off by figuring out enough syntax to solve the problem as defined by the first paragraph, then write a test which the simple code passes, then factor out the conditional logic. Normally I’d start with the test, but I think learning a new language is one of the cases where TDD isn’t really appropriate.
So here’s my first working code:
for n in range(1, 100): if n % 3 == 0: if n % 5 == 0: print 'FIZZBANG' else: print 'FIZZ' elif n % 5 == 0: print 'BANG' else: print n
This prints out what you would expect:
PyMate r8111 running Python 2.5.1 (/usr/bin/env python) >>> fizzbang.py 1 2 FIZZ 4 BANG FIZZ 7 8 FIZZ BANG 11 FIZZ 13 14 FIZZBANG 16 ...
OK, so far so good. The next step was to find out what the Pythonistas use for unit testing. As a card-carrying BDD and RSpec fan, I was initially interested to see that there’s a PySpec, but at first glance it doesn’t look that great (maybe it’s just that I’m not used to reading Python code). Then I came across the bundled DocTest, and while I’m not entirely convinced by its description on the Wikipedia BDD page as ‘BDD for Python’, it looked ideal for the task at hand.
Here’s the same code, but with the addition of a parameter to specify the number to count up to, and a DocTest test:
#!/usr/bin/env python """ Print numbers up to limit, replacing those divisible by 3 and/or 5 with FIZZ and/or BANG. >>> fizzbang(20) 1 2 FIZZ 4 BANG FIZZ 7 8 FIZZ BANG 11 FIZZ 13 14 FIZZBANG 16 17 FIZZ 19 BANG """ def fizzbang(limit): for n in range(1, limit + 1): if n % 3 == 0: if n % 5 == 0: print 'FIZZBANG' else: print 'FIZZ' elif n % 5 == 0: print 'BANG' else: print n def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test()
OK, now to start thinking about the hard bit – getting rid of those ifs. As a first step, I got rid of the nested logic by appending the ‘FIZZ’ and/or ‘BANG’ to a temporary string, then either printing that string, or the original number if it was empty:
def fizzbang(limit): for n in range(1, limit + 1): out = '' if n % 3 == 0: out += 'FIZZ' if n % 5 == 0: out += 'BANG' print out or n
At this point, I hit on the idea of using arrays to select either an empty string or the appropriate word:
def fizzbang(limit): for n in range(0, limit): print ['', '', 'FIZZ'][n%3] + ['', '', '', '', 'BANG'][n%5] or n + 1
Now all that’s left is to remove the limit parameter so the code merely satisfies the original criteria:
for n in range(100): print ['', '', 'FIZZ'][n%3] + ['', '', '', '', 'BANG'][n%5] or n + 1
I expect there are other more concise (and no doubt more efficient) solutions – I look forward to seeing what the other ‘pupils’ come up with!
[tags]python,web21c,fizzbang,fizzbuzz[/tags]
Here’s mine:
~ $ history|awk '{a[$2]++} END{for(i in a){printf "%5d\t%s\n",a[i],i}}'|sort -rn|head 232 git 82 cd 30 ls 20 sudo 18 rm 13 ./script/server 12 mate 11 rake 7 vi 7 ssh
Probably slightly skewed by the fact that I was doing a git demo yesterday.
Meme via Simon Brunning.
Some time last year I was interviewed by Tom Hoffman for Infoworld, as part of a piece he was writing on BT’s (ongoing) transition to an agile software development model. Here’s the article:
Domain Problems
As you may have noticed, kerrybuckley.com has been down for a few weeks now. This was mainly due to incompetence and laziness on my part causing the registration to expire (the hosting’s still fine), but so far the registrar has yet to respond to e-mails and messages on their 75p/min voicemail service.
For now (as you’ve obviously discovered) you can find me at kerry.ontoa.st instead (temporary feed links over on the right, assuming you’re not reading this in an RSS reader, in which case I guess you’d already figured them out). Please pass the word to anyone who might have noticed I was off-air.