<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-10846234</id><updated>2011-07-07T21:03:19.274+01:00</updated><category term='processes'/><category term='Principles'/><category term='cvs'/><category term='P-dd'/><category term='Windows'/><category term='Oracle'/><category term='Javascript'/><category term='Open Source'/><title type='text'>BOBABLOG: Oracle, PHP and Extreme Programming</title><subtitle type='html'>10 Years Oracle and counting.  4 years XP and laughing!
A view on eXtreme Programming, PHP with Oracle and software rollout that actually works</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default?start-index=101&amp;max-results=100'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>137</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-10846234.post-984883163840075002</id><published>2010-05-17T08:47:00.003+01:00</published><updated>2010-05-17T08:52:28.348+01:00</updated><title type='text'>Pleasing line</title><content type='html'>Gotta admit, I'm quite pleased with this line from my new ORM object based database connection library...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;$oFilter = Filter::attribute('player_id')-&gt;isEqualTo('1')-&gt;andAttribute('fixture_id')-&gt;isEqualTo('2');&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-984883163840075002?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/984883163840075002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=984883163840075002' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/984883163840075002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/984883163840075002'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2010/05/pleasing-line.html' title='Pleasing line'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2085328185754978468</id><published>2008-06-24T19:05:00.002+01:00</published><updated>2008-06-24T19:13:13.000+01:00</updated><title type='text'>Sainsburys reward carrier bag re-use!</title><content type='html'>For every carrier bag you re-use they give you a whopping 1 (yep a whole ONE) Nectar point.&lt;br /&gt;&lt;br /&gt;"QI: The Book of General Ignorance" is currently on offer at only 2,400 points, and using Amazon as a guide on its retail price (£4.76), that means each point is worth a staggerring 0.2p &lt;br /&gt;&lt;br /&gt;Oh, and you have to actually use a carrier bag to qualify.  Not using any carrier bags at all means you get nothing.&lt;br /&gt;&lt;br /&gt;Way to go Sainsburys, that global warming's going to be stopped in its tracks now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-2085328185754978468?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2085328185754978468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=2085328185754978468' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2085328185754978468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2085328185754978468'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/06/sainsburys-reward-carrier-bag-re-use.html' title='Sainsburys reward carrier bag re-use!'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3871277377538074584</id><published>2008-06-23T10:01:00.003+01:00</published><updated>2008-12-08T23:37:22.364Z</updated><title type='text'>The Happiness Meter</title><content type='html'>As part of any iteration review / planning meeting there should be a section where everybody involved talks about how they felt the last iteration went, what they thought stood in the way, what they though went particularly well and suchlike.&lt;br /&gt;&lt;br /&gt;We find that as the project goes on, and the team gets more and more used to each other, this tends to pretty much always dissolve into everyone going "alright I suppose", "yeah fine".&lt;br /&gt;&lt;br /&gt;Obviously, this isn't ideal and will tend to mean that you only uncover problems in the project when they've got pretty serious and nerves are pretty frayed.&lt;br /&gt;&lt;br /&gt;This is where "The Happiness Meter" comes in.&lt;br /&gt;&lt;br /&gt;Instead of asking the team if they think things are going OK and having most people respond non-committally, ask people to put a value against how happy they are with the last iteration's progress.  Any range of values is fine, just as long as it has enough levels in it to track subtle movements. I'd go with 1-10.&lt;br /&gt;&lt;br /&gt;You don't need strict definitions for each level, it's enough to say '1 is completely unacceptable, 5 is kinda OK, 10 is absolute perfection'.&lt;br /&gt;&lt;br /&gt;At some point in the meeting, everyone in the team declares their level of happiness.  When I say everyone, I mean everyone: developers, customers, XP coaches, infrastructure guys, project managers, technical authors, absolutely everyone who is valuable enough to have at the iteration review meeting should get a say.&lt;br /&gt;&lt;br /&gt;In order to ensure that everyone gets to provide their own thought, each person writes down their number and everyone presents it at the same time.  The numbers are then taken recorded and a graph is drawn.&lt;br /&gt;&lt;br /&gt;From the graph we should be able to see:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The overall level of happiness at the progress of the project.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If there is any splits / factions in the interpretation of the progress.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3uPATFRVors/SF9nf7WoqHI/AAAAAAAACe4/Sz43gHqXKtk/s1600-h/HappinessMeter.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_3uPATFRVors/SF9nf7WoqHI/AAAAAAAACe4/Sz43gHqXKtk/s320/HappinessMeter.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5215000691724560498" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If the level of happiness is low, this should be investigated; if there are any splits, this should be investigated; and just as importantly - if there are any highs, this should be investigated.  It's good to know why things go well so you can duplicate it over the next iteration.&lt;br /&gt;&lt;br /&gt;Factions tend to indicate that one part of the team has more power than the rest and the project is skewed into their interests rather than those of the team as a whole.&lt;br /&gt;&lt;br /&gt;You may want to split the graph into different teams (customer / developer) if you felt that was important, but I like to think of us all as one team on the same side...&lt;br /&gt;&lt;br /&gt;All said and done, the graph isn't the important bit - the discussion that comes after the ballot is the crucial aspect.  This should be a mechanism for getting people to talk openly about the progress of the project.&lt;br /&gt;&lt;br /&gt;UPDATE: Someone at work suggested a new name that I thought I should share: The Happy-O-Meter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-3871277377538074584?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3871277377538074584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=3871277377538074584' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3871277377538074584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3871277377538074584'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/06/happiness-meter.html' title='The Happiness Meter'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_3uPATFRVors/SF9nf7WoqHI/AAAAAAAACe4/Sz43gHqXKtk/s72-c/HappinessMeter.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-6215957321582561979</id><published>2008-06-21T10:49:00.003+01:00</published><updated>2008-06-21T11:03:35.811+01:00</updated><title type='text'>Ideas for improving innovation and creativity in an IS department</title><content type='html'>At our work we've set up a few 'action teams' to try to improve particular aspects of our working environment.&lt;br /&gt;&lt;br /&gt;The team that I'm a member of is responsible for 'Innovation and Creativity'.&lt;br /&gt;&lt;br /&gt;We're tasked with answering the question "How do we improve innovation and creativity in IS?" - How we can foster an environment that encourages innovation rather than stifles it.&lt;br /&gt;&lt;br /&gt;As a bit of a background, the company is a a medium sized (2,500 plus employees) based mainly in the UK, but recently spreading through the world, the vast majority of whom are not IS based.  The IS department is about 100 strong and includes a development team of 25 people.  It's an SME at the point where it's starting to break into the big-time and recognises that it needs to refine its working practices a little in order to keep up with the pace of expansion.&lt;br /&gt;&lt;br /&gt;We met early last week and have put together a proposal to be taken to the senior management tier.  I get a feeling it will be implemented since our team included the IS Director (you don't get any senior in our department), but you never know what'll happen.&lt;br /&gt;&lt;br /&gt;I figured it might be interesting to record my understanding of the plan as it stands now, and then take another look in 6 months time to see what's happened to it...&lt;br /&gt;&lt;br /&gt;We decided that in order to have an environment that fosters creativity and innovation you need:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Freedom:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Time for ideas for form, for you to explore them, and then to put them into practice.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Stimulus:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Outside influences that that can help to spark those ideas off - this may be from outside the organisation, or through cross-pollination within it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Courage:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The conviction to try things, to allow them to fail or succeed on their own merit - both on the part of the individual and the organisation as a whole.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Natural Selection:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The need to recognise success when it happens, to take it into the normal operation of the business and make it work in practice.  Also, the need to recognise failure when it happens, and stop that from going into (or continuing to exist within) the team.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Recognition:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When we have a good idea, the people involved need to be celebrated.  When we have a bad idea, the people involved DO NOT need to be ridiculed.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Refinement:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The initial ideas aren't always the ones that are successful, it's the 4th, 5th or 125th refinement of that idea that forms the breakthrough.  We need to understand what we've tried, and recognise how and why each idea has failed or succeeded so we can learn from that.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;We put together some concrete ideas on how we're going to help put these in place - and bear in mind that this isn't just for the development team, this is for the whole of the IS department - development, project management, infrastructure, operations, service-desk, even the technology procurement...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Curriculum:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A position set up that will be responsible for defining / tracking a curriculum for each job role in the department.&lt;br /&gt;&lt;br /&gt;Obviously this will be fed by those people that currently fulfil the roles, and will involve things ranging from ensuring the process documentation is up to scratch, through specifying reading lists (and organising the purchasing of the books for the staff) and suggesting / collecting / booking conferences, training courses and the like that might be of use.&lt;br /&gt;&lt;br /&gt;This takes the burden of responsibility away from the staff and managers - all you need is the idea and someone else will organise it and ensure it's on the curriculum for everyone else to follow up.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IdeaSpace (TM ;-) ):&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;A forum for the discussion of ideas, and collection of any documentation produced on those ideas and their investigation.  This will (hopefully) form a library of past investigations as well as a stimulus for future ones.  Everyone in the department will be subscribed to it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lab days:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Every employee is entitled to 2 days a month outside of their normal job to explore some idea they might have.  That time can be sandbagged to a point, although you can't take more than 4 days in one stint.  Managers have to approve the time in the lab (so that it can be planned into existing projects) and can defer the time to some extent, but if requests are forthcoming they have to allow at least 5 days each rolling quarter so that the time can't be deferred indefinitely.&lt;br /&gt;&lt;br /&gt;Whilst the exact format of the lab is yet to be decided, we're aiming to provide space away from the normal desks so that their is a clear separation from the day job and lab time.  People will be encouraged to take time in the lab as a team as well as individually.  Also, if we go into the lab for 3 days to find that an idea doesn't work, that idea should still be documented and the lab time regarded as a success (we learnt something)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Dragon's Den:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Gotta admit, I'm not sure about some of the connotations of this - but the basic idea is sound.  Coming out of time in the Lab should be a discussion with peers about the conclusion of the investigation in a Dragon's Den format.  This allows the wider community to discuss the suitability of the idea for future investigations, or even immediate applicability.  One output of this meeting may be the formalisation of conclusions in the IdeaSpace.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Press Releases:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The company is already pretty good at this, but when something changes for the better we will ensure that we celebrate those changes and, even for a day, put some people up on pedestals.&lt;br /&gt;&lt;br /&gt;None of the above should be seen as a replacement for just trying things in our day to day job - but the idea is that these things should help stress to the department that change and progress are important aspects of what we do, and that we value it enough to provide a structure in which big ideas can been allowed to gestate.  Cross pollination and communication should just form part of our normal day job anyway, and we should ensure that our project teams are cohesive and communicate freely amongst and between themselves.&lt;br /&gt;&lt;br /&gt;Also, an important factor in the success of the above has to be the format of the Dragon's Den - if it is in any way imposing or nerve-racking then the idea is doomed to failure. As soon as people feel under pressure to justify themselves then the freedom disappears.&lt;br /&gt;&lt;br /&gt;I'm quite excited by the prospect of putting these ideas into practice, and I wonder exactly where we'll end up.&lt;br /&gt;&lt;br /&gt;I'll keep you all posted.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-6215957321582561979?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/6215957321582561979/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=6215957321582561979' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6215957321582561979'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6215957321582561979'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/06/ideas-for-improving-innovation-and.html' title='Ideas for improving innovation and creativity in an IS department'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3219144731365136184</id><published>2008-04-01T21:25:00.001+01:00</published><updated>2008-04-01T21:27:06.472+01:00</updated><title type='text'>An apology to IE users</title><content type='html'>To all those visitors who've visited the site with IE over the last 6 months and had a borken layout.&lt;br /&gt;&lt;br /&gt;All apologies... I had no idea that long titles in the Orablogs digest was breaking things.  The offending feed has been removed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-3219144731365136184?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3219144731365136184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=3219144731365136184' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3219144731365136184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3219144731365136184'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/04/apology-to-ie-users.html' title='An apology to IE users'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-5651099907874377705</id><published>2008-03-29T12:35:00.003Z</published><updated>2008-04-22T09:17:09.839+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Principles'/><title type='text'>Things I believe in</title><content type='html'>&lt;ul&gt;&lt;li&gt;It's easier to re-build a system from its tests than to re-build the tests from their system.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can measure code complexity, adherence to standards and test coverage; you can't measure quality of design.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Formal and flexible are not mutually exclusive.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The tests should pass, first time, every time (unless you're changing them or the code).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Flexing your Right BICEP is a sure-fire way to quality tests.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Test code is production code and it deserves the same level of care.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Prototypes should always be thrown away.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Documentation is good, self documenting code is better, code that doesn't need documentation is best.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you're getting bogged down in the process then the process is wrong.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Agility without structure is just hacking.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pairing allows good practices to spread.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pairing allows bad practices to spread.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Cycling the pairs every day is hard work.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Team leaders should be inside the team, not outside it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Project Managers are there to facilitate the practice of developing software, not to control it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Your customers are not idiots; they always know their business far better than you ever will.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A long list of referrals for a piece of software does not increase the chances of it being right for you, and shouldn't be considered when evaluating it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can't solve a problem until you know what the problem is.  You can't answer a question until the question's been asked.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Software development is not complex by accident, it's complex by essence.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Always is never right, and never is always wrong.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Interesting is not the same as useful.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Clever is not the same as right.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The simplest thing that will work is not always the same as the easiest thing that will work.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It's easier to make readable code correct than it is to make clever code readable.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you can't read your tests, then you can't read your documentation.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There's no better specification document than the customer's voice.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can't make your brain bigger, so make your code simpler.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Sometimes multiple exit points are OK.  The same is not true of multiple entry points.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Collective responsibility means that everyone involved is individually responsible for everything.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Sometimes it's complex because it needs to be; but you should never be afraid to check.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If every time you step forward you get shot down you're fighting for the wrong army.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you're always learning you're never bored.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There are no such things as "Best Practices".  Every practice can be improved upon.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Nothing is exempt from testing.  Not even database upgrades.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It's not enough to collect data, you need to analyse, understand and act upon that data once you have it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A long code freeze means a broken process.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A test hasn't passed until it has failed.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you give someone a job, you can't guarantee they'll do it well; If you give someone two jobs you can guarantee they'll do both badly&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Every meeting should start with a statement on its purpose and context, even if everyone in the meeting already knows.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-5651099907874377705?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/5651099907874377705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=5651099907874377705' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5651099907874377705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5651099907874377705'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/03/things-i-believe-in.html' title='Things I believe in'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3527314385605883422</id><published>2008-03-25T17:54:00.006Z</published><updated>2008-03-29T12:38:27.863Z</updated><title type='text'>A reading list for our developers</title><content type='html'>An idea I'm thinking of trying to get implemented at our place is a required reading list for all our developers.  A collection of books that will improve the way that developers think about their code, and they ways in which they solve problems.  The company would buy the books as gifts to the employees, maybe one or two every three months.&lt;br /&gt;&lt;br /&gt;Some questions though:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Is it fair for a company to expect its employees to read educational material out of hours?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Conversely:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Is it fair for an employee to expect to be moved forward in their career without a little bit of personal development outside the office?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;If anyone has any books out there that they'd recommend - please let me know.  Otherwise, here's my initial ideas - the first three would be in your welcome pack:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt;Gary Myers came up with a good point, being that any book should really be readable on public transport.  That probably rules out Code Complete (although I read it on the tube, I can see that it's a little tricky), but Design Patterns and Refactoring to Patterns are small enough I reckon.&lt;br /&gt;&lt;br /&gt;Unfortunately, Code Complete is a &lt;i&gt;really&lt;/i&gt; good book that gives a lot of great, simple, valuable advice.  Does anyone out there have any other suggestions for similar books?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 2:&lt;/b&gt;Andy Beacock reminded me of Fowler's Refactoring, which really should also make the list.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 3:&lt;/b&gt;The development team have bought into the idea and the boss has been asked.  In fact, I'm pretty pleased with the enthusiasm shown by the team for the idea.  I can't see the boss turning it down.  Interestingly though, someone suggested that Code Complete go onto the list...&lt;br /&gt;&lt;br /&gt;In this order:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0321278658/203-1487708-4221519" target="_BLANK"&gt;Extreme Programming Explained - Kent Beck&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/020161622X/203-1487708-4221519" target="_BLANK"&gt;The Pragmatic Programmer - Andy Hunt &amp; Dave Thomas&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0974514012/203-1487708-4221519" target="_BLANK"&gt;Pragmatic Unit Testing using JUnit - Andy Hunt &amp; Dave Thomas&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0321205685/203-1487708-4221519" target="_BLANK"&gt;User Stories Applied - Mike Cohn&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0201741172/203-1487708-4221519" target="_BLANK"&gt;Software Configuration Management Patterns - Steve Berczuk&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.co.uk/Refactoring-Improving-Design-Existing-Technology/dp/0201485672/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1206634040&amp;sr=8-1" target="_BLANK"&gt;Refactoring: Improving the Design of Existing Code - Martin Fowler&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.co.uk/Refactoring-Patterns-Addison-Wesley-Signature-Kerievsky/dp/0321213351/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468874&amp;sr=1-1" target="_BLANK"&gt;Refactoring to Patterns - Joshua Kerievsky&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.co.uk/Design-patterns-elements-reusable-object-oriented/dp/0201633612/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468905&amp;sr=1-1" target="_BLANK"&gt;Design patterns : Elements of Reusable Object-Oriented Software - Gamma, Helm, Johnson, and Vlissides&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.co.uk/Mythical-Month-Essays-Software-Engineering/dp/0201835959/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468972&amp;sr=1-1" target="_BLANK"&gt;The Mythical Man Month and Other Essays on Software Engineering - Frederick P. Brooks&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Ruled out because of their size:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.co.uk/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468835&amp;sr=8-1" target="_BLANK"&gt;Code Complete - Steve McConnell&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.amazon.co.uk/Head-First-Design-Patterns/dp/0596007124/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1206468940&amp;sr=1-1" target="_BLANK"&gt;Head First Design Patterns - Freemen(s), Bates and Sierra&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-3527314385605883422?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3527314385605883422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=3527314385605883422' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3527314385605883422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3527314385605883422'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2008/03/reading-list-for-our-developers.html' title='A reading list for our developers'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3926374224924118442</id><published>2007-09-04T15:54:00.000+01:00</published><updated>2007-09-04T16:08:23.491+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='processes'/><category scheme='http://www.blogger.com/atom/ns#' term='Principles'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>Database Build Script "Greatest Hits"</title><content type='html'>I know its been a quiet time on this blog for a while now, but I've noticed that I'm still getting visitors looking up old blog posts.  It's especially true of the posts that relate to "The Patch Runner".  Many of them come through a link from &lt;a href="http://www.oratransplant.nl/" target="_BLANK"&gt;Wilfred van der Deijl&lt;/a&gt;, mainly his great post of "&lt;a href="http://www.oratransplant.nl/2005/08/03/version-control-of-database-objects" target="_BLANK"&gt;Version control of Database Objects&lt;/a&gt;".&lt;br /&gt;&lt;br /&gt;The patch runner is my grand idea for a version controlled database build script that you can use to give your developers sandbox databases to play with as well as ensuring that your live database upgrades work first time, every time.&lt;br /&gt;&lt;br /&gt;It's all still working perfectly here, and people still seem to be interested, so with that in mind I've decided to collate them a little bit.  Basically provide an index of all the posts I've made over the years that directly relate to database build scripts, sandboxes and version control.&lt;br /&gt;&lt;br /&gt;So, Rob's database build script 'Greatest Hits':&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/06/back-to-oracle_02.html" target="_BLANK"&gt;On the basic idea of building a database in an agile manner&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner.html"&gt;On how you might start putting together a version controlled database build script&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner-rollbacks.html" target="_BLANK"&gt;On how you can organise patches to reduce the pain of a rolling back changes&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner-table-centric.html" target="_BLANK"&gt;On the arguments between table-centric and patch-centric database build scripts&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-upgrade-scripts-why-all.html" target="_BLANK"&gt;On what automated build scripts give you&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/09/database-patch-runner-design-by.html" target="_BLANK"&gt;On having the build script be able to tell when something goes wrong, and stop&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2006/01/developing-environment.html" target="_BLANK"&gt;On how you can use your automated builds to give you a sandbox&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2006/01/we-need-space-to-develop.html" target="_BLANK"&gt;On why your DBAs shouldn't burst into tears when you tell them you need a database for each developer&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2006/07/version-control-and-patch-runner.html" target="_BLANK"&gt;On how you database build script can work with you version control tool&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;As well as: &lt;a href="http://robertbaillie.blogspot.com/2006/03/uk-oug-and-running.html" target="_BLANK"&gt;a presentation given to the UK OUG&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;All of the posts describe processes and patch runners that are very similar to those that I use in my work every day.  I started playing with these theories over 3 years ago now and there is no way I'd go back to implement database upgrades the way I did before.&lt;br /&gt;&lt;br /&gt;However, I'd LOVE to hear ideas on how things can be improved.  I'd be amazed if my three year old thinking was still up to date!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/oracle," rel="tag"&gt;oracle,&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/rdbms," rel="tag"&gt;rdbms,&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software," rel="tag"&gt;software,&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development," rel="tag"&gt;development,&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrades," rel="tag"&gt;upgrades,&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-3926374224924118442?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3926374224924118442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=3926374224924118442' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3926374224924118442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3926374224924118442'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/09/database-build-script-greatest-hits.html' title='Database Build Script &quot;Greatest Hits&quot;'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-211813710969096364</id><published>2007-08-20T16:52:00.000+01:00</published><updated>2007-08-20T16:54:44.354+01:00</updated><title type='text'>Wow</title><content type='html'>And you think software patents are bad...&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.msnbc.msn.com/id/20227400/site/newsweek/" target="_BLANK"&gt;China Regulates Buddhist Reincarnation&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-211813710969096364?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/211813710969096364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=211813710969096364' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/211813710969096364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/211813710969096364'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/08/wow.html' title='Wow'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-841467377530994339</id><published>2007-07-20T16:58:00.000+01:00</published><updated>2007-07-20T17:30:41.756+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cvs'/><title type='text'>Problems with CVS removes?</title><content type='html'>&lt;b&gt;Accidently removed a file in CVS that you want to keep?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Sounds like a stupid question, because when you know the answer to this problem it just seems blindingly obvious, &lt;br /&gt;but what if you've issued a 'remove' against a file in CVS and before you commit the remove you decided that you &lt;br /&gt;made a mistake and still want to keep it?&lt;br /&gt;&lt;br /&gt;I.E you issued (for example)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs remove -f sheep.php&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But not issued&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs commit -m removed sheep.php&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I've heard work arounds such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Edit the "entries" file in the relevant CVS directory in your workspace, removing the reference to the file.&lt;br /&gt;This makes the file appear unknown to CVS.&lt;li&gt;Perform an update in that directory.  This gets the repository version of the file and updates the "entries"&lt;br /&gt;file correctly&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;All you actually need to do is re-add the file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs add sheep.php&lt;br /&gt;&lt;br /&gt; U sheep.php&lt;br /&gt; cvs server: sheep.php, version 1.6, resurrected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When used in this way, the add command will issue an update against the file and retrieve the repository version of the file.&lt;br /&gt;&lt;br /&gt;A word of warning though, if you had uncommitted changes in that file before you issued a remove, CVS isn't going to recover that for you...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How about if you've removed a file, but your version of the file is out of date and so you can't commit it?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;So you've issued the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs remove -f sheep.txt &lt;br /&gt;&lt;br /&gt; cvs server: scheduling 'sheep.txt' for removal&lt;br /&gt; cvs server: use 'cvs sheep' to remove this file permanently&lt;br /&gt;&lt;br /&gt; &gt; cvs commit -m removed sheep.txt &lt;br /&gt;&lt;br /&gt; cvs server: Up-to-date check failed for 'sheep.txt'&lt;br /&gt; cvs server: correct above errors first!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can't issue an update because you get the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs update sheep.txt&lt;br /&gt;&lt;br /&gt;  cvs server: conflict: removed sheep.txt was modified by second party&lt;br /&gt;  C rob_tmp.txt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Again, add the file.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &gt; cvs add sheep.php&lt;br /&gt;&lt;br /&gt; U sheep.php&lt;br /&gt; cvs server: sheep.php, version 1.6, resurrected&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This gets you the most up to date version from the repository, that you can then check for changes (you wouldn't want to just remove it now that someone's added new content would you?)&lt;br /&gt;&lt;br /&gt;Once you've convinced yourself that it's still a good idea to delete it, just issue the remove and commit.&lt;br /&gt;&lt;br /&gt;Simple when you know how!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-841467377530994339?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/841467377530994339/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=841467377530994339' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/841467377530994339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/841467377530994339'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/07/problems-with-cvs-removes.html' title='Problems with CVS removes?'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2685212307780651303</id><published>2007-07-12T14:15:00.000+01:00</published><updated>2007-07-12T15:12:20.604+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>Can a change in execution plan change the results?</title><content type='html'>We've been using Oracle Domain indexes for a while now in order to search documents to get back a ranked order of things that meet certain criteria.&lt;br /&gt;&lt;br /&gt;The documents are releated to people, and we augment the basic text search with other filters and score metrics based on the 'people' side of things to get an overall 'suitability' score for the results in a search.&lt;br /&gt;&lt;br /&gt;Without giving too much away about the business I work with I can't really tell you much more about the product than that, but it's probably enough of a background for this little gem.&lt;br /&gt;&lt;br /&gt;We've known for a while that the domain index 'score' returned from a 'contains' clause is based not only on the document to which that score relates, but also on the rest of the set that is searched.  An individual document score does not live in isolation, rather in lives in the context of the whole result set.&lt;br /&gt;&lt;br /&gt;No problem.  As I say, we've known this for a while and so have our customers.  Quite a while ago they stopped asking what the numbers mean and learned to trust them.&lt;br /&gt;&lt;br /&gt;However, today we realised something.  Since the results are affected by the result set that is searched, this means that the results can be affected by the order in which the optimizer decides to execute a query.&lt;br /&gt;&lt;br /&gt;I can't give you a full end to end example, but I can assure you that the following is most definately the case on one of our production domain indexes (names changed, obviously):&lt;br /&gt;&lt;br /&gt;We have a two column table 'document_index', which contains 'id' and 'document_contents'.  Both columns have an index.  The ID being the primary key and the other being a domain index.&lt;br /&gt;&lt;br /&gt;The following SQL gives the related execution path:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT id, SCORE( 1 )&lt;br /&gt;FROM   document_index&lt;br /&gt;WHERE  CONTAINS( document_contents, :1, 1 ) &gt; 0&lt;br /&gt;AND    id = :2&lt;br /&gt;&lt;br /&gt;SELECT STATEMENT&lt;br /&gt;  TABLE ACCESS BY INDEX ROWID SCOTT.DOCUMENT_INDEX&lt;br /&gt;    DOMAIN INDEX SCOTT.DOCUMENT_INDEX_IDX01&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;However, the alternative SQL gives this execution path:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT id, SCORE( 1 )&lt;br /&gt;FROM   document_index&lt;br /&gt;WHERE  CONTAINS( document_contents, 'Some text', 1 ) &gt; 0&lt;br /&gt;AND    id = :2&lt;br /&gt;&lt;br /&gt;SELECT STATEMENT&lt;br /&gt;  TABLE ACCESS BY INDEX ROWID SCOTT.DOCUMENT_INDEX                       &lt;br /&gt;    INDEX UNIQUE SCAN SCOTT.DOCUMENT_INDEX_PK                         &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Normally, this kind of change in execution path wouldn't be a problem.  But as stated earlier, the result of a score operation against a domain index is not just dependant on the individual records, but the context of the whole result set.  The first execution provides you a score for the single document in the context of the all the documents in the table, the second gives you a score within the context of just that document.  The scores are different.&lt;br /&gt;&lt;br /&gt;Now obviously, this is an extreme example, but more subtle examples will almost certainly exist if you combine the domain index lookups with any other where clause criteria.  This is especially true if you're using literal values instead of bind variables in which case you may find the execution path changing between calls to the 'same' piece of SQL.&lt;br /&gt;&lt;br /&gt;My advice?  Well, we're going to split our domain index look ups from all the rest of the filtering criteria, that way we can prepare the set of documents we want the search to be within and know that the scoring algorithm will be applied consistently.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-2685212307780651303?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2685212307780651303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=2685212307780651303' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2685212307780651303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2685212307780651303'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/07/can-change-in-execution-plan-change.html' title='Can a change in execution plan change the results?'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-6742820872360641131</id><published>2007-07-04T19:29:00.000+01:00</published><updated>2007-07-04T19:41:54.623+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><title type='text'>Handy "Alert Debugging" tool</title><content type='html'>One of the coolest things about OO Javascript is that methods can be written to as if they are variables.  This means that you can re-write functions on the fly.  Bad for writing maintainable code if you're not structured; Fantastic for things like MVC controllers (rather use the controller to forward calls on to the model, you use it to rewire the view so that it calls it directly, and all without the view even realising it!).&lt;br /&gt;&lt;br /&gt;What I didn't realise was that the standard window object (and probably so many others out there) can have its methods overwritten like any other.&lt;br /&gt;&lt;br /&gt;Probably the simplest example of that proves to be incredibly useful... changing the alert function so that the dialog becomes a confirm window.  Clicking cancel means that no further alerts are shown to the user.  Great for when you're writing Javascript without a debugger and have to resort to 'alert debugging'.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;window.alert = function(s) {&lt;br /&gt; if( !confirm(s) ) window.alert = null;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In case you're wondering... I found it embedded in the comments on &lt;a href="http://www.joehewitt.com/blog/firebug_for_iph.php" target="_BLANK"&gt;this post: http://www.joehewitt.com/blog/firebug_for_iph.php&lt;/a&gt;.  Cheers &lt;a href="http://www.mennovanslooten.nl/" target="_BLANK"&gt;Menno van Slooten&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-6742820872360641131?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/6742820872360641131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=6742820872360641131' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6742820872360641131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6742820872360641131'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/07/handy-alert-debugging-tool.html' title='Handy &quot;Alert Debugging&quot; tool'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2944032322899376272</id><published>2007-06-26T20:00:00.000+01:00</published><updated>2007-06-26T20:04:48.671+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><title type='text'>Tab Complete in Windows</title><content type='html'>Another one of those things that I can never remember off the top of my head so find myself constantly looking it up whenever I get access to a new machine.&lt;br /&gt;&lt;br /&gt;I figure it may as well be my own site that I get the info from :-)&lt;br /&gt;&lt;br /&gt;To switch on 'Tab Complete' in Windows command line change the following registry keys to '09':&lt;br /&gt;&lt;ul&gt;&lt;li&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\CompletionChar&lt;/li&gt;&lt;li&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\PathCompletionChar&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-2944032322899376272?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2944032322899376272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=2944032322899376272' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2944032322899376272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2944032322899376272'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/06/tab-complete-in-windows.html' title='Tab Complete in Windows'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-7528429859998673834</id><published>2007-06-22T17:54:00.000+01:00</published><updated>2007-06-22T18:09:52.235+01:00</updated><title type='text'>Haiku</title><content type='html'>Saw a cracking Haiku on a t-shirt the other day:&lt;br /&gt;&lt;br /&gt;Haiku are easy&lt;br /&gt;But sometimes they don't make sense&lt;br /&gt;Refridgerator.&lt;br /&gt;&lt;br /&gt;Then my mind started dwelling on it:&lt;br /&gt;&lt;br /&gt;Got the release out&lt;br /&gt;But the testing's not finished&lt;br /&gt;It's falling over&lt;br /&gt;&lt;br /&gt;Or&lt;br /&gt;&lt;br /&gt;Database is slow&lt;br /&gt;Just can't see what's wrong with it&lt;br /&gt;Set autotrace on&lt;br /&gt;&lt;br /&gt;Or&lt;br /&gt;&lt;br /&gt;A quick refactor&lt;br /&gt;Turns into a bigger job&lt;br /&gt;Should have unit tests&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-7528429859998673834?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/7528429859998673834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=7528429859998673834' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/7528429859998673834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/7528429859998673834'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/06/haiku.html' title='Haiku'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-4163157394928644086</id><published>2007-05-28T17:45:00.000+01:00</published><updated>2007-05-28T18:02:03.995+01:00</updated><title type='text'>Records</title><content type='html'>And to follow on from the last post... my current personal bests:&lt;br /&gt;&lt;br /&gt;I figure if I keep them here, at least I'll always know where they are!&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;5km Run&lt;/td&gt;&lt;td&gt;23:44 (Battersea Park, 'Beat the Baton' 28/05/07)&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;10km Run&lt;/td&gt;&lt;td&gt;53:23 (Hyde Park, 'Run London' 08/10/06)&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Half Marathon&lt;/td&gt;&lt;td&gt;2:17:49 (Redcar, 'Tees Valley Half Marathon' 12/03/06)&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Rubik's cube&lt;/td&gt;&lt;td&gt;57 seconds&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-4163157394928644086?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/4163157394928644086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=4163157394928644086' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/4163157394928644086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/4163157394928644086'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/05/records.html' title='Records'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-6103502008715549084</id><published>2007-05-28T17:12:00.001+01:00</published><updated>2007-05-28T17:44:42.181+01:00</updated><title type='text'>Targets</title><content type='html'>You've gotta have targets.&lt;br /&gt;&lt;br /&gt;The more I try to motivate myself to do things, the more I realise that if I don't have a target it's incredibly difficult.&lt;br /&gt;&lt;br /&gt;When I realised this it came as a big surprise to me.  I'm really not the sort of person to have a 5 year plan or career goals, but it seems that if I don't set myself an only just achievable goal I find it very difficult to motivate myself to do much.&lt;br /&gt;&lt;br /&gt;I keep myself fit so that I get the most out of playing football.  But just having that in mind isn't enough to get me out and running.  If I didn't set myself a target time for a 5km or 10km run and then book a place at a running event, then I'd just sit on my fat arse every night watching TV.  OK, so I may be exaggerating my self deprecation, but you get the idea.&lt;br /&gt;&lt;br /&gt;I find that this affects me in many different aspects of my life.&lt;br /&gt;&lt;br /&gt;To motivate myself to run I set a target (public) 5km or 10km time (this year it's 22:30 and 50:00 respectively).&lt;br /&gt;&lt;br /&gt;To motivate myself to learn to do the Rubik's cube, I set myself a target completion time (1 minute - yup, managed it).&lt;br /&gt;&lt;br /&gt;To motivate myself to save money I set a target amount to reach by a certain date (nope, not telling you how much).&lt;br /&gt;&lt;br /&gt;A friend of mine decided that he'd set himself the target of taking a photo a day for a year and posting it on his site.  I may have to steal that idea next year... but until then you can find his here: &lt;a href="http://www.ysr23.com/blog/" target="_BLANK"&gt;www.ysr23.com/blog&lt;/a&gt;.  It really is damn good.&lt;br /&gt;&lt;br /&gt;I do the job I do because I just flat out enjoy it.  As soon as it becomes too much of a chore I'll move on.  And I reckon I'm doing alright career wise in whatever way you choose to measure it.  For me the only measure that truly counts is enjoyment, and in the main it's a damn fine job.  Well, it is most of the time anyway ;-)&lt;br /&gt;&lt;br /&gt;Someone at work once said to me:  You know, every now and again Tom Cruise probably gets up in the morning, probably on set, in his trailer and thinks to himself "Damn, gotta do some of that acting shit again today".  OK, so he gets paid more in a minute that I do in a year, but you get the point.&lt;br /&gt;&lt;br /&gt;And the big thing that keeps my enjoying my job is that I'm still learning new things.  I suppose I have a clear target in my career to always keep on learning and to surround myself in people who can teach me.  It's probably one of the biggest reasons why I'm so pleased to be working with Extreme Programming.  It makes it easy to fulfill that goal.  And it works on a clear system of easy to understand targets.&lt;br /&gt;&lt;br /&gt;A release to the business has a target set of functionality.&lt;br /&gt;A single story has a clearly defined purpose.&lt;br /&gt;A unit test gives you a goal that must be met, and a clear way of determining the success or failure.&lt;br /&gt;&lt;br /&gt;Layers of targets.&lt;br /&gt;&lt;br /&gt;And if you're doing XP properly you get to celebrate when you meet those targets.&lt;br /&gt;&lt;br /&gt;A brief whoop when the unit test passes.&lt;br /&gt;A handful of jelly beans when the story's complete.&lt;br /&gt;A damn big meal and a piss up when a release hits the business.&lt;br /&gt;&lt;br /&gt;OK, so real life targets don't have quite the same level of celebration, but it's the same deal.&lt;br /&gt;&lt;br /&gt;Set yourself a clear target and you get clarity of purpose in aiming for it, and the celebration when you pass it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-6103502008715549084?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/6103502008715549084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=6103502008715549084' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6103502008715549084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6103502008715549084'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/05/targets.html' title='Targets'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2197870824204963495</id><published>2007-04-11T08:12:00.000+01:00</published><updated>2007-04-11T08:20:35.682+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Principles'/><title type='text'>Roll-out</title><content type='html'>I'm pretty sure that most people that read this blog will also read &lt;a href="http://worsethanfailure.com/" target="_BLANK"&gt;The Daily WTF&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;But just in case you don't, &lt;a href="http://worsethanfailure.com/Articles/Soft_Coding.aspx" target="_BLANK"&gt;there's a nice entry on 'soft-coding'&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Overall the article makes sound sense, but there's a line right at the end that resonates with me, especially since I read it the day after someone told me that they needed a developer for a whole day (9 hours) to roll out their system...&lt;br /&gt;&lt;br /&gt;&lt;div class="quote"&gt;With the myriad of tools available today, there is no reason that your deployment process need be any more complicated than a simple, automated script that retrieves the code from source control, compiles it, copies/installs the executables, and then runs the relevant database scripts.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It makes me feel like I'm not alone.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-2197870824204963495?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2197870824204963495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=2197870824204963495' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2197870824204963495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2197870824204963495'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/04/roll-out.html' title='Roll-out'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-8781024129014645217</id><published>2007-03-23T07:40:00.000Z</published><updated>2007-03-23T07:44:02.913Z</updated><title type='text'>Question</title><content type='html'>I've had this conversation a couple of time with people, and I've realised that I can't get to a satisfactory answer without some research.   And I'm lazy.  So I'm going to pose a question... and if I don't get a satisfactory answer here I might well send it to The New Scientist in the hope that they'll answer it.&lt;br /&gt;&lt;br /&gt;Assuming that the cost of setting up and maintaining the infrastructure is already taken care of, which is more energy efficient: an electric kettle or a stove top kettle?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-8781024129014645217?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/8781024129014645217/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=8781024129014645217' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/8781024129014645217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/8781024129014645217'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/03/question.html' title='Question'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-6282890185642428980</id><published>2007-02-17T12:03:00.000Z</published><updated>2007-03-19T22:27:55.453Z</updated><title type='text'>I am still here...</title><content type='html'>Sorry people, I promise I'm still here and I WILL get round to finishing my text on estimating and answering the request for more info on the database patch runner.  I will, I will, I will!&lt;br /&gt;&lt;br /&gt;The problem is, I've started reading again, and I've started playing on-line poker.  Damn it :-)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0131479415/202-7357113-6378261"&gt;&lt;img src="http://ec2.images-amazon.com/images/P/0131479415.01._SCMZZZZZZZ_SL125_.jpg" align="right"/&gt;&lt;/a&gt;&lt;br /&gt;But I'm enjoying it, especially &lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0131479415/202-7357113-6378261" target="_BLANK"&gt;a Cohn book on Agile Estimation and Planning&lt;/a&gt;.  It is an absolute MUST read.  It takes off where the estimation chapter from &lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0321205685/202-7357113-6378261" target="_BLANK"&gt;User Stories Applied&lt;/a&gt; left off, and it really doesn't dissappoint.&lt;br /&gt;&lt;br /&gt;Unfortunately it seems to say an awful lot that I agree with, and was going to form the bulk of my next couple of posts.  So if you like what I have to say on the topic, then Mike Cohn is definately worth a read... he goes into a lot more detail than I ever will here!&lt;br /&gt;&lt;br /&gt;Obviously I'm reading an awful lot on Texas Hold 'em as well... but I'm not going to tell you what 'cause that might take away my advantage ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-6282890185642428980?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/6282890185642428980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=6282890185642428980' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6282890185642428980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/6282890185642428980'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/02/i-am-still-here.html' title='I am still here...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-1419539135792234802</id><published>2007-01-07T13:52:00.000Z</published><updated>2007-03-19T22:25:51.964Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='processes'/><title type='text'>Producing Estimates</title><content type='html'>OK, so it's about time I got back into writing about software development, rather than software (or running, or travelling, or feeds) and the hot topic for me at the moment is the estimation process.&lt;br /&gt;&lt;br /&gt;This topic's probably a bit big to tackle in a single post, so it's post series time.  Once all the posts are up I'll slap another up with all the text combined.&lt;br /&gt;&lt;br /&gt;So – Producing good medium term estimates...&lt;br /&gt;&lt;br /&gt;&lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0321205685/202-7357113-6378261" target="_BLANK"&gt;&lt;img src="http://ec2.images-amazon.com/images/P/0321205685.01._SCMZZZZZZZ_SL125_.jpg" style align="right"/&gt;&lt;/a&gt;I'm not going to talk about the process for deriving a short term estimate for a small piece of work, that's already covered beautifully by &lt;a href="http://astore.amazon.co.uk/bobablog-21/detail/0321205685/202-7357113-6378261" target="_BLANK"&gt;Mike Cohn in User Stories Applied&lt;/a&gt;, and &lt;a href="http://robertbaillie.blogspot.com/2005/12/valued-estimation.html" target="_BLANK"&gt;I blogged on that topic some time ago&lt;/a&gt;.  Rather I'm going to talk about producing an overall estimate for a release iteration or module.&lt;br /&gt;&lt;br /&gt;I've read an awful lot on this topic over the last couple of years, so I'm sorry if all I'm doing is plagiarising things said by &lt;a href="http://www.amazon.co.uk/Extreme-Programming-Explained-Embrace-Change/dp/0321278658/sr=8-1/qid=1168173084/ref=pd_ka_1/026-3026126-5358804?ie=UTF8&amp;s=books" target="_BLANK"&gt;Kent Beck&lt;/a&gt;, &lt;a href="http://www.amazon.co.uk/User-Stories-Applied-Development-Addison-Wesley/dp/0321205685/sr=11-1/qid=1168173113/ref=sr_11_1/026-3026126-5358804 " target="_BLANK"&gt;Mike Cohn&lt;/a&gt; or &lt;a href="http://www.amazon.co.uk/Planning-Extreme-Programming-Kent-Beck/dp/0201710919/sr=11-1/qid=1168173147/ref=sr_11_1/026-3026126-5358804" target="_BLANK"&gt;Martin Fowler (OK, the book's Kent as well, but you get the point)&lt;/a&gt;, or any of those many people out there that blog, and that I read.  Truly, I'm sorry.&lt;br /&gt;&lt;br /&gt;I'm not intending to infringe copyright or take credit for other people's work, it's just that my thinking has been heavily guided by these people.  This writing is how I feel about estimating, having soaked up those texts and put many of their ideas into practice.&lt;br /&gt;&lt;br /&gt;Many people think that XP is against producing longer term estimates, but it isn't.  It's just that it's not about tying people down to a particular date 2 and a half years in the future and beating them with a 400 page functional design document at the end of it; it's about being able to say with some degree of certainty when some useful software will arrive, the general scope of that software, and ultimately to allow those people in power to be able to predict some kind of cost for the project.&lt;br /&gt;&lt;br /&gt;Any business that allows the development team to go off and do a job without providing some kind of prediction back to the upper management team being irresponsible at best, grossly negligent at worst.&lt;br /&gt;&lt;br /&gt;Providing good medium term estimates is a skill, and a highly desirable one.  By giving good estimates and delivering to them you are effectively managing the expectations of upper management and allowing them to plan the larger scale future of the software in their business.  When they feel they can trust the numbers coming out of the development teams it means they are less likely to throw seemingly arbitrary deadlines at their IT departments and then get angry when they're not met.  Taking responsibility for, and producing good estimates will ultimately allow you to take control of the time-scales within your department.  If you continually provide bad estimates or suggest a level of accuracy that simply isn't there by saying "it'll take 1,203 ½ man days to complete this project", then you've only got yourself to blame when that prediction is used to beat you down when your project isn't complete in exactly 1,203 ½ man days.&lt;br /&gt;&lt;br /&gt;Whilst being written within the scope of XP, there's no reason why these practices won't work when you're not using an agile process.  All you need to do is to split your work into small enough chunks so that each one can be estimated in the region of a few hours up to 5 days work before you start.&lt;br /&gt;&lt;br /&gt;In summary, the process is this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Produce a rough estimate for the whole of the release iteration.&lt;/li&gt;&lt;li&gt;Iteratively produce more detailed estimates for the work most likely to be done over the next couple of weeks.&lt;/li&gt;&lt;li&gt;Feed the detailed estimates into the overall cost.&lt;/li&gt;&lt;li&gt;Do some development.&lt;/li&gt;&lt;li&gt;Feed the actual time taken back into the estimates and revise the overall cost.&lt;/li&gt;&lt;li&gt;Don't bury your head in the sand if things aren't going to plan.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1 – Produce a rough estimate for the whole of the release iteration.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Before you can start work developing a new release you should have some idea of what functionality is going into that release.  You should have a fairly large collection of loosely defined pieces of functionality.  You can't absolutely guarantee that these stories will completely form the release that will go out in 3 months time, but what you can say is: "Today we believe that it would be most profitable for us to work on these areas of functionality in order to deliver a meaningful release to the business within a reasonable time-frame".&lt;br /&gt;&lt;br /&gt;Things may change over the course of the next few months, and the later steps in this process will help you to respond to those changes and provide the relevant feedback to the business.&lt;br /&gt;&lt;br /&gt;Kent Beck states (I paraphrase): You don't drive a car by pointing it at the end of the road and driving in a straight line with your eyes closed.  You stay alert and make corrections moment by moment.&lt;br /&gt;I state: When you get in a car and start to drive you should at least have some idea where you're going.&lt;br /&gt;&lt;br /&gt;The two statements don't oppose each other. &lt;br /&gt;&lt;br /&gt;The basic idea is this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In broad strokes, group the stories in terms of cost in relation to each other.  If X is medium and Y is huge then Z is tiny.&lt;/li&gt;&lt;li&gt;Assign a numbered cost to each group of stories.&lt;/li&gt;&lt;li&gt;Inform that decision with past experience.&lt;br /&gt;Produce a total.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;So, get together the stories that you're putting into this release iteration, grab a room with a big desk, some of your most knowledgeable customers and respected developers and off you go.&lt;br /&gt;&lt;br /&gt;Ideally you want to have around 5 to 8 people in the room, more than this and you'll find you end up arguing over details, less and you may find you don't have enough buy in from the customer and development teams.&lt;br /&gt;&lt;br /&gt;As a golden rule: developers that are not working on the project should NOT be allowed to estimate.  There's nothing worse for developer buy-in than having an estimate or deadline over which they feel that have no control.&lt;br /&gt;&lt;br /&gt;Try to keep the number of stories down, I find around 50 stories is good.  If you have more you may be able to group similar ones together into a bigger story, or you may find you have to cull a few.  That's OK, you can go through this process several times.  You can try to go through the process with more, but I find that you can't keep more than 50 stories in your head at one time.  You're less likely to get the relative costs right.  And it'll get boring.&lt;br /&gt;&lt;br /&gt;Quickly estimate each story:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The customer explains the functionality required without worrying too much about the about the detail;&lt;/li&gt;&lt;li&gt;The developers place each story into one of 4 piles: small, medium, large and wooooaaaah man that's big.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;People shouldn't be afraid to discuss their thinking, though you should be worried if it's taking 10 minutes  to produce an estimate for each story.&lt;br /&gt;&lt;br /&gt;You may find that developers will want to split stories down into smaller ones and drive out the details.  Don't, if you can avoid it.  At this point we want a general idea of the relative size of each story.  We don't need to understand the exact nature of each individual story, we don't need enough to be able to sit down and start developing.  All we need is a good understanding of the general functionality needed and a good idea of the relative cost.  Stories WILL be re-estimated before they're implemented and many of them will be split into several smaller ones to make their development simpler.  But we'll worry about that later.&lt;br /&gt;&lt;br /&gt;As the stories are being added to piles be critical of past decisions.  Allow stories to be moved between piles.  Allow piles to be completely rebuilt.  You may start off with 10 stories that spread across all 4 piles, then the 11th 12th and 13th stories come along and have nowhere to go – they're all an order of magnitude bigger than the ones that have gone before.  That's OK, look at the stories you've already grouped in light of this new knowledge and recalibrate the piles.&lt;br /&gt;&lt;br /&gt;Once all the stories are done, spread the piles out so you can see every story in each one.  Appraise your groupings.  Move stories around.  It's important that you're comfortable that you've got them in the right pile relative to each other.&lt;br /&gt;&lt;br /&gt;If the developers involved in this round of estimating recently worked on another project or a previous release of this project then add some recently completed stories from that work.  Have the developers assign those stories into the piles.  You know how long those stories took and you can use that to help guide your new estimates.  There's nothing more useful in estimating than past experience... don't be afraid to use that knowledge formally.&lt;br /&gt;&lt;br /&gt;Once you're that the piles are accurate, put a number of days against each pile.  The idea is to get 'on average a single story in this pile will take x days'.&lt;br /&gt;&lt;br /&gt;If you have to err, then err on the side of bigger.  Bear in mind that people always estimate in a perfect world where nothing ever goes wrong.  The stories you added from the last project can be used to guide the number.  You know exactly how long it took to develop those stories and you'd expect the new numbers to be similar.&lt;br /&gt;&lt;br /&gt;The resulting base estimate is then just the sum of (number of stories in each pile * cost of that pile).  This number will give you a general estimate on the overall cost.&lt;br /&gt;&lt;br /&gt;You'll probably want to add a contingency, and past experience on other projects will help you guide that.  If you've gone through this process before, pull out the numbers from the last release... when you last performed this process and compare the starting estimate with the actual amount of time taken.  Use that comparison for the contingency.&lt;br /&gt;&lt;br /&gt;The gut feeling is to say, "Hang on, the last time we did this we estimated these 50 stories, but only built these 20 and added these other 30.  That means that the starting estimate was nothing like what we actually did".  But the point is that if every release has the same kind of people working on it, working for the same business with the same kinds of pressures then this release is likely to be the same as the last.  Odds are that THIS release will only consist of 20 of these stories plus 30 other unknown ones... and the effect on the estimate is likely to be similar.  Each release is NOT unique, each release is likely to go ahead in exactly the same way as the last one.  Use that knowledge, and use the numbers you so painstakingly recorded last time.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color:#EEC; border: 1px solid black; padding: 5px" &gt;Aside: Project managers tend to think that I have a problem with them collecting numbers (as they love to do).  They're wrong... I have a problem with numbers being collected and never being used.  The estimation process is the place where this is most apparent.  Learn from what happened yesterday to inform your estimate for tomorrow.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Once the job's done you should have enough information to allow you to have the planning game.  For those not familiar with XP, that is the point at which the customer team (with some advice from the development team) prioritises and orders the stories.  It gives you a very good overall view of the direction of the project and a clear goal for the next few weeks of work.&lt;br /&gt;&lt;br /&gt;At this point you should have a good idea of cost and scope and have a good degree of confidence in both.  Now's the time to tell your boss...&lt;br /&gt;&lt;br /&gt;Next up – using the shorter term estimates to inform the longer term one...&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/XP" rel="tag"&gt;XP&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Extreme+Programming" rel="tag"&gt;Extreme+Programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/programming" rel="tag"&gt;programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/estimating" rel="tag"&gt;estimating&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/user+stories" rel="tag"&gt;user+stories&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software+development" rel="tag"&gt;software+development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-1419539135792234802?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/1419539135792234802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=1419539135792234802' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/1419539135792234802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/1419539135792234802'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/01/producing-estimates.html' title='Producing Estimates'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-5629475470855706657</id><published>2007-01-05T16:49:00.000Z</published><updated>2007-01-05T16:51:47.122Z</updated><title type='text'>New Comments Feed</title><content type='html'>I've just found out that the new version of Blogger comes with a comments RSS feed.&lt;br /&gt;&lt;br /&gt;I've added an auto discovery link to the page, and once I get round to looking for a nice RSS Comments image I'll put a link on the top bar.&lt;br /&gt;&lt;br /&gt;If you can't wait that long, and can't auto discover it, you can find the feed here: &lt;a href="http://robertbaillie.blogspot.com/feeds/comments/default" target="_BLANK"&gt;http://robertbaillie.blogspot.com/feeds/comments/default&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-5629475470855706657?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/5629475470855706657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=5629475470855706657' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5629475470855706657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/5629475470855706657'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/01/new-comments-feed.html' title='New Comments Feed'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-2655046410611750871</id><published>2007-01-05T16:07:00.000Z</published><updated>2007-01-05T16:11:48.137Z</updated><title type='text'>Last P-dd post on BobaBlog</title><content type='html'>OK, so I'm going to stop going on about P-dd now for a bit on this blog.&lt;br /&gt;&lt;br /&gt;It's just to let everyone know that the P-dd blog is properly up and running now, and I'm going to try tit-bits of information on there, as well as the release announcements as code becomes available.&lt;br /&gt;&lt;br /&gt;There's finally an easy to use link to a zip file for the latest version as I've managed to come to terms with the Google Code downloads tab (rather it was case of "oh, there's a 'download' tab, that's handy").&lt;br /&gt;&lt;br /&gt;So, unless something pretty drastic happens in the P-dd world, this'll be the last note on the topic on BobaBlog.&lt;br /&gt;&lt;br /&gt;Promise.&lt;br /&gt;&lt;br /&gt;:-)&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/diagram" rel="tag"&gt;diagram&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dot" rel="tag"&gt;dot&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dotty" rel="tag"&gt;dotty&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/neato" rel="tag"&gt;neato&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/er" rel="tag"&gt;er&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/open+source" rel="tag"&gt;open+source&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Postgres" rel="tag"&gt;Postgres&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/MySql" rel="tag"&gt;MySql&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/documentation" rel="tag"&gt;documentation&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-2655046410611750871?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/2655046410611750871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=2655046410611750871' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2655046410611750871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/2655046410611750871'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2007/01/last-p-dd-post-on-bobablog.html' title='Last P-dd post on BobaBlog'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-1610507427761613811</id><published>2006-12-31T17:20:00.000Z</published><updated>2006-12-31T19:02:55.650Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='P-dd'/><category scheme='http://www.blogger.com/atom/ns#' term='Open Source'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>P-dd 0.1 finally released</title><content type='html'>So, with just 6 1/2 hours to go before the year ends I've finally managed to get P-dd - The PHP Database Documentor up to version 0.1 standard.&lt;br /&gt;&lt;br /&gt;The blog's up and running, a slab of source code sits on Google code and at last I feel like I can stand beside the library and say "yeah, well it's good enough for now".&lt;br /&gt;&lt;br /&gt;You can find the code &lt;a href="http://code.google.com/p/p-dd/" target="_BLANK"&gt;here: http://code.google.com/p/p-dd/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And the blog &lt;a href="http://p-dd.blogspot.com/" target="_BLANK"&gt;here: http://p-dd.blogspot.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So what does it do?&lt;br /&gt;&lt;br /&gt;Put simply, it's a library of PHP classes that allow for the easy generation of documentation from a set of database sources.&lt;br /&gt;&lt;br /&gt;The idea is that, over time, database sources will be added that will allow for the collection of meta-data from all the major database players (Oracle / MySql / Postgres / etc) and produce documentation in most of the popular forms (HTML / XML / RTF / PDF / etc) including ER diagrams.&lt;br /&gt;&lt;br /&gt;The aim is to make the library simple to use to produce either applications that output documentation for static publication or applications that allow for navigation through the database structure.  Note that it is not the aim of the project to produce either of these applications, merely to allow for their creation.&lt;br /&gt;&lt;br /&gt;It is also recognised that in the future it is desirable to take the library into a more analysis role.  For example - inferring foreign keys that are not explicitly stated, either by examining the table structures or the data within those tables.&lt;br /&gt;&lt;br /&gt;The library is very much in its early stages though, and for now we've got the following:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;A database model that consists of:&lt;br /&gt;    &lt;ul&gt;&lt;br /&gt;      &lt;li&gt;Tables&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;Columns&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;Primary Keys&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;Foreign Keys&lt;/li&gt;&lt;br /&gt;    &lt;/ul&gt;&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The database model can be created from the following sources:&lt;br /&gt;    &lt;ul&gt;&lt;br /&gt;      &lt;li&gt;Oracle&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;XML File&lt;/li&gt;&lt;br /&gt;    &lt;/ul&gt;&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The model can be rendered into the following formats:&lt;br /&gt;    &lt;ul&gt;&lt;br /&gt;      &lt;li&gt;HTML&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;XML&lt;/li&gt;&lt;br /&gt;      &lt;li&gt;Graphviz Neato Diagram (producing an ER diagram)&lt;/li&gt;&lt;br /&gt;    &lt;/ul&gt;&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;There are also lots of other little goodies in there such as datasource in dependant filters, a datasource caching system that limits the round trips to the database and a plethora of examples showing how components can be used as well as a simple Oracle database viewer application to show off what can be possible with just a small amount of work.&lt;br /&gt;&lt;br /&gt;I hope the code is of use, and I'm fully committed to getting more and more functionality into the code as soon as possible in the new year.&lt;br /&gt;&lt;br /&gt;Note: The eagle eyed of you may notice that I've added a new sidebar to this blog which will list the blog posts from the P-dd blog...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/diagram" rel="tag"&gt;diagram&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dot" rel="tag"&gt;dot&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dotty" rel="tag"&gt;dotty&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/neato" rel="tag"&gt;neato&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/er" rel="tag"&gt;er&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/open+source" rel="tag"&gt;open+source&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Postgres" rel="tag"&gt;Postgres&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/MySql" rel="tag"&gt;MySql&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/documentation" rel="tag"&gt;documentation&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-1610507427761613811?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/1610507427761613811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=1610507427761613811' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/1610507427761613811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/1610507427761613811'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/12/p-dd-01-finally-released.html' title='P-dd 0.1 finally released'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3125153360135114415</id><published>2006-12-06T09:07:00.000Z</published><updated>2006-12-06T09:11:27.666Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Principles'/><category scheme='http://www.blogger.com/atom/ns#' term='Open Source'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>Repeating a point</title><content type='html'>The other day I mentioned the principle "Don't repeat yourself".  I think it may have inspired Andy Clarke to &lt;a href="http://radiofreetooting.blogspot.com/2006/12/repetition.html" target="_BLANK"&gt;write this up&lt;/a&gt;, and he's quite right.  It comes from the &lt;a href="http://www.pragmaticprogrammer.com/" target="_BLANK"&gt;Pragmatic Programmers&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;APC's spot on in his description as it relates to writing code, but he doesn't go far enough.&lt;br /&gt;&lt;br /&gt;DRY relates to every part of software development, not just the bit where you're knocking out code.&lt;br /&gt;&lt;br /&gt;If, in any part of the process, you find you have a duplication of knowledge then you have a potential problem.&lt;br /&gt;&lt;br /&gt;Anyone ever read that comment at the top of a procedure and found its description doesn't match the code that follows?&lt;br /&gt;&lt;br /&gt;Watched that demonstration video and found that it's showing you an utterly different version of the system to that which you've just installed?&lt;br /&gt;&lt;br /&gt;Looked at that definitive ER diagram and found it's missing half the tables?&lt;br /&gt;&lt;br /&gt;Well, don't put a comment at the top of the procedure, instead document the behaviour by writing an easy to read unit test for it.  Whilst the knowledge might be duplicated (the test duplicates the knowledge inside the procedure), at least those pieces of knowledge are validated against each other (if you have to repeat, put in some validation)&lt;br /&gt;&lt;br /&gt;Don't have a team writing automated functional tests and another producing videos, write your video scripts as automated tests and have them generated with every build.&lt;br /&gt;&lt;br /&gt;Instead of manually creating a set of ER diagrams and documentation on what the system will be like, write some documentation generation software and have it generated from the current state of the database instead.&lt;br /&gt;&lt;br /&gt;You might notice that there's a running theme here... generation.  Well yup.  One of the best ways of reducing the chances of discrepancies between sources of knowledge is by ensuring there is only one representation of that knowledge and generating the others.&lt;br /&gt;&lt;br /&gt;It's one of the reasons why I've been working on the new Open Source library 'P-dd' (Php Database Documentor).  It's intended be a simple library for the production of database documentation from a number of different sources - the ultimate aim is to be able to read from any of the major RDBMS systems, Wikis, XML files and suchlike and be able to simply output many different forms, HTML, GIF, PDF, XML, Open Office Doc.  Over the next week I intend on letting people know where they can find it, in its early form...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-3125153360135114415?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3125153360135114415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=3125153360135114415' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3125153360135114415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3125153360135114415'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/12/repeating-point.html' title='Repeating a point'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-3830910528146787177</id><published>2006-11-29T09:16:00.000Z</published><updated>2006-11-29T09:19:30.350Z</updated><title type='text'>Worth repeating...</title><content type='html'>A mantra for all elements of software development...&lt;br /&gt;&lt;br /&gt;Repeat after me:&lt;br /&gt;&lt;br /&gt;Don't Repeat Yourself&lt;br /&gt;&lt;br /&gt;Don't Repeat Yourself&lt;br /&gt;&lt;br /&gt;Don't Repeat Yourself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-3830910528146787177?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/3830910528146787177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=3830910528146787177' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3830910528146787177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/3830910528146787177'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/11/worth-repeating.html' title='Worth repeating...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-116285024607036572</id><published>2006-11-06T21:45:00.000Z</published><updated>2006-11-06T22:00:30.546Z</updated><title type='text'>Testing doesn't have to be formal to be automated</title><content type='html'>Something I hear quite a bit from people who don't 'do' automated testing is the set of excuses that goes something like this:&lt;br /&gt;&lt;br /&gt;"Look, we know it's a really good idea and everything but we just can't afford the start-up time to bring in all these automated test tools, set up a continuous integration server and write all the regression tests we'd need in order to get it up and running.  And even if WE thought we could, there's no way we'd get it past the management team."&lt;br /&gt;&lt;br /&gt;Now let's for a second assume that the standard arguments haven't worked in response: How can you afford not to; It'll save you time in the long run; Yada yada yada.  When people are in that mind set there's not much you can do...&lt;br /&gt;&lt;br /&gt;For reasons that I'm going to explain to you right now, we have a project on the go that isn't covered by automated tests.  It's an inherited system that can't have tests retro-fitted in the kind of time we have.  In reality, most of the work we're doing is actually removing functionality, with a few cosmetic changes and a little bit of extra stuff in the middle.  No more than a couple of weeks work.&lt;br /&gt;&lt;br /&gt;It turns out our standard automated test tools can't just be readily fit onto the system we have.&lt;br /&gt;&lt;br /&gt;But that's OK.  It certainly doesn't mean we're not going test, and I'm damn sure that we're going automate a big chunk of it.&lt;br /&gt;&lt;br /&gt;First of all we've separated all the functionality that can be delivered in a new module and that part WILL be fully unit and story tested.  That leaves a pretty small amount of work in the legacy system.  Small enough that we could probably accept the risk associated with not doing any automated testing.&lt;br /&gt;&lt;br /&gt;But that would be defeatist.&lt;br /&gt;&lt;br /&gt;So instead we've picked up &lt;a href="http://www.openqa.org/selenium/" target="_BLANK"&gt;Selenium&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Not the full blown selenium server and continuous integration hooks and whatever.  Just the &lt;a href="http://www.openqa.org/selenium-ide/" target="_BLANK"&gt;Firefox based IDE&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It's simple, and requires absolutely minimal set-up... it's an xpi that just drops straight into Firefox like any other extension.  Having installed the IDE you get action record and playback, and nice context sensitive right click options on any page that allows you to 'assert "this ole text" appears on page' or 'check the value of this item is x'.  Basically it's almost trivial to get a regression test up and running.  Then you can use the IDE to run the test. &lt;br /&gt;&lt;br /&gt;So having got that up an running, before we set about deleting huge swathes of functionality we create a regression test that ensures that the functionality we want to keep stays there.  We've found that a decent sized test that covers a fair few screens and actions can take us as little as an hour to get together.  To put it into perspective:  we put together a test script today that took about 20 minutes to run manually.  That test took us about 45 minutes to sort out in the Selenium IDE and then a minute or two to run it each time after that.  So by the time we'd run it 3 times we'd saved ourselves 15 minutes!&lt;br /&gt;&lt;br /&gt;Running it might involve executing a SQL script manually, running a Selenium script, then checking some e-mails arrive, then running another Selenium script.  In short, it might involve a few tasks performed one after another.  And yes we could automate the whole lot, but like I say... we just don't have the time right now.  But using the tool to add what tests we can right now, to help us with our short (a few hours each) tasks means that we're building up a functional test suite without ever really thinking about it.  We'll keep those scripts, and maybe in a couple of weeks we'll realise that we DO have the time to set up everything else we need for proper functional testing.&lt;br /&gt;&lt;br /&gt;Yep, it could be a hell of a lot better (and on our other projects it is), but some informal testing using an automated runner is an order of magnitude better than no automated testing at all.&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/testing" rel="tag"&gt;testing&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/test" rel="tag"&gt;test&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/test+automation" rel="tag"&gt;test+automation&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/selenium" rel="tag"&gt;selenium&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/extreme+programming" rel="tag"&gt;extreme+programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-116285024607036572?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/116285024607036572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=116285024607036572' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116285024607036572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116285024607036572'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/11/testing-doesnt-have-to-be-formal-to-be.html' title='Testing doesn&apos;t have to be formal to be automated'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-116240369126167790</id><published>2006-11-01T17:49:00.000Z</published><updated>2006-11-01T17:54:51.276Z</updated><title type='text'>Going Dotty</title><content type='html'>There's a new big thing in my sphere of interest: Dotty.&lt;br /&gt;&lt;br /&gt;For the uninitiated: Dotty, Neato and Lefty are a family of products from &lt;a href="http://www.graphviz.org/About.php"&gt;Graphviz&lt;/a&gt; that take pretty simple text files and generate directed or un-directed graphs.&lt;br /&gt;&lt;br /&gt;For the initiated: No, I can't believe I've never found it before either!&lt;br /&gt;&lt;br /&gt;It was name checked during the Google LTAC by at least one presenter, and they reminded me that I'd heard its name quite some time ago and meant to look it up.  When we were looking at a diagramming problem just a few weeks ago and I figured I should track it down.  I was by no means disappointed.&lt;br /&gt;&lt;br /&gt;Basically we wanted something that would graph our MVC workflow configurations to make them more readable.&lt;br /&gt;&lt;br /&gt;That is, our MVC structure allows us to string arbitrary tasks together: perform x, if result is y, go to task z, if result is h go to task i.&lt;br /&gt;&lt;br /&gt;The idea is to keep these configurations as simple as possible; they're only really receiving user input and then prodding objects, but still, there are some complexities.  This is especially true when branches split and rejoin.  For some reason, XML files or a PHP arrays can be difficult to read ;-)&lt;br /&gt;&lt;br /&gt;Quite a long time ago we wrote a small application that would graph them in HTML, but we never liked its results. When paths split and rejoin, the HTML representation wouldn’t show the rejoin.&lt;br /&gt;&lt;br /&gt;So, as I say, we picked up Dotty.&lt;br /&gt;&lt;br /&gt;Simply genius.&lt;br /&gt;&lt;br /&gt;For directional graphs the Dot output is stunning. We can pass it a file in (the trivially simple) Dot language and it'll produce great looking diagrams.&lt;br /&gt;For example, the file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;digraph finite_state_machine {&lt;br /&gt; node [  fontsize="12",  fontname="arial"]&lt;br /&gt; edge [ fontsize="8", fontname="arial"  ]&lt;br /&gt; EntryPoint [ label="EntryPoint (BuildSheepFromInput)", shape="diamond" ];&lt;br /&gt; EntryPoint-&gt;SaveEditedSheep [ label="DEFAULT" ];&lt;br /&gt; SaveEditedSheep [ label="SaveEditedSheep (SaveEditedSheepTask)" ];&lt;br /&gt; SaveEditedSheep-&gt;SaveCheese [ label="DEFAULT" ];&lt;br /&gt; EditSheep__EntryPoint [ shape="box" ];&lt;br /&gt; SaveEditedSheep-&gt;EditSheep__EntryPoint [ label="ERRORS" ];&lt;br /&gt; SaveCheese [ label="SaveCheese (SaveCheeseTask)" ];&lt;br /&gt; SaveCheese-&gt;GetCheeseType [ label="DEFAULT" ];&lt;br /&gt; GetCheeseType [ label="GetCheeseType (GetCheeseTypeTask)" ];&lt;br /&gt; DisplayWensleydale__EntryPoint [ shape="box" ];&lt;br /&gt; GetCheeseType-&gt;DisplayWensleydale__EntryPoint [ label="WENSLEYDALE" ];&lt;br /&gt; GetCheeseType-&gt;DisplayCheddar__ComposeMessage [ label="CHEDDAR" ];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Would produce:&lt;br /&gt;&lt;img src="http://lh6.google.com/bobalicious.bob/RUjcDcfIABI/AAAAAAAAAm8/KgUQb_0dsx0/SaveSheepWorkflow.jpeg?imgmax=576" alt="SaveCheeseWorkflow – Example DOT image"/&gt;&lt;br /&gt;&lt;br /&gt;Stunning!&lt;br /&gt;&lt;br /&gt;It's not difficult to write code to generate the DOT files, and the output from neato (the same as dot, but for undirected graphs) is just as high quality.&lt;br /&gt;&lt;br /&gt;Of course, as soon as I saw the output the cogs started moving in my mind... I'm now on a bit of a brainstorm on what can come next: How about ER diagrams generated from the database schema and published on an internal site.  Generated documentation is never out of date, and it's a damn site easier having it generated of the fly than it is to load up Visio and get THAT monstrosity to do the job for you.&lt;br /&gt;&lt;br /&gt;Anyway, the ER diagramming library will be open source, and it IS on its way... I promise.&lt;br /&gt;&lt;br /&gt;(Note: if you want more info on dotty, &lt;a href="http://www.clsp.jhu.edu/support/faq/lefty.html" target="_BLANK"&gt;take a look here&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/diagram" rel="tag"&gt;diagram&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dot" rel="tag"&gt;dot&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dotty" rel="tag"&gt;dotty&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/neato" rel="tag"&gt;neato&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/er" rel="tag"&gt;er&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/documentation" rel="tag"&gt;documentation&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-116240369126167790?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/116240369126167790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=116240369126167790' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116240369126167790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116240369126167790'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/11/going-dotty.html' title='Going Dotty'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-116230422756046801</id><published>2006-10-31T13:58:00.000Z</published><updated>2006-10-31T14:20:29.123Z</updated><title type='text'>Run Done, and other news</title><content type='html'>Well, it's done.  Finally the running season's finished for me after completing the Rainforest Foundation's 10km run.&lt;br /&gt;&lt;br /&gt;My target for the year was to beat 55 minutes, and I managed it twice.  Sunday's run was finished in 54:01, but I managed to beat that 3 weeks ago in the Nike Run London event... 53:23.  So say I'm pleased is an understatement!&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh6.google.com/bobalicious.bob/RUb_Sy6kABI/AAAAAAAAAmU/wVmafyBf5kE/DSCF5465.JPG?imgmax=512" alt="Happy Bob"/&gt;&lt;br /&gt;&lt;br /&gt;Aside:  To those people protesting against Nike at the event:  You have my sympathies, you really do, but it was a Nike 10km 2 years ago that go me running.  I'd not long before given up smoking and the Nike run gave me a real incentive to get myself fit again (I say again, but really I'm not sure I've ever been that fit).&lt;br /&gt;&lt;br /&gt;Now that doesn't mean that Nike is a great company that gave me my health, or anything like that.  But it does illustrate a point... the vast majority of the people at the event were runners, and Nike took the time to organise a great run, probably not an audience that's going to be swayed by your argument.  As I said to the protesters on the day, and I really mean this: You organise a 10km run, and I'll run it.  An anti-globalisation 10km run in the middle of the most multi-cultural city in the world...&lt;br /&gt;&lt;br /&gt;Anyway, next year I'll be stepping it up a touch more and the aim is to get under 50 minutes.  The first run's booked already.&lt;br /&gt;&lt;br /&gt;And the other news?  Well, over the last few weeks I've been trying to get some coding done on a small open source project.  It's a PHP library that will generate documentation on arbitrary relational databases.  It's in its early stages right now, but I thought it was time to mention it and see if there's any interest out there.&lt;br /&gt;&lt;br /&gt;It'll produce bog standard HTML documentation as well as dotty files that you can use to generate diagrams like this:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.google.com/bobalicious.bob/RUdYPXbMABI/AAAAAAAAAmo/4lBVCxi9jpg/test.jpeg?imgmax=512"  alt="ExamplePddDiagram"/&gt;&lt;br /&gt;&lt;br /&gt;It'll be available soon...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-116230422756046801?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/116230422756046801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=116230422756046801' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116230422756046801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116230422756046801'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/10/run-done-and-other-news.html' title='Run Done, and other news'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-116003168378442544</id><published>2006-10-05T07:50:00.000+01:00</published><updated>2006-10-05T08:01:23.796+01:00</updated><title type='text'>Doing things I'm not very good at</title><content type='html'>Well, last night was my last training run before my next 10Km run.  And this time I've got a target...&lt;br /&gt;&lt;br /&gt;When it's come to official timing, I've managed a 10Km in just over 58 minutes.  This time I want to get it to under 55.  But what's the point in me doing a run if that's the kind of time I'm going to manage?  Odds on bet the winner will do it in something like 35 minutes; I'm not unfit, it's just that I'm not a very good runner; I eat fairly well and am generally pretty healthy, so I don't &lt;i&gt;need&lt;/i&gt; to do this in order to keep fit.&lt;br /&gt;&lt;br /&gt;I do it because I'm not very good at it.  Because running reminds me that I'm not the best at things that I do.  It's blindingly obvious that there are people out there that are vastly superior to me on the track.  In fact, there are 60 year olds out there that are faster than me.  When I did my one and only half marathon I was beaten by a veteran speed walker.&lt;br /&gt;&lt;br /&gt;But I'm getting better.  I'm learning more about running and training, and I will get faster.  One day I may manage under 45 minutes... and then I'll have to pick something else.&lt;br /&gt;&lt;br /&gt;The last run of the year will be the &lt;a href="http://www.rainforestfoundationuk.org/s-10k%20Run" target="_BLANK"&gt;Rainforest Foundation 10Km&lt;/a&gt;, and if you know me (and want to), &lt;a href="http://www.justgiving.com/robbaillierainforest" target="_BLANK"&gt;you can sponsor me&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-116003168378442544?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/116003168378442544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=116003168378442544' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116003168378442544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/116003168378442544'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/10/doing-things-im-not-very-good-at.html' title='Doing things I&apos;m not very good at'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115873480086041956</id><published>2006-09-20T07:38:00.000+01:00</published><updated>2006-09-20T07:49:25.546+01:00</updated><title type='text'>Countries I've visited</title><content type='html'>A friend pointed me in the direction of this little beauty:  &lt;a href="http://douweosinga.com/projects/visitedcountries" target="_BLANK"&gt;A site that generates a map of the world showing the countries you've visited.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've got to admit that the comments on the site are more actual fun than the map, but still...&lt;br /&gt;&lt;br /&gt;So here's mine:&lt;br /&gt;&lt;img src="http://www.world66.com/community/mymaps/worldmap?visited=ATBEQICZFRDEGRHUIENLESUKJPPHTH" alt="Countries I've visited"/&gt;&lt;br /&gt;&lt;br /&gt;Not &lt;i&gt;that&lt;/i&gt; impressive.&lt;br /&gt;&lt;br /&gt;Fingers crossed, if all goes to plan, in 18 months it'll look more like this:&lt;br /&gt;&lt;img src="http://www.world66.com/community/mymaps/worldmap?visited=CAUSATBEQICZFRDEGRHUIEITNLESUKJPPHTHAU" alt="Countries I plan to have visited"/&gt;&lt;br/&gt;&lt;br /&gt;&lt;br /&gt;Though still, it'll look nothing like as impressive as &lt;a href="http://tkyte.blogspot.com/" target="_BLANK"&gt;Tom Kyte's&lt;/a&gt; "countries I've in which I've given presentations" ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115873480086041956?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115873480086041956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115873480086041956' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115873480086041956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115873480086041956'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/countries-ive-visited.html' title='Countries I&apos;ve visited'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115833477548432028</id><published>2006-09-15T16:22:00.000+01:00</published><updated>2006-09-15T16:39:35.576+01:00</updated><title type='text'>Make your own mind up</title><content type='html'>Earlier today Bob Binder made a comment on my test conference entry that included a (not too complimentary) summary of his talk.  In response to his comment I made a statement that I want to make absolutely clear to everyone else...&lt;br /&gt;&lt;br /&gt;I thought that every single one of the talks at the conference was worth attending.&lt;br /&gt;Every one had value, and I think you should watch every one on Google video.&lt;br /&gt;&lt;br /&gt;If any comment on an individual talk makes it sound uninviting it doesn't mean it wasn't a good talk.  The conference quality was far above the norm, and every talk is worth seeing.  And you can, they're on &lt;a href="http://video.google.com/videosearch?q=label:ltac"&gt;Google Video&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Other than that, I invite comments from the speakers, other attendees and anyone that might disagree with me.  Just because I have an opinion, doesn't mean that I don't want to hear yours!  But don't expect me to agree with you ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115833477548432028?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115833477548432028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115833477548432028' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115833477548432028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115833477548432028'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/make-your-own-mind-up.html' title='Make your own mind up'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115830802867372560</id><published>2006-09-15T09:12:00.000+01:00</published><updated>2006-09-15T09:13:48.686+01:00</updated><title type='text'>The Google Test Automation Videos are here...</title><content type='html'>Well, not here, but &lt;a href="http://video.google.com/videosearch?q=label:ltac" target="_BLANK"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'll update the earlier posts with each relevant video link when I can...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115830802867372560?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115830802867372560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115830802867372560' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115830802867372560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115830802867372560'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/google-test-automation-videos-are-here.html' title='The Google Test Automation Videos are here...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115818163919523872</id><published>2006-09-13T21:56:00.000+01:00</published><updated>2006-09-13T22:29:19.636+01:00</updated><title type='text'>XHTML Transitional</title><content type='html'>OK, so it's taken a bit of time, though very little effort (if you know what I mean), and I think it's probably one of the sadest exercises I've ever gone through (pretty difficult to believe, I know)... BUT the home page of this site is now XHTML Transitional compliant.&lt;br /&gt;&lt;br /&gt;It's been validated in two seperate sources.&lt;br /&gt;&lt;br /&gt;First up, the obvious: &lt;a href="http://validator.w3.org/check?uri=http%3A%2F%2Frobertbaillie.blogspot.com%2F&amp;amp;charset=%28detect+automatically%29&amp;amp;doctype=Inline" target="_BLANK"&gt;W3C 's online validator&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Secondly (because one is never enough), the much handier HTML Validator Firefox extension by Marc Gueury, which is based on the Tiny validator.&lt;br /&gt;&lt;br /&gt;The plan is to keep any new posts to the site XHTML transitional compliant.  Though I've got no plans to go back and fix earlier posts. Frankly I've got better things to do with my time than remove the &amp;lt;br /&amp;gt; tags that Blogger sticks between &amp;lt;li&amp;gt; tags and change all the &amp;amp;s to &amp;amp;amp;s in the hrefs. :)&lt;br /&gt;&lt;br /&gt;At least the site finally looks the same in Firefox and IE6 now!&lt;br /&gt;&lt;br /&gt;Update: Is it just me, or is the commented out CDATA tag inside the script tag telling the browser that there's unstructured data a bit of a hack.  I know my javascript should be in a different file, but I don't have any hosting for the files, and I'm not going to set it up for 2 or 3 1K files!.  Anyway, you'd think the spec would cover this problem in a more natural way.  Maybe an attribute on the tag &lt;pre&gt;unstructured="true"&lt;/pre&gt;  Maybe &lt;pre&gt;type="CDATA"&lt;/pre&gt;  Maybe it can be implied by the script tag itself?&lt;br /&gt;&lt;br /&gt;I'm no XML expert, but the solution seems more than a bit nasty to me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115818163919523872?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115818163919523872/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115818163919523872' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115818163919523872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115818163919523872'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/xhtml-transitional.html' title='XHTML Transitional'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115815298168753550</id><published>2006-09-13T13:54:00.000+01:00</published><updated>2006-09-13T21:56:38.573+01:00</updated><title type='text'>Google LTAC - A more personal note</title><content type='html'>Alright, so I've been going on about the Google LTAC quite a bit recently, but I wanted to mention a few more personal observations...&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Is it coincidence that the comedy presenters were called Adam and Joe?&lt;/li&gt;&lt;li&gt;Google might talk about Work / Life balance, but you're always at the conference even when you're on the toilet thanks to the highly informative 'testing on the toilet' hints and tips sheets ;) (see below)&lt;/li&gt;&lt;li&gt;Even the user interface 'simplicity innovators' can't help themselves when it comes to conference freebies... I never realised that I needed a coloured light on my pen until now. (Shame the light makes the pen a bit too heavyweight, the ink keeps clogging meaning a slow startup time and the blue LED keeps on cutting out)&lt;/li&gt;&lt;li&gt;Also, for a company that's very much 'of the now', a mouse mat is just soooo last millennium.&lt;/li&gt;&lt;li&gt;I've never been at a conference where there were so many laptops. Although I'm a little surprised that no-one brought any internet enabled CI lava lamps with them.&lt;/li&gt;&lt;li&gt;Google may not be Evil, but they still gave us plastic cutlery and polystyrene plates (boooo)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;And a couple of awards&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Phrase of the conference: "That's a big bucket of suck"&lt;/li&gt;&lt;li&gt;Agile Pimp: Dan North, a man with an eye for spotting the delegate that's ripe for a bit of lean process&lt;/li&gt;&lt;li&gt;Free snack food of the conference: Innocent Smoothies.&lt;/li&gt;&lt;li&gt;Information Download Award: Adam Porter, watch the video (when it's out), you'll understand.&lt;/li&gt;&lt;li&gt;Demo of the conference: Jason Huggins, the cutting edge can cut you deeply when you've got an audience.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://static.flickr.com/88/237577001_b972a66a73.jpg?v=1157739881" alt="Testing on the toilet"/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115815298168753550?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115815298168753550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115815298168753550' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115815298168753550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115815298168753550'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/google-ltac-more-personal-note.html' title='Google LTAC - A more personal note'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115806601187814971</id><published>2006-09-12T13:54:00.000+01:00</published><updated>2006-09-12T14:00:11.890+01:00</updated><title type='text'>Spare a moment for the little people</title><content type='html'>They're &lt;a href="http://little-people.blogspot.com/" target="_BLANK"&gt;here&lt;/a&gt;, and they may need your help.&lt;br /&gt;&lt;br /&gt;Also, it's good to see the new world order we were promised has finally &lt;a href="http://www.woostercollective.com/2006/08/found_new_world_order.html" target="_BLANK"&gt;arrived&lt;/a&gt;, though if might appear that Disney World has taken the political situation a bit far over &lt;a href="http://www.woostercollective.com/2006/09/breaking_the_story_disneyland_doesnt_wan.html" target="_BLANK"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115806601187814971?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115806601187814971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115806601187814971' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115806601187814971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115806601187814971'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/spare-moment-for-little-people.html' title='Spare a moment for the little people'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115790893586596349</id><published>2006-09-10T18:13:00.000+01:00</published><updated>2006-09-21T22:13:18.923+01:00</updated><title type='text'>A language for discussing performance testing</title><content type='html'>OK, so all I'm doing here is repeating the text that I previously had in a post about the Google London Automation Testing Conference, discussing the talk by Goranka Bjedov on Performance testing.&lt;br /&gt;&lt;br /&gt;I figured that it was sitting in the middle of a fairly large post, and I wanted it to be seen and reviewed by more people than would be bothered to plough through the other stuff.&lt;br /&gt;&lt;br /&gt;It's a suggested series of terms by which different types of performance tests can be described:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Performance Test: Given load X, how fast will the system perform function Y?&lt;/li&gt;&lt;li&gt;Stress Test: Under what load will the system fail, and in what way will it fail?&lt;/li&gt;&lt;li&gt;Load Test: Given a certain load, how will the system behave?&lt;/li&gt;&lt;li&gt;Benchmark Test: Given this simplified / repeatable / measurable test, if I run it many times during the system development, how does the behaviour of the system change?&lt;/li&gt;&lt;li&gt;Scalability Test: If I change characteristic X, (e.g. Double the server's memory) how does the performance of the system change?&lt;/li&gt;&lt;li&gt;Reliability Test: Under a particular load, how long will the system stay operational?&lt;/li&gt;&lt;li&gt;Availability Test: When the system fails, how long will it take for the system to recover automatically?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;In addition to the above, there is then another term, which I would suggest is not a type of test in its own right, rather it is a term denoting the depth of analysis being performed.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Profiling: Performing a deep analysis on the behaviour of the system, such as stack traces, function call counts, etc.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Any thoughts?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115790893586596349?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115790893586596349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115790893586596349' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115790893586596349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115790893586596349'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/language-for-discussing-performance.html' title='A language for discussing performance testing'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115780138031862280</id><published>2006-09-09T12:24:00.000+01:00</published><updated>2006-09-15T09:27:59.863+01:00</updated><title type='text'>Google Automated Testing Conference (London) - Day 2 (part 2)</title><content type='html'>&lt;b&gt;Selenium: The in-browser acceptance testing tool&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=-594153467742593805&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Another talk I was looking forward to was Jason Huggins, the creator of &lt;a href="http://www.openqa.org/selenium/" target="_BLANK"&gt;Selenium&lt;/a&gt; talking about his tool.  I was hoping for a little on the basics of the tool, but really the idea is simple enough to be easy to describe.&lt;br /&gt;&lt;br /&gt;It is a test framework that allows you to build Fit like (HTML table), or code based UI tests and then drive your web application through multiple browsers. The result is a solution to the web version of the mobile problem... How do you test the against the diversity of target platforms.&lt;br /&gt;&lt;br /&gt;The product ships with an IDE that sits in Firefox and allows you to record actions against the application, then edit the resulting test scripts.  These scripts can be in any of many languages such as Java, C# and Ruby.  I did notice the lack of PHP support though. Shame, but does it really matter?&lt;br /&gt;&lt;br /&gt;The bulk of his talk was then one some of the possibilities you can get from this.  In particular, his demo was to check a change into &lt;a href="http://subversion.tigris.org/" target="_BLANK"&gt;subversion&lt;/a&gt;, this would kick off &lt;a href="http://cruisecontrol.sourceforge.net/" target="_BLANK"&gt;cruise control&lt;/a&gt; and run the unit tests for his app.  The successful build message would then get picked up by a number of listeners running in different OSs running on virtual machines.  These listeners would kick off Selenium for different browsers, all run the same set of tests and record the resulting actions as movies.&lt;br /&gt;&lt;br /&gt;His ultimate aim was then to add some voice over and these screencasts could be used as marketing videos: in fact, he suggested that the Apple voice synthesis would be good enough for that job (especially the new version coming out soon), and the spoken text could be in the test itself.&lt;br /&gt;&lt;br /&gt;Very nice idea.  it's a shame the demo didn't actually work ;)&lt;br /&gt;&lt;br /&gt;One point made was that the movies are going to get big, and maybe you wouldn't want to keep them for all versions.  Maybe, but then maybe you don't need movie files... Maybe just the html files will do.  You can then wrap it up in a player that will move you between pages and kick off the right bit of audio at the right time.  A selenium driven app won't show you the mouse pointer, so you don't loose that... You could highlight clicks and focus with a bit of nifty css.  Alas I didn't get a chance to pass the idea on to Jason as he was (unsurprisingly) surrounded for most the rest of the day.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: When you're innovating, your demos might not be as smooth as you want them to be ;)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Or maybe&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: The DRY principle (don't repeat yourself) crosses so many boundaries it's crazy.  Be aware that there are many ways you can re-use the same data / process / tools, and automation can be applied to many many things.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Testing Metro WiFi&lt;/b&gt; (No Google video yet :( )&lt;br /&gt;&lt;br /&gt;Karl Garcia of Google was next up, talking about testing the open wireless network in Mountain View, California.&lt;br /&gt;This new network covers about 12 square miles of reasonably densely populated urban area.  The network consists of just under 400 wireless nodes sitting on the top of lampposts spread no more than about 150 yard apart.  This mesh is then connected to 3 base stations via a number of gateways spread more sparsely.&lt;br /&gt;&lt;br /&gt;The question is, how do you automate the testing of such a beast.&lt;br /&gt;&lt;br /&gt;The testing was covered in two main stages. First the coverage test... Simply taking a device that will poll the network and driving it down every street. Hook that up to GPS and get it to record the network strength and you've got stage 1 covered.&lt;br /&gt;&lt;br /&gt;The second is the throughput testing.  For this the team grabbed cheap routers and PDAs, installed Linux and &lt;a href="http://dast.nlanr.net/Projects/Iperf/" target="_BLANK"&gt;iPerf&lt;/a&gt; (network testing tool), setup their start-up so that they'd report in to the main server and get ready to run test, make sure that if they fail they go into a restart mode and then gave them cheap solar panels.  Then it was just a case of picking a spot to test, taking the clients out and leaving them.&lt;br /&gt;&lt;br /&gt;Back at the office the test server polls for the clients, gets them to run the suites and reports the results.  OK, so the machines have to be taken out and placed, but a small number of inexpensive components makes the kit cheap.  If the client machines go down then you just need to wait for them to reboot and they'll get back in touch.  No need to go out and reset them.&lt;br /&gt;&lt;br /&gt;When you need to change the test area you just go out, pick the clients up and move them.  Karl admitted that they could have had more clients and that they considered having more expensive bits, but in the end the kit they had was stupidly cheap and more than fit for purpose.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: Not every part of the process can be automated, but you can minimise those bits that can't and still get great benefits.  The most expensive way of doing that isn't always the best.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Distributed Continuous Quality Assurance:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=8839342624264709864&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And the last full talk of the day was Adam Porter.  With an incredible bio (in terms of academia), it seemed as though Adam thought the lightning talks had started early and he powered through 45minutes of information dispensing, at an incredible speed.&lt;br /&gt;&lt;br /&gt;His discussion as very much at an academic level, but the system he talked about sounds like it may be one of the next big things in testing.&lt;br /&gt;&lt;br /&gt;The starting premise is this: traditional testing strategies are failing large scale development because the variations possible in the configuration of most new systems are enormous.  It simply isn't possible to test the full variation of configuration options, deployment operating systems, setups of those operating systems and so on.&lt;br /&gt;&lt;br /&gt;His system (Skoll) attempts to address that.&lt;br /&gt;&lt;br /&gt;I'm going to go into no depth on this topic whatsoever because the amount of information was overwhelming, but I'll try to give an overview.&lt;br /&gt;&lt;br /&gt;By splitting QA tasks into small chunks of work, defining the valid combinations of deployment, providing a means of producing those variations and using a grid of machines to test on it is possible to cover an enormous amount of combinations in your tests.&lt;br /&gt;If you then get smart about which combinations should be tested then you can statistically cover the whole set without having to actually test them all.&lt;br /&gt;&lt;br /&gt;You can test disparate configurations until you find a failure, then take that configuration, change it in a single dimension and then test again... Feel out the extent of the failure scape.&lt;br /&gt;&lt;br /&gt;You can then datamine those results to try to provide clues on the underlying problem.&lt;br /&gt;He gave compelling arguments that certain strategies for choosing particular configurations work and followed every one with a case study that tested it empirically.&lt;br /&gt;&lt;br /&gt;He also showed how the approach can be used for tracking performance problems with a selective method of benchmarking.&lt;br /&gt;&lt;br /&gt;It was a very solid, very info laden, well thought out talk n a great approach.  When the video comes out, I urge you to watch it.  At half speed.&lt;br /&gt;&lt;br /&gt;And if you're really interested in reading the papers, there's one &lt;a href="http://www.cs.umd.edu/~atif/papers/ICSE-04.pdf#search=%22Skoll%20testing%22" target="_BLANK"&gt;here&lt;/a&gt; and you can find many more (or copies of the same) by searching on Google for Skoll testing.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: It's not just about the volume of QA you perform, it's about the QUALITY of the QA.  By getting smarter in your testing you can cover more of the application in less time.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lightning Talks:&lt;/b&gt; (No Google video yet :( )&lt;br /&gt;&lt;br /&gt;Finally it was the turn of the lightning talks. 10 speakers, 10 subjects, 5 minutes per speaker, no exceptions.  I'm not going to cover much of the ground here because it was, of course, a lot of info in a small amount of time.  It's worth picking up the video when it's available and watching it.  The quality's a bit hit and miss, but you can always skip the 5 minutes of the person you don't like.&lt;br /&gt;&lt;br /&gt;Highlights were Dan North on 'Getting Lean' – what it means and how automation helps you get there, Steve Freman and Nat Price giving us a glimpse of jMock, particularly of jMock 2 (looking tasty). James Lyndsay reminding us that "automation good" does not equal "manual bad" and Jordan Dea-Mattson reminding us that our QA processes need to have "defence in depth", or "overlapping fields of fire".&lt;br /&gt;&lt;br /&gt;All in all, an excellent 2 days!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115780138031862280?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115780138031862280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115780138031862280' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115780138031862280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115780138031862280'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/google-automated-testing-c_115780138031862280.html' title='Google Automated Testing Conference (London) - Day 2 (part 2)'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115779904130686746</id><published>2006-09-09T11:42:00.000+01:00</published><updated>2006-09-21T22:11:34.353+01:00</updated><title type='text'>Google Automated Testing Conference (London) - Day 2 (part 1)</title><content type='html'>So, day 2 has been and gone, and it seemed slightly quieter than day 1.&lt;br /&gt;It seems like the free bar took its toll.  Despite the slight drop in numbers, the conversations seem a little more free flowing. Damn it!  Lesson to earn from this conference: 'No matter how bad you feel, go to the free bar'.&lt;br /&gt;&lt;br /&gt;Anyway, this time round I'm going to tackle the blog entry in two chunks... Thursday's entry was just too damn big!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Objects - They just work:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=-1460202716366487526&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I may be biased here, but I was really disappointed with this one. It looked like it might be a talk on the decomposition of objects into the simplest form so that they just work.&lt;br /&gt;&lt;br /&gt;It wasn't.&lt;br /&gt;&lt;br /&gt;The title was a reference to the NeXTSTEP presentation given by Steve Jobs way back in 92 when he said the same.  The point being that they didn't just work.  There was a lot of pain and hardship to get them to work, and that's a recurring theme in software development.&lt;br /&gt;&lt;br /&gt;So the talk was given by Bob Binder, CEO and founder of &lt;a href="http://www.mverify.com" target="_BLANK"&gt;mVerify&lt;/a&gt;.  He discussed a few of the difficulties and resulting concepts behind their mobile testing framework.  &lt;br /&gt;&lt;br /&gt;We've already had a discussion on the difficulties involved (permutations), so that didn't tell us anything we didn't already know.&lt;br /&gt;&lt;br /&gt;He moved on to mention &lt;a href="http://www.ttcn-3.org/TTCN3about.htm" target="_BLANK"&gt;TTCN (Testing and Test Control Notation)&lt;/a&gt; an international standard for generic test definition.  It's probably worth a look.&lt;br /&gt;&lt;br /&gt;He also mentioned the fact that their framework added pre and post conditions to their tests - require and ensure.  I may be a die hard stick in the mud here, but the simplicity of 'setup – prod – check – throw away' seems like a pretty flawless workflow for testing to me.  Though I admit, I could well be missing something: If anyone can enlighten me on their use I'd be reasonably grateful. Though thinking about it, I wasn't interested enough to ask for an example then, so maybe you shouldn't bother ;)&lt;br /&gt;&lt;br /&gt;One good thing to come out was the link to that &lt;a href="rixstep.com/1/1/20060814,00.shtml" target="_BLANK"&gt;NeXTSTEP demo&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I really want to say more and get some enthusiasm going, but sorry Bob, I just can't.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: Try as they might, CEOs can't do anything without pimping their product and glossing over the details.&lt;/b&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Goranka Bjedov - Using Open Source tools for performance testing:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=-6891978643577501895&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After the disappointment of the first talk, this one was definitely a welcome breath of fresh air.&lt;br /&gt;&lt;br /&gt;Like the first time I read &lt;a href="http://www.amazon.co.uk/-Pragmatic-Programmer/dp/020161622X/sr=8-1/qid=1157748034/ref=pd_ka_1/026-2847427-0002806?ie=UTF8&amp;amp;s=gateway" target="_BLANK"&gt;Pragmatic Programmer&lt;/a&gt;, this talk was packed full of 'yes, Yes, YES' moments.  If you took out all the bits I had previously thought about and agreed with you'd be left with a lot of things I hadn't thought about, but agreed with.&lt;br /&gt;&lt;br /&gt;When the videos hit the web you MUST watch this talk.&lt;br /&gt;&lt;br /&gt;Goranka proposed a vocabulary for talking about performance tests, each test type with a clear purpose.  Having this kind of clear distinction allows people to more clearly define what they're testing for, decide what tests to run, and ultimately work out what the test results are telling them.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Performance Test – Given load X, how fast will the system perform function Y?&lt;/li&gt;&lt;li&gt;Stress Test – Under what load will the system fail, and in what way will it fail?&lt;/li&gt;&lt;li&gt;Load Test – Given a certain load, how will the system behave?&lt;/li&gt;&lt;li&gt;Benchmark Test– Given this simplified / repeatable / measurable test, if I run it many times during the system development, how does the behaviour of the system change?&lt;/li&gt;&lt;li&gt;Scalability Test – If I change characteristic X, (e.g. Double the server's memory) how does the performance of the system change?&lt;/li&gt;&lt;li&gt;Profiling – Performing a deep analysis on the behaviour of the system, such as stack traces, function call counts, etc.&lt;/li&gt;&lt;li&gt;Reliability Test – Under a particular load, how long will the system stay operational?&lt;/li&gt;&lt;li&gt;Availability Test – When the system fails, how long will it take for the system to recover automatically?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I would probably split the profiling from the list and say that you could profile during any of the above tests, that's really about the depth of information you're collecting.  Other than that I'd say the list is perfect and we should adopt this language now.&lt;br /&gt;&lt;br /&gt;She then put forward that infrastructure you need in order to do the job.&lt;br /&gt;&lt;br /&gt;I don't want to be smug about it, but the description was scarily similar to that which we've put together.&lt;br /&gt;&lt;br /&gt;Alas, the smugness didn't last long because she then went on to tell us the reasons why we shouldn't bother trying to write this stuff ourselves... That the open source community is already rich for doing these jobs, directing us to look at &lt;a href="http://jakarta.apache.org/jmeter/"&gt;Jmeter&lt;/a&gt;, &lt;a href="http://opensta.org/" target="_BLANK"&gt;OpenSTA&lt;/a&gt; and &lt;a href="http://grinder.sourceforge.net/" target="_BLANK"&gt;Grinder&lt;/a&gt;.  A helpful bystander also directed us to &lt;a href="http://opensourcetesting.org/"&gt;opensourcetesting.org&lt;/a&gt; - there are a lot of test tools on there.&lt;br /&gt;&lt;br /&gt;Fair enough... I admit we didn't look when we put together our test rig, but you live and learn.  And I'll definitely be taking a look for some DB test tools.&lt;br /&gt;&lt;br /&gt;A big idea I'll be taking away is the thought that we could put together a benchmarking system for our products. This isn't a new thought but rather an old one presented in a new way.  Why shouldn't we put together a run that kicks off every night and warns us when we've just killed the performance of the system.  It's just about running a smoke test and getting easy to read latency numbers back. Why not? Oh, I'll tell you why not... We need production hardware ;)&lt;br /&gt;&lt;br /&gt;She then gave us a simple approach to start performance testing with, a series of steps we can follow to start grabbing some useful numbers quickly:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Set up a realistic environment&lt;/li&gt;&lt;li&gt;Stress test&lt;/li&gt;&lt;li&gt;&lt;ul&gt;&lt;li&gt;Check the overload behaviour&lt;/li&gt;&lt;li&gt;Find the 80% load point&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Build a performance test based on the 80%&lt;/li&gt;&lt;li&gt;&lt;ul&gt;&lt;li&gt;Make it run long enough for a steady state to appear&lt;/li&gt;&lt;li&gt;Give it time to warm up at the start&lt;/li&gt;&lt;li&gt;Collect the throughput and latency numbers for the app and the machine performance stats.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;If I wasn't already married, I might have fallen in love :)&lt;br /&gt;&lt;br /&gt;Main message: You CAN performance test with off the (open source) shelf software, it just takes clarity of purpose, infrastructure, a production like deployment and time.&lt;br /&gt;&lt;br /&gt;Oh, and you're always happiest in a conference when someone tells you something you already know ;)&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Testing Mobile Frameworks with FitNesse:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=582823406455567909&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As the last one before lunch Uffe Koch took the floor with a pretty straightforward  talk.  By this time I was sick of hearing about mobile testing ;)&lt;br /&gt;The thing is, the manual testing problem is so big with mobiles that it's prime for automation.&lt;br /&gt;&lt;br /&gt;It turned out that he gave a pretty good talk on the fundamentals of story (or functional) testing practice.  For a different audience, this would have been a fantastic talk, but unfortunately, I think most people here are already doing many of the things.&lt;br /&gt;&lt;br /&gt;A lot of the early part of the discussion crossed over between the Fit and Literate Testing talks from the day before, though the ideas weren't presented in the same kind of depth.  The suggestion that the test definition language of 'click 1 button' was a domain was pushing it, but the point is reasonably valid. The structure of test definition languages need to be very different to the programming languages we're used to.  This is one of the winning points of Fit, since its presentation is very different to Java, C# or whatever it's approached by the developers in a very different way.  Kudos to Uffe for realising this explicitly and producing a language for driving the app framework.&lt;br /&gt;&lt;br /&gt;His team have put together a UI object set that can be driven through the serial or USB port of a phone and can report the UI state back to the tester at any time, passing it to the tester as an XML document.&lt;br /&gt;&lt;br /&gt;It's very similar to our method.  We do it so that the test don't need to know about page structure, just the individual components; so we don't need to worry with things like XPath when we want to extract data from the page; so our story tests aren't as brittle as they could be.  They're doing this to solve the problem of screen scraping from a phone.&lt;br /&gt;&lt;br /&gt;It's an elegant solution to testing these phones and whilst Uffe admits that it means you're not testing the real front end, or the real screen display, it allows them to hook up a phone to the test rig and run the full suite.  I'm sure those tests must take and age though... doing a UI test of a web page is bad enough, but some of those phones can take some time to respond!  I'd like to see the continuous integration environment.  I've got an image of 500 Dell machines hooked up to different phones through masses of cables.  That'd be cool!&lt;br /&gt;&lt;br /&gt;The common FitNesse question did come up:  How did you address the version control of the FitNesse scripts.  Like everyone else (it seems), the archiving was switched off, local copies of the wikis were created and they got checked into the same version control as the code when they were changed. I really feel I've got to ask the question:  If this is the way everyone does it, why isn't there an extension to the suite to allow this out of the box?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: With a bit of thought and design even the most difficult to test targets can be tested.  You just might need a tiny touch of emulation in there.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;And that led us on to lunch...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115779904130686746?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115779904130686746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115779904130686746' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115779904130686746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115779904130686746'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/google-automated-testing-conference_09.html' title='Google Automated Testing Conference (London) - Day 2 (part 1)'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115766077896176360</id><published>2006-09-07T19:42:00.000+01:00</published><updated>2006-09-15T09:30:26.983+01:00</updated><title type='text'>Google Automated Testing Conference (London) - Day 1</title><content type='html'>Google Automated Testing Conference (London) - Day 1&lt;br /&gt;&lt;br /&gt;(Get ready, it's a long one)&lt;br /&gt;&lt;br /&gt;So, finally the Google testing conference has come around, and it's pretty good at making a man feel like a small fish in a big pond. It's pretty clear that the place is populated by developers who are all working in some form of test driven way.  A large number contribute to open source software, and many of those are working on test frameworks... 3 of the 4 main contributors to &lt;a href="http://www.jmock.org/" target="_BLANK"&gt;jMock &lt;/a&gt;are in attendance.  I don't mind admitting that I feel like a bit of an interloper.&lt;br /&gt;&lt;br /&gt;But before I start, I've got to point out that (of course) all the words here are my own interpretations of the presenters words... and I could very easily have got it all very very wrong.  But then that would be their fault for presenting badly ;)&lt;br /&gt;&lt;br /&gt;Also, Google assured us that the talks will be available on Google video, and that many supporting links will be sent out to attendees.  Once I get those things I'll update this entry to keep you updated.&lt;br /&gt;&lt;br /&gt;Anyway, the first day didn't disappoint:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Distributed Testing with SmartFrog:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=-4478242864801668108&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Steve Lougran and Julio Guijarro talked about their HP Labs research project on testing distributed systems: &lt;a href="http://www.smartfrog.org/" target="_BLANK"&gt;SmartFrog&lt;/a&gt;.  They've got a good looking framework together that allows you to define a system deployment as a class hierarchy and then describe their relationships.  That means that you can use it to state when and where components need to be deployed, services need to start and so on. A nice tool for rolling out complex distributed systems.&lt;br /&gt;&lt;br /&gt;But their interesting points came when they talked about using it for testing.   By wrapping a few testing frameworks (&lt;a href="http://www.junit.org/index.htm" target="_BLANK"&gt;JUnit&lt;/a&gt; for one) and then describing the test suites as components to install, they're producing a framework that allows you to test each component of the system in a place similar to where it would run when in production.  Not only that, but it allows you to install emulated components such as switches, routers and flaky proxy servers allowing you to test on complex and failure prone infrastructure.  Not bad.&lt;br /&gt;&lt;br /&gt;Their main problems at the moment seem to come from trying to then collect all the test results, logs and suchlike and compiling that into a reasonable test result summary.  It's fine when things pass, but as soon as you get a lot of failures it starts to stutter. But that problem's surely surmountable (if dull to solve ;) ).  A few people out there must be looking at the same thing under a different guise.&lt;br /&gt;&lt;br /&gt;One great thing to come out of it was the call to arms to those producing test frameworks... Where is the common reporting standard?  A guy working on &lt;a href="http://www.lastcraft.com/simple_test.php" target="_BLANK"&gt;SimpleTest &lt;/a&gt;(sorry, didn't catch his name) seemed to be up for it...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: Test using real deployments, not just idealised or local versions.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;b&gt;Literate Functional Testing:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=1505469784301926538&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Robert Chatley and Tom White of &lt;a href="http://www.kizoom.com/" target="_BLANK"&gt;Kizoom&lt;/a&gt; talked about their functional testing framework that extends the Knuth idea of 'Literate Programming'.  It's most succinctly described in Knuth's own words: "Let us change our traditional attitude to the construction of programs. Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do."&lt;br /&gt;&lt;br /&gt;The aim is to end up with a language that can be used by both developers and customers; that the resulting test code can be read by non developers and therefore can by used by the customer to validate the developers' interpretation of the system requirements.&lt;br /&gt;&lt;br /&gt;They have produced a language that takes few jMock ideas (like &lt;a href="http://www.jmock.org/constraints.html" target="_BLANK"&gt;constraints&lt;/a&gt;) and uses them to produce truly elegant code. &lt;br /&gt;&lt;br /&gt;So, the test cases become along the lines of&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;assertThat( currentPage, has( 3, selectBoxes.named('Region', 'Time', 'Method' ) ) );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The idea is lofty, looks pretty damn good and definitely reverberates with my own ideas on producing story tests.  They drive through the user interface and are easy to read.  Implementing the same tests in a more traditional Java approach leads to a very difficult to read lump of code.&lt;br /&gt;&lt;br /&gt;It's a shame they haven't taken it a little further so that the code is a fully readable English script rather than a halfway house between English and Java, but I love it none the less.&lt;br /&gt;&lt;br /&gt;Oh, and like any good tool should, it's going &lt;a href="http://code.google.com/p/literate/" target="_BLANK"&gt;open source&lt;/a&gt;...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: It's possible to write functional tests that can be read by your customer, it just takes a different approach to the rest of your code.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;b&gt;Testing using Real Objects:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=6552390366458794081&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Massimo and Massimo (Arnoldi and Milan) talked about their approach to generating test data for their (sounds) highly successful company &lt;a href="http://www.lifeware.ch/" target="_BLANK"&gt;Lifeware&lt;/a&gt;.  By no means particular to their market (life insurance), they find that bugs don't occur in their system with short lived data.  Rather it's the contracts that have lived in the system for years, and have a huge amount of complex events in their lifetime are always the ones that fail.  Those contracts are basically atypical of the ones that are usually used for testing.&lt;br /&gt;&lt;br /&gt;They found that there was always a great deal of difficulty in producing test cases for these large data sets and so have produced a method of exporting data from live systems an importing it into the test suite.&lt;br /&gt;&lt;br /&gt;They admitted that it was born of a data migration tool, and you can see how.  Having identified an object that needs to be extracted they generate a set of events that will re-create it.  Those events are simply any data changes that you would need to create the primary and related objects in the state that they exist now.  If a contract has a number of payments against it, then you'll see a number of distinct payment events in the list.&lt;br /&gt;This list of events is then translated into a series of method calls that can be used to recreate the object in another environment.  Having created the data set it's merely a case of describing whatever assertions you need in your test.&lt;br /&gt;&lt;br /&gt;It sounds like there's some clever Smalltalk code in the background going on, and much of the talk was on that, but it's the idea that's the important component.&lt;br /&gt;&lt;br /&gt;As a means of extracting or generating test data it sounds great.  The events list is a neat solution.  However, as a means of describing a data migration it sounds phenomenal!  And that's where &lt;b&gt;I&lt;/b&gt; really see the benefits.  Being of a DB background that's no real surprise ;-)&lt;br /&gt;&lt;br /&gt;If you can always generate data in your system from a set of precise events then when you need to migrate data from an external system you don't need to create a data-mapping, you need to create an event mapping.  Customers are notoriously bad at data mapping because the data often not in a form they recognise.  But these events sound to me like a domain language just waiting to jump out.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: Test using real data (objects), not just idealised versions.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;b&gt;Doubling the value of Automated tests:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=-7227306990557696708&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next up was Rick Mugridge, the man behind &lt;a href="http://fitlibrary.sourceforge.net/" target="_BLANK"&gt;FitLibrary&lt;/a&gt; (an extension to &lt;a href="http://sourceforge.net/projects/fit/" target="_BLANK"&gt;Fit&lt;/a&gt; a means of specifying and running tests), and co-author of '&lt;a href="http://www.amazon.co.uk/FIT-Developing-Software-Framework-Integrated/dp/0321269349/ref=sr_11_1/026-2847427-0002806?ie=UTF8" target="_BLANK"&gt;Fit for Developing Software&lt;/a&gt;'.  He's a big believer in story tests and much of his work seems to be around getting the process of producing them as slick as possible.&lt;br /&gt;&lt;br /&gt;His big idea is 'Story-test Driven Development' (SDD).  That is, taking the idea of Test Driven Development (TDD) a step further and getting system requirements specified in tests as early as possible in the process.  The 'story' to which the TLA relates is the Extreme Programming idea of a story.  A single action that a user wishes to perform with a system.&lt;br /&gt;&lt;br /&gt;He proposed that writing story tests in a language that describes user interface interaction is like programming in assembler.  It has its uses, but is far too low a level for many purposes; that using a low level language can hide the underlying purpose of the test, being to test the business rules of the application with respect to the story.&lt;br /&gt;&lt;br /&gt;In producing a story test you should be talking in the domain language not a UI language.  By doing so the business rules become apparent and the vocabulary becomes clear enough to be used by business analysts, product managers, the customers.  He advocates the use of story tests as a means of the customers defining the requirements of the system.  That these can then be further refined by the developers and the test teams, but that the customers ultimately own them.&lt;br /&gt;&lt;br /&gt;Also, if the purpose of the story test is to convey information between disparate parties (in both geography and time), then concise, concrete examples are the way forward.&lt;br /&gt;&lt;br /&gt;I whole heartedly agree with the basic premise that there is a different approach to testing that can be forgotten about, namely testing the integration of objects in the domain language.  I'm just not sure it replaces the UI testing.  I'm not 100%, but I don't think that Rick was suggesting this.&lt;br /&gt;&lt;br /&gt;Also, I'm really not sure about the Fit method of showing tests in tables (&lt;a href="http://fit.c2.com/" target="_BLANK"&gt;example here&lt;/a&gt;).  I like the idea of a non text based representation, but I'm just not sure the tables really work for anything other than trivial examples.  Still, I was very impressed with his notion that tests could be described in diagrams.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: Test in the domain language, not in a UI language. If you do that you can always generate a UI test, and your tests will express the essential business rules rather than workflow.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;b&gt;Auto-test, Push button testing using contracts:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=-4914296609640869140&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next it was Andreas Leitner's turn, A PHD student working with ETH Zurich.&lt;br /&gt;&lt;br /&gt;In typical PHD style, his talk centred around &lt;a href="http://en.wikipedia.org/wiki/Design_by_Contract" target="_BLANK"&gt;Design by Contract&lt;/a&gt;.  For those that aren't familiar with the concept he described the ideas of the pre and post conditions and invariants that are apparent in such languages (Eiffel being his language of choice).  The idea is that for a given method there will be defined:&lt;br /&gt;Pre-conditions - The conditions that must be true before that method is called. &lt;br /&gt;Post-conditions – The conditions that the method guarantee will be true once the method is complete.&lt;br /&gt;Invariants – The conditions that can never be broken.&lt;br /&gt;&lt;br /&gt;The framework Andreas has put together can be used to test Eiffel classes to ensure that the post and invariant conditions are never broken. The innovative approach that this framework takes is that it does not require the developer to produce any test code.  Instead test code is generated based on a 'strategy', for which there are already a number created.&lt;br /&gt;&lt;br /&gt;The most basic of those strategies is the purely random: Create a bunch of objects, call methods on them with parameters that pass the pre-conditions, make sure the post conditions are true.  He also offers what he calls an 'Adaptive Random' strategy, where each object tested is aimed to be as different in structure from the last as possible, and AI based strategies influenced by the well understood maze solving technique of World /State / Goal definition.&lt;br /&gt;&lt;br /&gt;These tests are then intended to run for a large amount of time, unlike the traditional unit test idea of 'run as fast as you can through the interesting cases'.  This then becomes a brute force attack on the objects, attempting to break them pretty much by chance.&lt;br /&gt;On finding a failure case, the framework will then try to extract the essential nature of the test and provide you with a script that can be added as a standard unit test to your suite.  The obvious (but clever) way of checking the generated test script is valid... it runs the script again and checks it fails.&lt;br /&gt;&lt;br /&gt;The big win is the fact that this can be used to test third party libraries without having to define the tests yourself.  OK, so the intent of each method isn't really tested, merely the accuracy of the post-conditions and invariants, but that's definitely better than just taking everything on trust.&lt;br /&gt;&lt;br /&gt;He also asserted that you don't need explicit pre and post conditions in your language in order to use this technique.  Java has extensions that provide the capability, and SPEC# does something similar in the C# world. Also, it was pointed out that there are other tools doing similar things, &lt;a href="http://www.agitar.com" target="_BLANK"&gt;Agitar&lt;/a&gt; being one that takes Java classes and trying to work out how to break them.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: It's possible to produce auto generated test cases for classes as long as you have (or can infer) design by contract components and enough time.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;b&gt;Does my button look big in this?  Building Testable AJAX Applications:&lt;/b&gt; &lt;a href="http://video.google.com/videoplay?docid=4378663232897374824&amp;amp;q=label%3Altac" target="_BLANK"&gt;(Google video)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Finally some Google guys got in on the act and Adam Connors and Joe Walnes gave us a presentation on how to test AJAX apps.&lt;br /&gt;&lt;br /&gt;In reality this was a pretty generic talk illustrating the fact the any type of application in any language can be decomposed into testable components.  It quite rightly put forward the idea that the industry (or more accurately, the people in it) is still naive when it comes to writing Javascript.  Good practice goes out the window as soon as the manipulation of a Document Object Model and an XMLHTTPRequest object comes into play.&lt;br /&gt;&lt;br /&gt;But, as they demonstrated, it's not that hard to design Javascript code in a way that can be tested, it just takes a little bit of thought and a lot of discipline.&lt;br /&gt;&lt;br /&gt;Still, they did suffer the wrath of the audience when they suggested that the DOM interaction and the View code doesn't &lt;i&gt;need&lt;/i&gt; to be unit tested.  They proposed that it's OK not to unit test some components just as long as those components are as simple as possible in what they do. Contentious, but it does have merit.  I tried to suggest that the story tests can take care of that, but Joe didn't seem to want to bite!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Main message: AJAX apps are like any other type of app.  There are easy to test bits, hard to test bits and seemingly impossible to test bits.  Separate the bits and you make your life easier.  The fact that it's Javascript is no excuse for bad design.&lt;/b&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;And that was day one.  I could have gone to the pub, but in the end it was far too exhausting for me and I took my free t-shirt, fancy LED laden pen and Google notepad and skulked off home to prepare for tomorrow (and write this entry, of course).&lt;br /&gt;&lt;br /&gt;If tomorrow's as good as today, I'll be exhausted, happy and a lot richer for the experience!&lt;br /&gt;&lt;br /&gt;Update: As per the multiple requests by Google, this post is tagged: Google LATC&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115766077896176360?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115766077896176360/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115766077896176360' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115766077896176360'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115766077896176360'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/google-automated-testing-conference.html' title='Google Automated Testing Conference (London) - Day 1'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115765382658652978</id><published>2006-09-07T19:29:00.000+01:00</published><updated>2006-09-07T19:30:26.600+01:00</updated><title type='text'>Configuration is the new code</title><content type='html'>Fairly recently I was thinking about the development processes for the configuration of a large, off the shelf system.  you know the type; CRM, ERP, TLA ;), that kind of thing. All things to all people, completely generic, no need to do any development to get it just right for you business, just a bit of configuration needed.&lt;br /&gt;&lt;br /&gt;Only it's not just a bit of configuration, it's a lot of configuration.  And with the business world the way it is, it's &lt;i&gt;ongoing&lt;/i&gt; configuration much the same as it's ongoing development for every other bit of software we have.&lt;br /&gt;&lt;br /&gt;So, if we're going to have a team of people continually working on configuring this system, configuring the system is basically changing the behaviour of the system, then what differentiates it from source code?&lt;br /&gt;&lt;br /&gt;As far as I'm concerned, nothing.&lt;br /&gt;&lt;br /&gt;When the configuration of the system goes as far as it does on the particular system (and it's not alone), then the configuration of the system has to be dealt with as if it's the source code of that system.  It has to undergo the same quality checks, regression tests, audited rollout processes, version control.&lt;br /&gt;&lt;br /&gt;The particular product I was looking at has had some functionality added to support these kinds of ideas.  It has a clear migration method to get from development to test to staging to live. It supports that kind of structured, scripted rollout. But the config (development) tool can be attached straight to the live environment and be used to 'just make a quick change'. And there's nothing you can do to lock it down.&lt;br /&gt;&lt;br /&gt;The configuration all lives in a database, so you can't just simply check the configuration in and out of version control.  The development tool &lt;i&gt;does&lt;/i&gt; has some version control integration, but it doesn't allow you to branch, tag or, most importantly, revert.  Not only that, but the dev tool can be used to change any number of configuration sets, but when you flick between them the version control module you're using doesn't change.  So you can check a config from one environment into the version control module of another!&lt;br /&gt;&lt;br /&gt;So I find I have to ask the question... What's the point in having the option if it's so hopelessly crippled?&lt;br /&gt;&lt;br /&gt;My only conclusion is that there is none!&lt;br /&gt;&lt;br /&gt;Anyway, the process isn't completely doomed, there is a process that will allow us to make sure our release versions are version controlled and tagged, and therefore audited. &lt;br /&gt;&lt;br /&gt;Unfortunately, since the solution means putting a single binary (rather than multiple files) into version control we loose many of the day to day benefits of version control, like granular logs of changes and the ability to diff.  But hey, at least our process is auditable.&lt;br /&gt;&lt;br /&gt;The whole way through the examination I was told by consultants that "most people don't do this" and "I've never worked on a project where people thought version control was necessary".  Probably very true... But that's because a lot of the industry doesn't know what it's doing when it comes to software development.&lt;br /&gt;It's a big shame, because the inclusion of the migration tools and the lip service towards integrated version control points to the fact that they've &lt;i&gt;started&lt;/i&gt; to think about it.  It's just that it's not very well thought out yet.&lt;br /&gt;&lt;br /&gt;On day soon, the big players will wake up, provide the proper tools for version controlling their configurations and maybe then the rest of the industry will learn to use it.&lt;br /&gt;&lt;br /&gt;Hopefully, the Google Test conference I'm attending this week will give me some ideas on how to add automated regression tesing, and plug another gap in their toolset...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115765382658652978?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115765382658652978/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115765382658652978' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115765382658652978'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115765382658652978'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/09/configuration-is-new-code.html' title='Configuration is the new code'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115703030008048967</id><published>2006-08-31T14:02:00.000+01:00</published><updated>2006-08-31T14:18:20.176+01:00</updated><title type='text'>Well I Never - Followup 1</title><content type='html'>OK, so I've managed to grab some time during the day to experiment, and I've got things to post.  For now I've just got the time for this...&lt;br /&gt;&lt;br /&gt;Turns out that William Robertson was quite right, the TO_CHAR 'too many declarations' issue has gone away (certainly by the time it reached 9.2), and I never even noticed!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL*Plus: Release 9.2.0.1.0 - Production on Thu Aug 31 13:53:25 2006&lt;br /&gt;&lt;br /&gt;Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Connected to:&lt;br /&gt;Oracle9i Release 9.2.0.6.0 - Production&lt;br /&gt;JServer Release 9.2.0.6.0 - Production&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT TO_CHAR( 'CHARACTER' ) FROM DUAL&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;TO_CHAR('&lt;br /&gt;---------&lt;br /&gt;CHARACTER&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT TO_CHAR( NULL ) FROM DUAL&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;T&lt;br /&gt;-&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Second up (also in 9.2) the first suspicion I had was quite right... the following doesn't work.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL*Plus: Release 9.2.0.1.0 - Production on Thu Aug 31 13:42:49 2006&lt;br /&gt;&lt;br /&gt;Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Connected to:&lt;br /&gt;Oracle9i Release 9.2.0.6.0 - Production&lt;br /&gt;JServer Release 9.2.0.6.0 - Production&lt;br /&gt;&lt;br /&gt;SQL&gt; CREATE OR REPLACE PACKAGE test_pkg IS&lt;br /&gt;  2    --&lt;br /&gt;  3    FUNCTION cannot_be_overloaded RETURN NUMBER;&lt;br /&gt;  4    FUNCTION cannot_be_overloaded RETURN VARCHAR2;&lt;br /&gt;  5    --&lt;br /&gt;  6  END test_pkg;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Package created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; CREATE OR REPLACE PACKAGE BODY test_pkg IS&lt;br /&gt;  2    --&lt;br /&gt;  3    FUNCTION cannot_be_overloaded RETURN NUMBER IS&lt;br /&gt;  4    BEGIN&lt;br /&gt;  5      RETURN 0;&lt;br /&gt;  6    END cannot_be_overloaded;&lt;br /&gt;  7    --&lt;br /&gt;  8    FUNCTION cannot_be_overloaded RETURN VARCHAR2 IS&lt;br /&gt;  9    BEGIN&lt;br /&gt; 10      RETURN 'Character';&lt;br /&gt; 11    END cannot_be_overloaded;&lt;br /&gt; 12    --&lt;br /&gt; 13  END test_pkg;&lt;br /&gt; 14  /&lt;br /&gt;&lt;br /&gt;Package body created.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT test_pkg.cannot_be_overloaded FROM DUAL&lt;br /&gt;  2  /&lt;br /&gt;SELECT test_pkg.cannot_be_overloaded FROM DUAL&lt;br /&gt;       *&lt;br /&gt;ERROR at line 1:&lt;br /&gt;ORA-06553: PLS-307: too many declarations of 'CANNOT_BE_OVERLOADED' match this&lt;br /&gt;call&lt;br /&gt;&lt;br /&gt;SQL&gt; DECLARE&lt;br /&gt;  2    vn_number  NUMBER;&lt;br /&gt;  3    vc_character  VARCHAR2(100);&lt;br /&gt;  4  BEGIN&lt;br /&gt;  5    vn_number := test_pkg.cannot_be_overloaded;&lt;br /&gt;  6    vc_character := test_pkg.cannot_be_overloaded;&lt;br /&gt;  7  END;&lt;br /&gt;  8  /&lt;br /&gt;  vn_number := test_pkg.cannot_be_overloaded;&lt;br /&gt;                        *&lt;br /&gt;ERROR at line 5:&lt;br /&gt;ORA-06550: line 5, column 25:&lt;br /&gt;PLS-00307: too many declarations of 'CANNOT_BE_OVERLOADED' match this call&lt;br /&gt;ORA-06550: line 5, column 3:&lt;br /&gt;PL/SQL: Statement ignored&lt;br /&gt;ORA-06550: line 6, column 28:&lt;br /&gt;PLS-00307: too many declarations of 'CANNOT_BE_OVERLOADED' match this call&lt;br /&gt;ORA-06550: line 6, column 3:&lt;br /&gt;PL/SQL: Statement ignored&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;However, my second suspicion was off the mark (at least in 9.2).  Almost certainly this is related to the change in behaviour to TO_CHAR described above,&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SQL*Plus: Release 9.2.0.1.0 - Production on Thu Aug 31 13:45:47 2006&lt;br /&gt;&lt;br /&gt;Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Connected to:&lt;br /&gt;Oracle9i Release 9.2.0.6.0 - Production&lt;br /&gt;JServer Release 9.2.0.6.0 - Production&lt;br /&gt;&lt;br /&gt;SQL&gt; CREATE OR REPLACE PACKAGE test_pkg IS&lt;br /&gt;  2    --&lt;br /&gt;  3    FUNCTION can_be_overloaded ( pn_number   NUMBER   ) RETURN NUMBER;&lt;br /&gt;  4    FUNCTION can_be_overloaded ( pc_varchar  VARCHAR2 ) RETURN VARCHAR2;&lt;br /&gt;  5    --&lt;br /&gt;  6  END test_pkg;&lt;br /&gt;  7  /&lt;br /&gt;&lt;br /&gt;Package created.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;SQL&gt; CREATE OR REPLACE PACKAGE BODY test_pkg IS&lt;br /&gt;  2    --&lt;br /&gt;  3    FUNCTION can_be_overloaded ( pn_number   NUMBER   ) RETURN NUMBER IS&lt;br /&gt;  4    BEGIN&lt;br /&gt;  5      RETURN pn_number;&lt;br /&gt;  6    END can_be_overloaded;&lt;br /&gt;  7    --&lt;br /&gt;  8    FUNCTION can_be_overloaded ( pc_varchar  VARCHAR2 ) RETURN VARCHAR2 IS&lt;br /&gt;  9    BEGIN&lt;br /&gt; 10      RETURN pc_varchar;&lt;br /&gt; 11    END can_be_overloaded;&lt;br /&gt; 12    --&lt;br /&gt; 13  END test_pkg;&lt;br /&gt; 14  /&lt;br /&gt;&lt;br /&gt;Package body created.&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT test_pkg.can_be_overloaded( 0 ) FROM DUAL&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;TEST_PKG.CAN_BE_OVERLOADED(0)&lt;br /&gt;-----------------------------&lt;br /&gt;                            0&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT test_pkg.can_be_overloaded( 'WORD' ) FROM DUAL&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;TEST_PKG.CAN_BE_OVERLOADED('WORD')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;WORD&lt;br /&gt;&lt;br /&gt;SQL&gt; SELECT test_pkg.can_be_overloaded( '100' ) FROM DUAL&lt;br /&gt;  2  /&lt;br /&gt;&lt;br /&gt;TEST_PKG.CAN_BE_OVERLOADED('100')&lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;100&lt;br /&gt;&lt;br /&gt;SQL&gt; DECLARE&lt;br /&gt;  2    vn_number     NUMBER;&lt;br /&gt;  3    vc_character  VARCHAR2(100);&lt;br /&gt;  4  BEGIN&lt;br /&gt;  5    vn_number     := test_pkg.can_be_overloaded( 0 );&lt;br /&gt;  6    vc_character  := test_pkg.can_be_overloaded( 'WORD' );&lt;br /&gt;  7    vc_character  := test_pkg.can_be_overloaded( '0' );&lt;br /&gt;  8    vn_number     := test_pkg.can_be_overloaded( TO_NUMBER( '0' ) );&lt;br /&gt;  9    vn_number     := test_pkg.can_be_overloaded( TO_CHAR( 0 ) );&lt;br /&gt; 10  END;&lt;br /&gt; 11  /&lt;br /&gt;&lt;br /&gt;PL/SQL procedure successfully completed.&lt;br /&gt;&lt;br /&gt;SQL&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Cheers to everyone who commented on the last post... it's led me to check out a few things that I might not have bothered with and I reckon I'll be looking a little deeper in the next few days.  Contrived examples of where named parameter notation could go wrong are called for I think ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115703030008048967?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115703030008048967/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115703030008048967' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115703030008048967'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115703030008048967'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/08/well-i-never-followup-1.html' title='Well I Never - Followup 1'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115688719731720584</id><published>2006-08-29T22:27:00.000+01:00</published><updated>2006-08-29T22:49:42.310+01:00</updated><title type='text'>Well I never</title><content type='html'>Good to be reminded that there's always something that you don't already know.  And that's especially true of Oracle.  I'd never suspected that some of those things are pretty fundamental, like the fact that package functions and procedures can be overloaded!  I'd always assumed that since standalone functions and procedures can't, that the same was true of packages.  Turns out that assumption was all wrong...&lt;br /&gt;&lt;br /&gt;I.E.&lt;br /&gt;This doesn't work:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE FUNCTION cannot_be_overloaded RETURN NUMBER IS&lt;br /&gt;BEGIN&lt;br /&gt;  RETURN 0;&lt;br /&gt;END cannot_be_overloaded;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE FUNCTION cannot_be_overloaded ( pc_varchar  VARCHAR2 ) RETURN VARCHAR2 IS&lt;br /&gt;BEGIN&lt;br /&gt;  RETURN pc_varchar;&lt;br /&gt;END cannot_be_overloaded;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But this does!&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE test_pkg IS&lt;br /&gt;  --&lt;br /&gt;  FUNCTION can_be_overloaded RETURN NUMBER;&lt;br /&gt;  FUNCTION can_be_overloaded ( pc_varchar  VARCHAR2 ) RETURN VARCHAR2;&lt;br /&gt;  --&lt;br /&gt;END test_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE BODY test_pkg IS&lt;br /&gt;  --&lt;br /&gt;  FUNCTION can_be_overloaded RETURN NUMBER IS&lt;br /&gt;  BEGIN&lt;br /&gt;    RETURN 0;&lt;br /&gt;  END can_be_overloaded;&lt;br /&gt;  --&lt;br /&gt;  FUNCTION can_be_overloaded ( pc_varchar  VARCHAR2 ) RETURN VARCHAR2 IS&lt;br /&gt;  BEGIN&lt;br /&gt;    RETURN pc_varchar;&lt;br /&gt;  END can_be_overloaded;&lt;br /&gt;  --&lt;br /&gt;END test_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'm sure there are gotchas in there, and I'm not &lt;i&gt;really&lt;/i&gt; sure it's actually that useful (I've gone 8 years without it ;-) ), but still... how did I miss it?  What else have I missed?&lt;br /&gt;&lt;br /&gt;Update - aside: Why does Oracle allow overloading in the package, but not with standalone.  I'm guessing, but probably because not allowing the standalone overload makes things like the 'DROP PROCEDURE' command a lot simpler to use (care to specify which procedure with that name to drop?)&lt;br /&gt;Probably because allowing the package procedures to be overloaded seemed like a god idea to someone in Oracle ;-o&lt;br /&gt;&lt;br /&gt;Update: Just re-reading the bulk of the post... and now I'm blogging something that I've not tested (no Oracle at home).  But I reckon that the following won't work:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE test_pkg IS&lt;br /&gt;  --&lt;br /&gt;  FUNCTION cannot_be_overloaded RETURN NUMBER;&lt;br /&gt;  FUNCTION cannot_be_overloaded RETURN VARCHAR2;&lt;br /&gt;  --&lt;br /&gt;END test_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE BODY test_pkg IS&lt;br /&gt;  --&lt;br /&gt;  FUNCTION cannot_be_overloaded RETURN NUMBER IS&lt;br /&gt;  BEGIN&lt;br /&gt;    RETURN 0;&lt;br /&gt;  END cannot_be_overloaded;&lt;br /&gt;  --&lt;br /&gt;  FUNCTION cannot_be_overloaded RETURN VARCHAR2 IS&lt;br /&gt;  BEGIN&lt;br /&gt;    RETURN 'Character';&lt;br /&gt;  END cannot_be_overloaded;&lt;br /&gt;  --&lt;br /&gt;END test_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Any calls to the functions would be ambiguous.  Surely Oracle can't choose which one to use based on the type of the variable you're going to hold the value in... that would be a nightmare bit of compiler to implement.  No no no!&lt;br /&gt;&lt;br /&gt;Also, I reckon you'd have to take care with this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE test_pkg IS&lt;br /&gt;  --&lt;br /&gt;  FUNCTION can_be_overloaded ( pn_number   NUMBER   ) RETURN NUMBER;&lt;br /&gt;  FUNCTION can_be_overloaded ( pc_varchar  VARCHAR2 ) RETURN VARCHAR2;&lt;br /&gt;  --&lt;br /&gt;END test_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE BODY test_pkg IS&lt;br /&gt;  --&lt;br /&gt;  FUNCTION can_be_overloaded ( pn_number   NUMBER   ) RETURN NUMBER IS&lt;br /&gt;  BEGIN&lt;br /&gt;    RETURN pn_number;&lt;br /&gt;  END can_be_overloaded;&lt;br /&gt;  --&lt;br /&gt;  FUNCTION can_be_overloaded ( pc_varchar  VARCHAR2 ) RETURN VARCHAR2 IS&lt;br /&gt;  BEGIN&lt;br /&gt;    RETURN pc_varchar;&lt;br /&gt;  END can_be_overloaded;&lt;br /&gt;  --&lt;br /&gt;END test_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Even if the above would compile (I don't know if it would), then if you were to call the above with:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  test_pkg.can_be_overloaded ( '100' );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I &lt;i&gt;suspect&lt;/i&gt; that Oracle will throw a wobbler.  The parameter passed could be treated as a number or a varchar2, meaning either function could be valid for passing.&lt;br /&gt;The only reason I suspect the package would compile is that the call could become non-ambiguous with:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  test_pkg.can_be_overloaded ( pc_varchar =&gt; '100' );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I can see how this could get very knotty, positional parameters could make all kinds of function calls ambiguous.&lt;br /&gt;Reckon I might try some experiments tomorrow...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115688719731720584?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115688719731720584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115688719731720584' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115688719731720584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115688719731720584'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/08/well-i-never.html' title='Well I never'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115383251811828815</id><published>2006-07-25T13:46:00.000+01:00</published><updated>2006-09-13T17:42:23.513+01:00</updated><title type='text'>Version Control and the Patch Runner</title><content type='html'>Commenting on my first post on &lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner.html" target="_BLANK"&gt;upgrading databases&lt;/a&gt;, someone with the moniker 'gonen' asked a question:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;"How do you handle the situation where one developer is checking in code that doesn't have to be released in current release?"&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;It's a good question, but it's a question mainly of version control rather than the patch runner itself.  In fact, whilst the question was asked against the database patch runner post, it's interesting that the question doesn't mention the word 'patch', it uses the word 'code'.  This is a general question that relates to any code, documentation, or other product of software development.&lt;br /&gt;&lt;br /&gt;But, before I answer the question I'm going to turn it round a little and ask:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;'Why do you have code that you want in version control that you don't want in the next release?'.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I can see three main answers to this one:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1 - The code is useless and so there's no reason to ever release it.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This is probably the most unlikely answer, but hey, it happens.&lt;br /&gt;If this is the case, why are you keeping it? Commonly such code is kept because "it might be useful in the future and I don't want to throw it away".&lt;br /&gt;Well, is it &lt;i&gt;really&lt;/i&gt; likely to be useful?  If it is historic code that is already is version control, then with most VC you can remove it and then still get it back at a later date if you need to.  &lt;i&gt;Proper&lt;/i&gt; version control software will record the fact that the file was deleted at a particular point in time but make sure the version exists in the historic versions.  Otherwise you wouldn't be able to re-release only versions of your software that requires that code.&lt;br /&gt;If it's new code that's not used anywhere, isn't yet in version control and isn't currently useful... why on earth would you want that in your system?  It'll likely never get used, and merely hang around for years to come by which time people will be confused by existence but too scared to delete it.&lt;br /&gt;The answer:  If you don't need it, don't check it in.  If it's already checked in, remove it.  In either case, delete it, you don't need it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2 - You're trying to fix a bug in the live system, but since you've made the original live release you've done a load more development. You're not ready to release the new stuff to live yet.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Or to put it another way.  There was a live version of the system was released and since then Alex has been working on bug fixes.  At the same time Ben's working on the new release and he new funky functionality.  A few weeks in and Ben's been checking in his new stuff, but hasn't quite finished it.  Alex gets given a critical bug to fix that &lt;b&gt;must&lt;/b&gt; ship to the customer as soon as it's fixed.  She can't commit and release the head of the trunk because it contains Ben's half finished stuff.&lt;br /&gt;&lt;br /&gt;Ok. So when you made the live release you tagged up the release version in your version control didn't you.  If not, you can always go back and do it, you just need to know the date you cut the release.  And from now on you'll tag up everything that you release right?&lt;br /&gt;&lt;br /&gt;The tagged version is the version you want to make your live bug fixes against, not the version at the head of the trunk. When you release a bug fix version you &lt;b&gt;only&lt;/b&gt; want the bug fixes to be released, never the new functionality&lt;br /&gt;&lt;br /&gt;To do this, you create a branch from the tag and do your bug fixing there.  On that branch you now have your live version plus your bug fix.  This branch doesn't contain any of the changes that you've made in the trunk since the live version was released.  Of course, whenever you make a bug fix in the branch you make sure that the bug fixed is made in the trunk version as well, otherwise when your customer comes off the branch the bug will reappear. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;3 - You have several people working on code at the same time, some on short term work, some on longer term stuff.  Therefore have incomplete work you don't want to release yet.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;For example, Alice has a long running bit of work and has been checking code into VC as she goes along. She hasn't really finished the job yet. Ben has been doing the same, but has managed to complete his.  The trunk now contains a mixture of complete and incomplete work.&lt;br /&gt;&lt;br /&gt;If you have several streams of concurrent long term development and often get to points where you need to release one stream without releasing another then branches can help again.&lt;br /&gt;Rather than allow Alice and Ben to work directly on the trunk, give them a branch each.  Known as 'task branches', each branch exists purely for the length of that task.  Once the task is complete the developer merges their branch into the trunk and then discards the branch.  When the next long term task arrives another branch is created and the work for that task done there&lt;br /&gt;&lt;br /&gt;Going back to the example above:&lt;br /&gt;Alice and Ben work on their own branches.  As Ben finishes his work and commits into his branch we have the following situation:  Ben's work is complete on his branch. Alice's incomplete work is on her branch.  The trunk doesn't contain either Alice or Ben's work.&lt;br /&gt;Now Ben's finished his work on the branch he merges it onto the trunk, effectively promoting it into the general release. Ben's task branch can now be discarded.  The trunk can now be released to the customer.&lt;br /&gt;&lt;br /&gt;Note that if Alice had finished her work first, then her work hits the trunk and the customer release can contain that bit of functionality instead of Ben's.  It is not important which bit of work is finished first, which branch is discarded first.  There is flexibility inherent in the system.&lt;br /&gt;&lt;br /&gt;However, in this situation there may be another underlying problem.  Ask yourself the question, would it be possible and be more efficient to have a single stream of work that gets completed quickly rather than multiple streams that each take time?  That could engender a team attitude as well as focus the developers, testers, project managers, and business as a whole on a single task at a time. Yep, I know, that's not always possible...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;So what about the patch runner then?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The patch runner can work very nicely in this branching structure, just as long as it's coded to deal with the fact that patches should only ever be installed once.&lt;br /&gt;&lt;br /&gt;To quote myself, from that earlier post:&lt;br /&gt;&lt;div style="background-color:#EEC; border: 1px solid black; padding: 5px" &gt;So, in summary, you check out a given tagged version of the application to run against an arbitrary database and run the patch runner. It loads the list of patches that are applicable for that tagged version. It runs over each patch in turn and checks if it has previously ran. If it has not, then it runs the patch.&lt;br /&gt;By the time it reaches the end of the list it has ran all the patches, none of them twice. You can even run the patch runner twice in succession and guarantee that the second run will not change the state of the database.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Also, if the answer is in version control then the patch runner, the patch list and all the patches themselves need to be in version control.  If they're not, then the fact that version control can answer your original question is itself a great reason for putting version control in place.  Did you know that &lt;a href="http://www.nongnu.org/cvs/" target="_BLANK"&gt;CVS&lt;/a&gt; and &lt;a href="http://subversion.tigris.org/project_packages.html" target="_BLANK"&gt;Subversion&lt;/a&gt; are both free... as is Tortoise (&lt;a href="http://www.tortoisecvs.org/download.shtml" target="_BLANK"&gt;CVS&lt;/a&gt; and &lt;a href="http://tortoisesvn.tigris.org/" target="_BLANK"&gt;SVN&lt;/a&gt;). Download and install one of them, it'll take you all of 15 minutes to get a local version up and running and experimenting with.&lt;br /&gt;&lt;br /&gt;Finally, if you want more information on version control then there are three books out there that you really should read.  Well, you should read two out of three of them anyway...&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.amazon.co.uk/gp/product/0201741172/202-9065696-8830259?v=glance&amp;amp;n=266239&amp;amp;s=gateway&amp;amp;v=glance" target="_BLANK"&gt;Software Configuration Patterns - Steve Berczuk and Brad Appleton&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.amazon.co.uk/gp/product/0977616657/202-9065696-8830259?v=glance&amp;amp;n=266239&amp;amp;s=gateway&amp;amp;v=glance" target="_BLANK"&gt;Pragmatic Version Control using Subversion - Mike Mason&lt;/a&gt;&lt;br /&gt;or&lt;br /&gt;&lt;a href="http://www.amazon.co.uk/gp/product/0974514004/202-9065696-8830259?v=glance&amp;amp;n=266239&amp;amp;v=glance" target="_BLANK"&gt;Pragmatic Version Control using CVS - Dave Thomas&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And if you want more blog posts on version control then your man is &lt;a href="http://mikemason.ca/" target="_BLANK"&gt;Mike Mason&lt;/a&gt;... &lt;b&gt;The&lt;/b&gt; go to guy on using Subversion in the real world. &lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/CVS" rel="tag"&gt;CVS&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/SVN" rel="tag"&gt;SVN&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/version+control" rel="tag"&gt;version+control&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrading" rel="tag"&gt;upgrading&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/programming" rel="tag"&gt;programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/DB" rel="tag"&gt;DB&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/SQL" rel="tag"&gt;SQL&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/tortoise" rel="tag"&gt;tortoise&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115383251811828815?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115383251811828815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115383251811828815' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115383251811828815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115383251811828815'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/07/version-control-and-patch-runner.html' title='Version Control and the Patch Runner'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115201870404176894</id><published>2006-07-04T13:57:00.000+01:00</published><updated>2006-07-04T14:11:44.060+01:00</updated><title type='text'>Merge triggers</title><content type='html'>Had a quick puzzler at work today that I thought I'd share... and the link to &lt;a href="http://asktom.oracle.com" target="_BLANK"&gt;Ask Tom&lt;/a&gt; that solved it (before I could be bothered to delve into the test case myself).&lt;br /&gt;&lt;br /&gt;Q: Which statement level triggers fires on a &lt;a href="http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96540/statements_915a.htm#SQLRF01606" target="_BLANK"&gt;MERGE&lt;/a&gt; statement?&lt;br /&gt;&lt;br /&gt;A: Since it is possible for either inserts or updates to occur during the statement, and since statement level triggers always fire even when no rows are processed, both the INSERT and UPDATE triggers fire every time, regardless of whether any inserts or updates occur.&lt;br /&gt;&lt;br /&gt;Obvious when you think about it.  Trouble is, how many our YOUR statement level triggers would work properly if both sets fired at the same time?  There's no reason why they shouldn't, if you code for the possibility....&lt;br /&gt;&lt;br /&gt;Tom (and Mikito and Kevin and others) explains &lt;a href="http://asktom.oracle.com/pls/ask/f?p=4950:8:3485730494385186442::::F4950_P8_DISPLAYID:25733900083512" target="_BLANK"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115201870404176894?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115201870404176894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115201870404176894' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115201870404176894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115201870404176894'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/07/merge-triggers.html' title='Merge triggers'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-115158618005474770</id><published>2006-06-29T13:55:00.000+01:00</published><updated>2006-06-29T14:03:00.076+01:00</updated><title type='text'>World Cup Trivia</title><content type='html'>Well, I'm finally back from my honeymoon, and with the wedding out of the way I may finally get some time to write blog entries again.  But wait, hang on, it seems like there's a World Cup (footy that is) to take up almost all of my evenings.  Ah well, no blogging just yet then!&lt;br /&gt;&lt;br /&gt;Still, there's no matches on at the moment (not until Friday anyway) and so to quench my need for football I decided to go on a trivia hunt. And thus was born Honest Bob's World Cup Trivia facts... I can't guarantee the accuracy of any of these facts as all were researched on the internet ;-)&lt;br /&gt;&lt;br /&gt;Of the 17 events (prior to Germany 2006), the hosts have won 6 times, with only Sweden being a losing host finalist (1958, losing to Brazil).&lt;br /&gt;&lt;br /&gt;Argentina and Brazil are the only countries to win outside of their own continent.  Brazil have managed to win on every continent the competition has been played:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Asia: South Korea / Japan (2002)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;North America: USA (1994), Mexico (1970)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;South America: Chile (1962)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Europe: Sweden (1958)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Not only that, but Brazil have managed to qualify for every world cup tournament.&lt;br /&gt;&lt;br /&gt;However, they are the only team that have won the competition to have NOT won it as hosts.&lt;br /&gt;&lt;br /&gt;Although a German team have won the competition 3 times, they've never managed it as a combined Germany, only ever as West Germany.&lt;br /&gt;&lt;br /&gt;In total only 7 teams have won the competition out of 207 countries who have competed for qualification, and 78 who have made it to the world cup proper.  The 7 winners are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Brazil (5 times)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Italy (3 times)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;West Germany (3 times)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Uruguay (2 times)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Argentina (2 times)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;England (once)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;France (once)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The competition has been utterly dominated by European and South American teams, with only two teams outside of the two continents having made it to the semi-final stages.  USA (1994) and South Korea (2002) both being hosts when they managed it.&lt;br /&gt;The fastest goal in a world cup match was scored by Hakan Sukur (Turkey), 11 seconds after kick off against South Korea in the 2002 tournament.&lt;br /&gt;&lt;br /&gt;However, that time is beaten when you also take into account the qualifying matches.  David Gualtieri (San Marino) scored after 8 seconds against England in their ill-fated qualification attempt for the 1994 Finals.  England needed to win by 7 clear goals and have Holland lose their match against Poland. England only managed to win 7-1, though it mattered little as Holland eventually won their game 3-1.&lt;br /&gt;&lt;br /&gt;In the whole of that qualification group (10 games) San Marino managed to score only one other goal and conceded 46.&lt;br /&gt;&lt;br /&gt;The 1950 World Cup was not decided by a final.  Rather it was a league contested by 4 teams, with the match between Uruguay and Brazil (2-1) being the decisive match and therefore generally regarded as 'the final'.&lt;br /&gt;&lt;br /&gt;The only person to have played both World Cup Football and World Cup Cricket is Viv Richards - West Indies at cricket (obviously) and for Antigua in their 1974 World Cup Qualifying matches, which ultimately ended in failure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-115158618005474770?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/115158618005474770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=115158618005474770' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115158618005474770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/115158618005474770'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/06/world-cup-trivia.html' title='World Cup Trivia'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114855703668774457</id><published>2006-05-25T12:32:00.000+01:00</published><updated>2006-05-26T09:27:09.243+01:00</updated><title type='text'>Best Practice?</title><content type='html'>Yeah yeah, it's been very quiet on the Bob front recently... I have an excuse though, it's only a week and a half to my wedding, so I've been a busy busy boy.  Plus I've got the task of completely rewriting our development manual, so that's taking all of my creative juices.  I'm running on empty.&lt;br /&gt;&lt;br /&gt;But, when you get sent a link like this, you just have to share:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If you're anything like me, you'll absolutely hate the phrase 'Best Practice'.  I cringe every time I hear it.  It suggests to me that the speaker has stopped learning... "This is the best practice there could possibly be, so there's no point in trying to improve it".  In addition it has the connotation that "This is the best practice for all situations, whatever it may be".  Sorry, but that just doesn't work.&lt;br /&gt;&lt;br /&gt;So anyway, a site that calls itself &lt;a href="http://www.fairlygoodpractices.com"&gt;Fairly Good Practices&lt;/a&gt; was always going to pique my interest...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114855703668774457?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114855703668774457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114855703668774457' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114855703668774457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114855703668774457'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/05/best-practice.html' title='Best Practice?'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114590547313593787</id><published>2006-04-24T19:55:00.000+01:00</published><updated>2006-04-24T21:41:47.900+01:00</updated><title type='text'>In my spare time I contribute to an OS CMS...</title><content type='html'>Whenever I see a CV cross my desk containing the words 'Open Source Content Management System', I shiver. Visibly.&lt;br /&gt;&lt;br /&gt;So what was I supposed to do with the news that my manager had decided to write one?&lt;br /&gt;&lt;br /&gt;Well, it turns out that I needed to take a look at the &lt;a href="http://www.stuntfart.com/wiki/McWiki.php" target="_BLANK"&gt;home page&lt;/a&gt;, then take a &lt;a href="http://www.stuntfart.com/wiki/McWiki.php?page=Main.Download" target="_BLANK"&gt;look at then code&lt;/a&gt;, and finally decide that it's not actually that bad.&lt;br /&gt;&lt;br /&gt;Obviously, the last thing the world needs is yet another Wiki, but as it's actually very very lightweight, looks ludicrously easy to set up, and is very well structured, then why not make space for just one more?  &lt;br /&gt;&lt;br /&gt;Obviously, he needs a slap for not writing unit tests, but the &lt;a href="http://www.stuntfart.com/wiki/McWiki.php?page=Main.Design Concept"&gt;plug in authentication and storage classes&lt;/a&gt; are spot on.&lt;br /&gt;&lt;br /&gt;In fact, I'd like to present it as an example of how PHP code &lt;i&gt;can&lt;/i&gt; look when its &lt;i&gt;not&lt;/i&gt; produced by an idiot.  In addition, the CSS driven layout is a great example of content and presentation separation.  All in all it's starting from a very nice position.  Hopfeully it'll continue in the same way.&lt;br /&gt;&lt;br /&gt;There's the odd little bit of refactoring to do, but once it's on sourceforge or freshmeat I'll help him with that...&lt;br /&gt;&lt;br /&gt;Rest assured, it won't appear on my CV...&lt;br /&gt;&lt;br /&gt;P.S. Momentous occasion:  This my 100th blog entry&lt;br /&gt;P.S.S Momentous occasion 2: It's also my boss's 30th birthday.  Don't you hate it when your boss is younger than you!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114590547313593787?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114590547313593787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114590547313593787' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114590547313593787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114590547313593787'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/in-my-spare-time-i-contribute-to-os.html' title='In my spare time I contribute to an OS CMS...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114553759871813583</id><published>2006-04-20T13:46:00.000+01:00</published><updated>2006-04-20T13:53:18.746+01:00</updated><title type='text'>Auto-increment in Oracle</title><content type='html'>I'm sure that 100s of references exist all over the web for this, but someone asked me today how to do this, and so it's trivial for me to add it onto this blog...&lt;br /&gt;&lt;br /&gt;In MySql you have an 'auto-increment' column.  Does Oracle have one, and if not, how do you implement one in Oracle?&lt;br /&gt;&lt;br /&gt;Well, we tend to say it's best to wrap up your INSERT statements in procedures and then manually grab the sequence number yourself... but if not:&lt;br /&gt;&lt;br /&gt;You might notice the rather useful RETURNING clause in the insert statements.  This bit seems to be missing from most internet examples I found...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CREATE SEQUENCE rob_tmp_seq&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE TABLE rob_tmp_tab ( id NUMBER, descr VARCHAR2(200) )&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;ALTER TABLE rob_tmp_tab ADD CONSTRAINT rob_tmp_tab_pk PRIMARY KEY ( id )&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE TRIGGER rob_tmp_tab_trig BEFORE INSERT ON rob_tmp_tab FOR EACH ROW&lt;br /&gt;DECLARE&lt;br /&gt;BEGIN&lt;br /&gt;  --&lt;br /&gt;  IF :new.id IS NULL THEN&lt;br /&gt;    SELECT rob_tmp_seq.NEXTVAL&lt;br /&gt;    INTO   :new.id&lt;br /&gt;    FROM   DUAL;&lt;br /&gt;  END IF;    &lt;br /&gt;  --&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;SET SERVEROUTPUT ON SIZE 1000000&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;  --&lt;br /&gt;  vn_number   NUMBER;&lt;br /&gt;  --&lt;br /&gt;BEGIN&lt;br /&gt;  --&lt;br /&gt;  INSERT INTO rob_tmp_tab( descr )&lt;br /&gt;  VALUES                 ( 'This is a description' )&lt;br /&gt;  RETURNING id INTO vn_number;&lt;br /&gt;  --&lt;br /&gt;  DBMS_OUTPUT.PUT_LINE( 'Created a record with the automatically assigned ID: ' || vn_number );&lt;br /&gt;  --&lt;br /&gt;  INSERT INTO rob_tmp_tab( id, descr )&lt;br /&gt;  VALUES                 ( rob_tmp_seq.NEXTVAL, 'This is a description' )&lt;br /&gt;  RETURNING id INTO vn_number;&lt;br /&gt;  --&lt;br /&gt;  DBMS_OUTPUT.PUT_LINE( 'Created a record with the ID grabbed from sequence manually ID: ' || vn_number );&lt;br /&gt;  --&lt;br /&gt;  INSERT INTO rob_tmp_tab( id, descr )&lt;br /&gt;  VALUES                 ( 150, 'This is a description' )&lt;br /&gt;  RETURNING id INTO vn_number;&lt;br /&gt;  --&lt;br /&gt;  DBMS_OUTPUT.PUT_LINE( 'Created a record with the ID specified manually ID: ' || vn_number );&lt;br /&gt;  --&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114553759871813583?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114553759871813583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114553759871813583' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114553759871813583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114553759871813583'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/auto-increment-in-oracle.html' title='Auto-increment in Oracle'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114516912489267039</id><published>2006-04-16T07:28:00.000+01:00</published><updated>2006-04-16T13:35:03.663+01:00</updated><title type='text'>Measuring Performance</title><content type='html'>A couple of months ago we started to get reports into our service desk that the larger of our  databases were suffering from performance problems.  Nothing catastrophic, just that the speed of the applications accessing those databases were starting to slow down.&lt;br /&gt;&lt;br /&gt;As we'd added new versions of our system into the live environment fairly recently, the development team was asked to join into the discussion of how to solve the problem.  This was a little unusual, as we don't normally get involved in the support of the live systems... be that firefighting or strategic support.  And it wasn't that our applications were under suspicion, it was just that it made political sense to get us involved.&lt;br /&gt;&lt;br /&gt;Rather than jump straight into the database, we took a holistic look at the performance of the applications, taking in as many components as we could: from client machines, through network infrastructure down to individual datafiles on the database server.  In the end we came to the usual, almost inevitable conclusion... the database was the bottleneck, and it wasn't any single aspect of the database.&lt;br /&gt;&lt;br /&gt;One thing we did notice however, was that the memory settings for the database server appeared to be unusual.  So we advised to the DBA that these be looked at straight away.  The DBA's suggested approach to this was &lt;br /&gt; "Change the settings one at a time on live, if it doesn't break the system then we're probably on the right lines.  Because it's dangerous doing this, we can only make one change per day and then wait for any calls on the service desk before we make the next change"&lt;br /&gt;&lt;br /&gt;The rest of us didn't think that sounded like a safe way to go, so I suggested a new approach.&lt;br /&gt;&lt;br /&gt;I put forward the idea of taking a production quality server that we could run in isolation, and importing the database owned by the brand that had the most urgent problems.  We would then write an application that would stress that database in line with the behaviour of the live system, we would monitor the system's peroformance during the test and record our findings.  I argued that once we had that in place we could produce a benchmark, make a change and rerun the test.  This would then tell us how the system's performance characteristics had changed.  We could then make the next proposed change, run the test again.  Then make another change...&lt;br /&gt;&lt;br /&gt;Having this tool in place would mean that we could thoroughly test the performance of the system in relation to our changes without ever having to rollout a change into a live environment.  Once we were sure that we had made a step change in the performance of the system we could roll that change onto the live system reasonably safe in the knowledge that we would always have a positive impact on the system.&lt;br /&gt;&lt;br /&gt;The rest of the team agreed and we set about producing our performance test rig.&lt;br /&gt;&lt;br /&gt;Our requirements were:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The test should be completely repeatable.  Meaning that, as far as is reasonably possible, the test should perform the same queries, at the same time, with the same parameters, every time the test runs.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When the test is running, the database being tested should appear to the DBA like the live version of the system under a particularly high level of stress.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The connections to the database should be made in the same style as the live system.  E.g. emulations of connections from a web application should appear, make their requests and then disappear.   Connections from a client machine application should appear at the start of the test and then remain connected for the duration of the test.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It should be easy to increase / decrease / change the characteristics of the test load.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It should be possible to run the test by entering a single command into a suitably set-up machine.  It should then be possible to re-run the test by entering the same single command.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Running the test should include reporting on the performance characteristics.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I hope to go into the design of the solution in another blog entry soon...&lt;br /&gt;&lt;br /&gt;We've since put the performance test rig together, and using it we managed to very closely replicate what was hapenning on live.  Each test takes an hour to run and emulates a load equal to around double to the normal live load.  We ran several test runs, each time changing a single memory setting until such a time as we had a decent change in performance.&lt;br /&gt;&lt;br /&gt;We measured the performance differences by checking the average, minimum and maximum run times of each of our sets of emulation queries (we had a set for each of our applications, sometimes a set for each module in the application).  The test rig also produces two stats pack snashots for each test, one at the start and another at the end.  All the test results are then copied into a seperate database for long term storage and reporting.&lt;br /&gt;&lt;br /&gt;Once we had the test rig up and running it took a day to tune the memory settings for our biggest database.  We were able to run 5 tests in that day and end up with a very clear picture of the effects our changes would make on the live system.&lt;br /&gt;&lt;br /&gt;The changes involved slashing the shared pool to less than a quarter its original size, taking the PGA target up by a factor of four and doubling the buffer cache.  We then rolled the complete set of changes out onto the live database in one go.&lt;br /&gt;&lt;br /&gt;It more than doubled the overall performance of the database.  Exactly as predicted.&lt;br /&gt;&lt;br /&gt;Since then the performance test rig has been earmarked for a few other jobs:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Balancing our read : write ratio on the local disk controllers.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Testing and tuning a Network Attached Storage device as a replacement for local disks.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Checking the impact of adding new indexes on heavily used areas of the system.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Investigation into possible designs for a new realtime "MIS light" application module.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Testing if regular defragmentation of our DBs give us an increase in performance.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Examination of how block sizes affect the performance of the system.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Basically, because the test rig properly emulates what happens on the live system, it can be used to investigate &lt;b&gt;any&lt;/b&gt; change that would impact the performance of the system, be that hardware or software.  And it allows us to make that investigation quickly and without any impact on any live system.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114516912489267039?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114516912489267039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114516912489267039' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114516912489267039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114516912489267039'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/measuring-performance.html' title='Measuring Performance'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114503747960509644</id><published>2006-04-14T18:50:00.000+01:00</published><updated>2006-04-14T18:57:59.606+01:00</updated><title type='text'>New Agile DB Blog on the block</title><content type='html'>I've just spotted a few new links into this site, and (as always) I followed them back... to &lt;a href="http://www.mayocchi.com/blog/" target="_BLANK"&gt;here&lt;/a&gt;. Warren Mayocchi's blog looks like its been around for a while, but the author's just on a new journey to solve the Agile DB release problem.  He's definitely thinking along some very sound lines and I for one am very interested to find out where the journey takes him.&lt;br /&gt;&lt;br /&gt;Good luck fellow pilgrim!&lt;br /&gt;&lt;br /&gt;PS. Honestly, he's not getting a plug JUST because he linked to me ;-)&lt;br /&gt;PSS. I like the 100 word fiction.  Warren, just us some more!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114503747960509644?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114503747960509644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114503747960509644' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114503747960509644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114503747960509644'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/new-agile-db-blog-on-block.html' title='New Agile DB Blog on the block'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114503658814235834</id><published>2006-04-14T18:36:00.000+01:00</published><updated>2006-04-14T18:43:08.163+01:00</updated><title type='text'>Pretty Print</title><content type='html'>In my current pilgrimage towards blog template beauty, I've now added CSS to format the page nicely for printing.  Try the print preview and you'll see what I mean.&lt;br /&gt;&lt;br /&gt;It turns out it's nice and easy... just add a media="screen" to your on screen CSS definition and add a media="print" to your paper one.  Et Viola!&lt;br /&gt;&lt;br /&gt;As with the javascript additions, all the formatting and suchlike that I've added has been added inline.  That way you guys can take a gander if you want.&lt;br /&gt;&lt;br /&gt;At some point soon the whole template is going to get a refactor to simplify it, maybe make it a bit more readable... just hang on in there for now!&lt;br /&gt;&lt;br /&gt;Oh, and if anyone has any good idea for tracking sites, I'm getting a bit sick of going to three different places for my full set of tracking needs!&lt;br /&gt;&lt;br /&gt;Brief question:  When will Blogger.com's dictionary include the word blog?&lt;br /&gt;&lt;br /&gt;It's a rhetorical question, AND I DEMAND AN ANSWER!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114503658814235834?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114503658814235834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114503658814235834' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114503658814235834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114503658814235834'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/pretty-print.html' title='Pretty Print'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114484910535277092</id><published>2006-04-12T14:33:00.000+01:00</published><updated>2006-04-12T14:38:25.400+01:00</updated><title type='text'>Attack of the Javascript</title><content type='html'>Recently I've been wondering just what kind of lightweight javascript stuff can be added to the blog in order to make it "look kinda cool".&lt;br /&gt;&lt;br /&gt;I think I've decided to do it because we spend all day here building systems that users actually want rather than just following our own flights of fancy and doing what we think might be cool.&lt;br /&gt;&lt;br /&gt;Anyway, from now on you're likely to see little new additions to the site that I like.  Let me know if you like it, or don't like it, or don't care.&lt;br /&gt;&lt;br /&gt;I'll try to make sure things fail gracefully when they do... but I'm almost certainly going to mess up someone's web experience.  If I do, then let me know and I'll fix it.  It's the only way I'll learn, and the only way it'll get fixed.&lt;br /&gt;&lt;br /&gt;Anyway, for now... have folding sidebar panels.&lt;br /&gt;&lt;br /&gt;Woooohoooo&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114484910535277092?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114484910535277092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114484910535277092' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114484910535277092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114484910535277092'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/attack-of-javascript.html' title='Attack of the Javascript'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114435792034633044</id><published>2006-04-06T22:09:00.000+01:00</published><updated>2006-04-06T22:12:00.386+01:00</updated><title type='text'>On Cloud Nine...</title><content type='html'>I'm a Boro fan, I don't know how many times I've been asked what it is about football that gets me and so many people excited.&lt;br /&gt;&lt;br /&gt;If you'd have been a Boro fan watching the &lt;a href="http://news.bbc.co.uk/sport1/hi/football/teams/m/middlesbrough/4875620.stm" target="_BLANK"&gt;Boro v Basel&lt;/a&gt; match tonight you'd completely understand why.&lt;br /&gt;&lt;br /&gt;Every now and again you get to watch YOUR team play like they're playing for their lives.  The whole of their existence being entirely for that one night.  When they're playing like nothing will ever matter as much as this does in this moment.  Every single one of them is on the same wavelength as every other player and they're plaing as if though they're a single being.&lt;br /&gt;&lt;br /&gt;And you know that they're doing it all just for you.&lt;br /&gt;&lt;br /&gt;Absolutely phenominal&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114435792034633044?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114435792034633044/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114435792034633044' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114435792034633044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114435792034633044'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/on-cloud-nine.html' title='On Cloud Nine...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114421962070276398</id><published>2006-04-05T07:34:00.000+01:00</published><updated>2006-04-05T07:47:00.720+01:00</updated><title type='text'>Dixons in "good service" shocker</title><content type='html'>I hate Dixons.&lt;br /&gt;&lt;br /&gt;Every time I go in there I have to wait an absolute age for anyone to help me.  They have the same staffing level problems that Debenhams and Maplins suffer from. They simply don't have any people on the shop floor.&lt;br /&gt;&lt;br /&gt;However, when I went snowboarding last month I decided that I wanted to take a cheap video camera with me, one that I wouldn't mind breaking.  And Dixons had exactly what I was looking for, an end of line Sony Handycam for 200 quid.  Nice one.&lt;br /&gt;&lt;br /&gt;As it was end of line I decided to haggle and managed to get 3 years of their Mastercare thrown into the deal.  It covers accidental damage.&lt;br /&gt;&lt;br /&gt;When we were out boarding I managed to stack it big time and break the flip out screen on the camera.  Really annoying, because as soon as you flip out the screen the viewfinder switches off, and the menu buttons are under the screen.  AAAaaargh.&lt;br /&gt;&lt;br /&gt;Anyway, we soldiered on through the holiday with the broken camera (still recorded fine) and when we got back I phoned up their Mastercare phone line expecting a nice big argument about whether or not it's covered.&lt;br /&gt;&lt;br /&gt;They just asked what the problem was, not once did they ask how it happened.  They sent out a courier bag that I got about 2 days later.  I put the camera in and called DHL to pick it up; they came the next day.  That was Thursday.  On Monday this week I got a DHL package back; I assumed that they'd messed up and just sent my camera to me instead of Mastercare.  It WAS the camera, but it was fixed!&lt;br /&gt;&lt;br /&gt;I couldn't believe it.  I reckon it must have taken no more than a week from start to finish to get the camera fixed, the best bit being the TWO WORKING DAYS it took for them to get delivery of the camera, fix it and send it back.&lt;br /&gt;&lt;br /&gt;Quite simply... WOW!&lt;br /&gt;&lt;br /&gt;At 65 quid, the Mastercare service is a bit pricy, no matter how good it is... but it's well worth trying to get it thrown in for free!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114421962070276398?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114421962070276398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114421962070276398' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114421962070276398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114421962070276398'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/dixons-in-good-service-shocker.html' title='Dixons in &quot;good service&quot; shocker'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114388728657352295</id><published>2006-04-01T11:18:00.000+01:00</published><updated>2006-04-01T11:28:06.593+01:00</updated><title type='text'>Net Send from an Oracle DB</title><content type='html'>Now I'm no DBA, but &lt;a href="http://www.chrispoole.co.uk/apps/xutlnet.htm" target="_BLANK"&gt;this (XUTL_NET) looks useful&lt;/a&gt;... &lt;br /&gt;&lt;br /&gt;It's a small footprint product that adds windows style NET SEND capability to Oracle 8i and above, running on any operating system... just as long as you've got NETBIOS over TCP/IP on your network and Windows clients to receive the message.&lt;br /&gt;&lt;br /&gt;It just seems like a neat little way of sending youself notification messages when those long running jobs finish, so you don't have to sit there with a window to your server open.&lt;br /&gt;&lt;br /&gt;Chris also supplies an &lt;a href="http://www.chrispoole.co.uk/apps/xutludp.htm" target="_BLANK"&gt;API for UDP (XUTL_UDP of course)&lt;/a&gt;... it seems he needed one to write XUTL_NET, and if you've written it, why not make it available?&lt;br /&gt;&lt;br /&gt;UDP is available for free, as is a trial version of NET.  I admit that I've not used either... but they both sound worth checking out if you need that kind of thing!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114388728657352295?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114388728657352295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114388728657352295' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114388728657352295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114388728657352295'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/04/net-send-from-oracle-db.html' title='Net Send from an Oracle DB'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114328773385050293</id><published>2006-03-25T11:35:00.000Z</published><updated>2006-03-25T11:55:33.866Z</updated><title type='text'>UK OUG Feedback and letting it slide</title><content type='html'>I got back some feedback on my presentation from the UK OUG.  I thought it was a nice touch.  You get to hear what people thought of your presentation in terms of content / topic / presentation skills / quality of slides and the overal presentation.&lt;br /&gt;&lt;br /&gt;All in all I was pretty pleased with the response, I was obviously a little disappointed as I can't handle being told that I'm anything other than exemplary at anything I do ;-)&lt;br /&gt;&lt;br /&gt;Still, I couldn't be that disheartened as everyone seemed to agree that the presentation overall was well done, and of value. Wooo hoo.  I'd agree that my presentation skills need a little bit of work... I was a little 'rabbit in headlights', and I'm likely to be like that for the next couple of presentations I do (assuming there are more to come).&lt;br /&gt;&lt;br /&gt;However, I was very disappointed at the response on my slides.  Apparently nearly 70% of people thought my slides were poor.  Damn it!&lt;br /&gt;&lt;br /&gt;I think this probably comes from a difference of opinion on the usefulness of slides as much as anything else.&lt;br /&gt;&lt;br /&gt;I wasn't presenting anything technical, there were no difficult concepts to comprehend and there was no structures that needed describing visually.  I was talking on what was a very human subject.  A natural one.  One I could happily discuss in the pub, without the need to resort to drawing on napkins.&lt;br /&gt;&lt;br /&gt;I wasn't presenting as a representative of the company I work for.  I develop in-house systems and we don't do consultancy.  There was no need to advertise my firm to the audience.&lt;br /&gt;&lt;br /&gt;With those two points in mind I figured that there was no great need for a lot of slides, or to make the ones I did use flashy.&lt;br /&gt;&lt;br /&gt;I personally don't like a lot of slides with bullet point representations of the detailed discussion.  I sometimes can't help but read the slide and then ignore the presenter.  I also don't go for graphics, points sliding in from the side, fading in, fading out.  They don't add anything to the discussion.&lt;br /&gt;&lt;br /&gt;Anyway... I ended up with 5 slides, every one was black text on a grey background.  They weren't sexy by any measure!&lt;br /&gt;&lt;br /&gt;Each slide contained a single sentence that was a salient point of that part of the discussion.  I wanted to convey the fact that when you walked away from the presentation I wanted you to walk away with these 5 points in your mind.  Those 5 points were the main ingredients of the talk, the rest was just spice to fill out the flavour.&lt;br /&gt;I didn't think I needed to sugar coat them, or spoon feed them, or shove them down your throat.  I thought the words had enough impact on their own.&lt;br /&gt;&lt;br /&gt;If that's what poor slides are... then I'm more than happy to get that feedback!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114328773385050293?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114328773385050293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114328773385050293' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114328773385050293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114328773385050293'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/03/uk-oug-feedback-and-letting-it-slide.html' title='UK OUG Feedback and letting it slide'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114294850918998653</id><published>2006-03-21T13:18:00.000Z</published><updated>2006-03-21T13:41:49.216Z</updated><title type='text'>Tool envy and SQL Developer</title><content type='html'>I've finally started evaluating &lt;a href="http://www.oracle.com/technology/products/database/sql_developer/index.html" target="_BLANK"&gt;SQL Developer&lt;/a&gt; as a general tool for our team.  Like most people I've been speaking to, I'm getting a bit sick of TOAD's increasingly bloated footprint and array of meaningless icons.  (And I liked &lt;a href="http://www.groundside.com/blog/content/SueHarper/"target="_BLANK"&gt;Sue Harper &lt;/a&gt;'s presentation on it...)&lt;br /&gt;&lt;br /&gt;Anyway, the early signs are reasonably good.  As version 1.0 tools go this is definitely one of the most stable and feature rich applications I've used and it's got some really strong points.&lt;br /&gt;&lt;br /&gt;Things I like:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The ability to name database connections&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Autocompletion for object names&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pretty much everything about the package / procedure editor&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The simplicity and general lack of clutter in the UI&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Things I don't like:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The procedure editor doesn't yet save to files&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There's no TOADlike session browser&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;For now, the lack of transparently saving packages and procedures as files is a bit of a block to us picking it up as a full blown IDE tool, but I'm sure this is coming and we'll be watching SQL Developer very closely for when this appears.&lt;br /&gt;&lt;br /&gt;The session browser I can live without as there are other tools out there that'll do this job.&lt;br /&gt;&lt;br /&gt;But the thing that's starting to get me REALLY excited is the prospect of &lt;a href="http://www.eclipse.org/" target="_BLANK"&gt;Eclipse IDE&lt;/a&gt; integration...&lt;br /&gt;&lt;br /&gt;So how far are Oracle likely to take this?  Here's what I'm hoping:&lt;br /&gt;&lt;br /&gt;In your Eclipse Project you get a new set of properties that allow you to assign database connections to your workspace.&lt;br /&gt;Whenever you want to edit a package / procedure / etc it launches the SQL Developer procedure editor.&lt;br /&gt;Saving one of the said files issues a save against the file system and a compile against the database.&lt;br /&gt;The result is that I get a feature rich editor in Eclipse plus the fact that I can completely ignore my database when I'm writing my code.  No more accidentally forgetting to install the latest version of the package before I test it.&lt;br /&gt;&lt;br /&gt;Beyond that?  I'm kinda hoping that Oracle take the lead from the Java community and start offering refactor tools.  Imagine a 'Rename Package' option that drops your old version, renames the files, creates the new version, and then updates all references to that package in other packages / procedures / functions / etc / etc.&lt;br /&gt;&lt;br /&gt;Similarly with a 'Rename Table'.  Change the table name and then change every reference to that table that exists in the system.  Generate me a script that does the rename table operation and I just need to put it into my patch runner.&lt;br /&gt;&lt;br /&gt;Sorry, I'm drooling now.&lt;br /&gt;&lt;br /&gt;Then we start to get a development environment for Oracle that properly supports you working in an agile manner.&lt;br /&gt;&lt;br /&gt;Oh, and while we're at it, integration with &lt;a href="http://utplsql.sourceforge.net/" target="BLANK"&gt;UT-PLSQL&lt;/a&gt; would be nice too...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114294850918998653?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114294850918998653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114294850918998653' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114294850918998653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114294850918998653'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/03/tool-envy-and-sql-developer.html' title='Tool envy and SQL Developer'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114244734428505086</id><published>2006-03-15T17:46:00.000Z</published><updated>2006-03-15T18:32:39.646Z</updated><title type='text'>UK-OUG and running</title><content type='html'>Well, that busiest of weeks is finally over, and I'm getting back into working life.&lt;br /&gt;After a week of flying down powder runs in the French Alps, I managed to complete my first (and last) half marathon in a phenominal 2 hours, 17 minutes and 49 seconds.&lt;br /&gt;If that wasn't enough, I then followed it up with my first public presentation, to &lt;a href="http://ukoug.net/" target="_BLANK"&gt;UK OUG&lt;/a&gt;, as part of their combined SIG day.&lt;br /&gt;&lt;br /&gt;It was a great experience, and I can't thank &lt;a href="http://radiofreetooting.blogspot.com/" target="_BLANK"&gt;Andrew Clarke&lt;/a&gt; enough for inviting me to speak, as well as Penny for putting up with me chopping and changing the presentation at the last minute.&lt;br /&gt;The full script for the presentation should make it on to the &lt;a href="http://ukoug.net/" target="_BLANK"&gt;UK OUG site&lt;/a&gt; in the next day or two (pdf hopefully), but for those that can't wait (honestly... can you REALLY not wait), the full text is repeated below.&lt;br /&gt;&lt;br /&gt;The highlight was most definately the demonstration of &lt;a href="http://www.oracle.com/technology/products/database/sql_developer/index.html" target="_BLANK"&gt;SQL Developer&lt;/a&gt; (AKA Raptor) by &lt;a href="http://sueharper.blogspot.com/" target="_BLANK"&gt;Sue Harper&lt;/a&gt;, a product manager who has most definately got her head screwed on right.  It's good to hear Oracle making the right noises, and SQL Developer seems to be a clear tool developed in an environment of customer collaboration.  Quest should really be watching their backs...&lt;br /&gt;&lt;br /&gt;Also, Ivan Pellegrin's talk on service oriented architecture was quite enlightening, demystifying the principles of SOA, clearly cutting through the hype and revealing the important concepts... reusability, transparency and simplicity.&lt;br /&gt;&lt;br /&gt;For those that wanted to catch me at the bar, please accept my most humble of apologies.  I had somewhere to which I needed to run, and couldn't avoid.  If you have any questions... any at all... please mail me and I'll happily open up some correspondance.  If it's appropriate I'll post the responses on this site.&lt;br /&gt;&lt;br /&gt;Also, Any feedback, even negative, is more than appreciated!&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UK OUG presentation&lt;br /&gt;March 14th 2006 – 12:45pm&lt;br /&gt;&lt;br /&gt;Lessons from an Agile project&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For the last 18 months I've been team leading a project to replace a legacy Oracle and Powerbuilder system with an Oracle and PHP system. The requirements were that over the coming 3 years or so we would completely replace the existing functionality of the legacy system, and enhance the system where-ever the business thought it could gain competitive advantage.&lt;br /&gt;&lt;br /&gt;We were told that we should expect to be able to release components of the new system with very short notice, basically whenever the business thought we had something it could use, that the direction of the project was likely to change frequently during its course and that at all times our new system would have to access the same data the legacy system does, that the data must be completely in step at all times.&lt;br /&gt;&lt;br /&gt;Basically, the project is the same as every other project I've ever worked on. It's just that the organisation was honest about it up front.&lt;br /&gt;&lt;br /&gt;To cut a long story short, we've had a pretty successful 18 months. In that time we've replaced about half the legacy system, and we've added a whole load of bells and whistles that the business feels has had a tangible effect on practices, turnover and, most importantly, profit.&lt;br /&gt;&lt;br /&gt;We've had more good feedback from the 500 or so end users than I've ever heard from any other set of end users in my life, and I've got to say that all in the new system is more than liked by those that use it.&lt;br /&gt;&lt;br /&gt;We get a constant feed of new ideas from the shop floor, and we have a dedicated customer team who are great at filtering those requests and combining them with the board of director's strategic view of the project. The result is a very clear short term development direction and a feeling that we know where we're generally headed.&lt;br /&gt;&lt;br /&gt;We've made 4 releases of the system to the business now, averaging one every 4 or 5 months, and every time we've made those releases we've made them to 12 independent installations of the system. That is, in the last 18 months our system has been released into a production environment nearly 50 times.&lt;br /&gt;&lt;br /&gt;We've never had a release fail.&lt;br /&gt;&lt;br /&gt;We made a major release of the system about two weeks ago, and since it's been released the users have reported 2 bugs. Which we've fixed and released, which makes our current outstanding live bug count zero.&lt;br /&gt;&lt;br /&gt;As we've been working on the system we've made a few demo releases.  In fact, every time we finish a small piece of functionality we release it to the demo environment.&lt;br /&gt;&lt;br /&gt;That equates to an average of one release every two days... a total of about 300. &lt;br /&gt;&lt;br /&gt;Often, the customer team will come up with an idea for a new piece of functionality, and if they think it's important enough, it can be specced, coded, tested and released into the demo environment within 3 or 4 days.&lt;br /&gt;&lt;br /&gt;Basically, we're working in line with the principles of Extreme Programming, and I can honestly say that I've never worked on a project that has been any where near as successful as this.&lt;br /&gt;&lt;br /&gt;Now, I firmly believe that internal system development is perfectly suited to Extreme Programming and vice versa, and I could very easily spend all day up here talking about how XP works and the advantages it gives you. I can happily evangelise about the individual principles and practices that XP embodies and try to convince you that XP is the way forward.&lt;br /&gt;&lt;br /&gt;But XP isn't for everyone. And XP probably isn't universal.&lt;br /&gt;&lt;br /&gt;Instead I'd like to talk about what working on an XP project has taught me in a more general sense.&lt;br /&gt;&lt;br /&gt;I'd like to talk about some core practices that we work with, that are complimentary to XP, and that I think are fundamental foundations upon which you can build a development environment.&lt;br /&gt;&lt;br /&gt;That is, Extreme Programming encourages you to think about why the process and your practices are the way they are, to question their worth and in turn improve them. &lt;br /&gt;&lt;br /&gt;Working on an XP project has taught me to take a new look at the practices I'd taken for granted. To examine the problems I thought were essential, rather than accidental, re-evaluate them, and hopefully solve them.&lt;br /&gt;&lt;br /&gt;We've come up with some good practices that I think are are good whether you choose to pair program and unit test or not. That these are practices from which any development process will benefit, and for which I believe there are NO excuses for not following. Certainly none that I've heard.&lt;br /&gt;&lt;br /&gt;These are things that are complimentary to any development process and that I think are probably unspoken assumptions of processes like XP.&lt;br /&gt;These are things that I am worried may be 'no brainers' in the Java, .NET and Ruby, communities, but that we, as Oracle developers and DBAs need to learn.&lt;br /&gt;&lt;br /&gt;My biggest hope is that as I'm describing these lessons, many or most of you will just think 'well yeah, of course, jeez, didn't you already know that', that many of you will go away in a 40 minutes time thinking, 'I learnt nothing from that, he just said a lot of stuff that's obvious'.&lt;br /&gt;&lt;br /&gt;I hope that it's just me that's taken this long to learn these lessons.&lt;br /&gt;&lt;br /&gt;I hope that my own experiences of Oracle development prior to this project are not the same as everyone else's.&lt;br /&gt;&lt;br /&gt;I hope that I was just unlucky.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Understand version control&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;On the face of it, version control tools are simple beasts. Technically, they do very few things:&lt;br /&gt;&lt;br /&gt;• Record changes that have occurred to files - Commit.&lt;br /&gt;• Allow you to extract the files as they appeared at any particular point in time - Checkout.&lt;br /&gt;• Label particular points in time to be of interest - Tag.&lt;br /&gt;• Manage multiple changes made to single files, at the same time, by multiple people – Lock or Merge.&lt;br /&gt;• Segregate sets of changes from each other - Branch.&lt;br /&gt;• Perform actions when commits occur - Trigger.&lt;br /&gt;• Group changes together in a set and put comments against it.&lt;br /&gt;&lt;br /&gt;All in all, the tools provide nothing that a manual process couldn't.&lt;br /&gt;In fact, I've worked at a place that implemented a pessimistic locking version control system purely through manual processes and a directory structure on a network drive. We'd copy files, change their extensions to lock them, change their extensions to release them, copy folders to tag, copy them again to branch.&lt;br /&gt;&lt;br /&gt;The result was a lot of time spent following the manual process, and fixing problems that were caused by people not following them. Every developer hated it. It sucked time out of your day and it was a dull, repetitive process that you could never get quite right.&lt;br /&gt;&lt;br /&gt;But that wasn't because there was anything wrong with the processes they were trying to implement.&lt;br /&gt;The problem was solely with the methods by which the processes were carried out.&lt;br /&gt;&lt;br /&gt;That is, there are two parts to version control:&lt;br /&gt;• Underlying processes that allow teams to manage changes to files over a period of time.&lt;br /&gt;• Tools that help to make those processes easy to follow.&lt;br /&gt;&lt;br /&gt;The process without the tool is laborious and cumbersome.&lt;br /&gt;The tool without the process is, well, useless.&lt;br /&gt;&lt;br /&gt;So often you see organisations that pick up the tool, but don't understand the principles behind it, or who understand the processes but don't get how the tool can help you perform them easily.&lt;br /&gt;&lt;br /&gt;My old friends with the manual processes DID eventually pick up CVS, after I'd left, but then completely failed to marry up their old processes with the new tool and ended up alienating the developers in a different way. It was a bit of a shame, especially considering that the original processes were actually built on sound principles.&lt;br /&gt;&lt;br /&gt;In order to really take advantage of version control you need to understand both those aspects. How the processes work, and how the tools can help you perform those processes so that the development team don't see them as a barrier to getting the job done.&lt;br /&gt;&lt;br /&gt;In order to understand how to put a version control process together that works we need to understand why we need version control, and what it does for us.&lt;br /&gt;&lt;br /&gt;For me, the most important aspect of version control is that when it's used properly, it becomes an absolute truth.&lt;br /&gt;&lt;br /&gt;In order for the developers to work, they need an absolute truth that they can rely on. A place from which any piece of development can start. A good version control repository provides that. It allows a developer to take any source code file they need and say with a great deal of certainty,&lt;br /&gt;&lt;br /&gt;"This is the file against which I must develop. This specification&lt;br /&gt;relates to this set of files, and this is the truth from which I must&lt;br /&gt;work".&lt;br /&gt;&lt;br /&gt;In teams where the version control repository either doesn't exist, or is an unreliable source of the truth, developers will find an alternative truth. Most often this is either the live system (which is a particular, narrow truth), or their own world (which is almost always a lie).&lt;br /&gt;&lt;br /&gt;So in order to make version control work for us, we need to make sure that it is reliable.&lt;br /&gt;&lt;br /&gt;What do I mean by reliable?&lt;br /&gt;&lt;br /&gt;First of all, we need to make sure that the team agree that from our version control we can build the live systems.&lt;br /&gt;&lt;br /&gt;Regularly ask of your version control:&lt;br /&gt;&lt;br /&gt;"If the live version of our system was to suddenly disappear,&lt;br /&gt;along with all our backups, can I completely rebuild the structure&lt;br /&gt;of our system from the version control repositories, and be&lt;br /&gt;certain it's correct?"&lt;br /&gt;&lt;br /&gt;If the answer is no then your repository is not good enough. Its content is unreliable in the sense that either:&lt;br /&gt;• It does not hold all the information that it needs to, or&lt;br /&gt;• The information it holds is inaccurate.&lt;br /&gt;&lt;br /&gt;There is reliability of completeness.&lt;br /&gt;&lt;br /&gt;And&lt;br /&gt;&lt;br /&gt;There is reliability of accuracy.&lt;br /&gt;&lt;br /&gt;Secondly, we need to make sure that the software in the repository is of a standard that all parties agree to.&lt;br /&gt;&lt;br /&gt;It may be that all code in the repository must always be in a ready to release state and that the build never breaks, it may be that the code must be regarded as broken except during short periods of well defined time.&lt;br /&gt;&lt;br /&gt;Whatever the decision is for the level of quality of code, it must be known throughout the team what that level is.&lt;br /&gt;&lt;br /&gt;If not, a developer will avoid updating their workspace to the latest version of the code... they'd never know if doing so would create a whole swathe of bugs to appear.&lt;br /&gt;&lt;br /&gt;There is reliability of quality.&lt;br /&gt;&lt;br /&gt;Finally, we need to make sure that whenever a developer wants a particular version of a particular piece of code that they can reach it, easily.&lt;br /&gt;&lt;br /&gt;Also, that whenever they need to check code into the repository that there is a way of them to do so, and that doing so takes a trivial amount of time.&lt;br /&gt;&lt;br /&gt;The version control system and its standards should not stand in the way of the developers from reaching their goals, it should aid them. Its crucial that whatever standards and tools are put in place that they match the working practices of the team involved.&lt;br /&gt;&lt;br /&gt;There is reliability of usefulness.&lt;br /&gt;&lt;br /&gt;Without those things, the version control processes will never be perceived as anything more than a pointless chore that gets in the way. And when people think it gets in the way, there will always be people who will find ways around it. As soon as one developer works round it, it ceases to be the truth. As soon as the repository is no longer the truth, the whole idea of version control collapses.&lt;br /&gt;&lt;br /&gt;The good news is that ensuring that the aspects of reliability are reached is really just about recognising that they're there and they're attainable. It's really just about understanding the relationship between the process and the tool and the impact their use has.&lt;br /&gt;&lt;br /&gt;It's about developing a process that matches your aims and your team and then finding a tool that matches the process.&lt;br /&gt;&lt;br /&gt;Thankfully there's plenty of good thinking out there that can help us put together appropriate processes, and there's one book in particular that really stands out:&lt;br /&gt;&lt;br /&gt;Software Configuration Management Patterns by Steve Berczuk and Brad Appleton.&lt;br /&gt;&lt;br /&gt;It's brilliant, and a must read for anyone that is looking to put version control processes in place, or wants to understand how they can improve the processes they have. Quite simply – if you haven't already, you should read it.&lt;br /&gt;&lt;br /&gt;All in all, I can't understate how important I think the implementation of a good version control policy and tool is. I found that understanding how version control works on a conceptual level led us to put together a set of processes that really don't get in the way of developing code but ensure that the repository is a reliable truth.&lt;br /&gt;&lt;br /&gt;And doing so led me very quickly onto the next lesson. Something that I didn't realise was possible until I'd seen the capabilities of a version control tool used inside a clear and robust process...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Implement a build script that an idiot could run&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The most dangerous part of any database development is the bit where you roll it out onto a live server.&lt;br /&gt;&lt;br /&gt;Because it's risky, many organisations think that they need highly skilled database developers or DBAs to perform the upgrade. That way, if anything goes wrong then you're guaranteed to have someone there to fix it.&lt;br /&gt;&lt;br /&gt;It doesn't sound like a bad idea.&lt;br /&gt;&lt;br /&gt;But because these people are highly skilled and able to keep track of the things that need doing during the upgrade, then you find that the upgrade becomes a series of disparate tasks. A long mundane series of steps. If you're really lucky then they may have a check-list beside them as they're doing it, but often they won't.&lt;br /&gt;&lt;br /&gt;The problem is that when you have a developer or DBA producing and performing the upgrade, you end up with a build script that a developer or DBA can run. And that means it's a build script that needs a developer or DBA for it to be run, and that means its basically time consuming and difficult to run.&lt;br /&gt;&lt;br /&gt;When anything is time consuming and difficult to do, then you do anything you can to avoid doing it. You do it as few times as you possibly can, and that leads to problems...&lt;br /&gt;&lt;br /&gt;I've worked for far too many people who will run an upgrade on a live system that they have never ran anywhere else. That means that the most dangerous part of the system life cycle is effectively done without any testing.&lt;br /&gt;&lt;br /&gt;Not only that, but I've worked for software houses that will do so and then have the cheek to charge the customer extra for support during the upgrade.&lt;br /&gt;&lt;br /&gt;Looking back I can't understand how I accepted, and how everyone around me accepted that when you decide you're going to upgrade a database you need to spend a long time putting together a collection of scripts and then run them in an informal manner.&lt;br /&gt;&lt;br /&gt;I've since interviewed a lot of developers and every single one has more or less said the same thing:&lt;br /&gt;Upgrades are time consuming, untested, informal processes.&lt;br /&gt;&lt;br /&gt;Now that sounds like a bad idea.&lt;br /&gt;&lt;br /&gt;So, I've got an alternative.&lt;br /&gt;&lt;br /&gt;Assume that you won't have highly skilled people running your builds on live systems. &lt;br /&gt;&lt;br /&gt;Assume that the person performing the upgrade is an idiot.&lt;br /&gt;&lt;br /&gt;If you've got an idiot performing the upgrade then in order to make the upgrade safe you need to make the build scripts safe. You can no longer rely on highly skilled DBA to nurse it through.&lt;br /&gt;&lt;br /&gt;Basically, the build script has to be of as high a quality as any other piece of code in the system.&lt;br /&gt;&lt;br /&gt;The build script needs to work first time, every time, and needs to do so with the minimum of input from the user. It needs to tell the person who's running it that it's working, that every component is doing what it should be doing, and as soon as it stops working it needs to stop and flash red and tell the idiot that the world's going to end... and then maybe stop and roll-back to a time when things were still working.&lt;br /&gt;&lt;br /&gt;The code that forms the build need to be covered by proper testing strategies, fall under version control, and be held in a structure that everyone on the team understands and can support. As well as being trivial to run the build, it needs to be trivial to add to the build. You want all of your developers to be able to actively change the build themselves, not ask someone else to do it for&lt;br /&gt;them. You want every developer to take personal responsibility for the quality of the build scripts in the same way that they take responsibility for the system.&lt;br /&gt;&lt;br /&gt;When the developers have to ask a third party to produce the scripts for them there is the possiblity of misinterpretation.  If the developers add the scripts themselves there is clarity and no risk of ambiguity.&lt;br /&gt;&lt;br /&gt;I argue that if you need to, then you can put together a build script that an idiot can run. That doing so doesn't actually require much more work to do than producing a build script a DBA could run. And that doing so makes the build script easier to run, safer to release to live and far quicker to produce.&lt;br /&gt;&lt;br /&gt;When you've got that it means that you have a build script that anyone can run, and can run easily.&lt;br /&gt;&lt;br /&gt;We've got our build scripts to such a point that we can roll out any version of our system to any earlier version of our system by switching off the web server, running a single command with 3 parameters, waiting for the script to finish, and then switching the web server back on again.&lt;br /&gt;&lt;br /&gt;If we need to create a brand new replica of the system, we take a configured web server and a fresh Oracle instance, and run the same script.&lt;br /&gt;&lt;br /&gt;We use the same script to roll out to the demo environment as we do for the live environment and every developer working on the project will roll out the system to the demo environment every few days... that is, when they finish each piece of work.&lt;br /&gt;&lt;br /&gt;This means that in a 3 month release cycle the upgrade scripts will have been ran, on average, 20 times by every developer on the team.&lt;br /&gt;&lt;br /&gt;Having the developers run the scripts that often really helps in getting the build scripts to run smoothly, and with the minimum of effort. Ask a developer to do the same task every two days and if they're worth employing they'll do everything they can to make sure that task is trivial to perform.&lt;br /&gt;&lt;br /&gt;We've done this by having a very clear structure for our build scripts. We've put a structure together that consciously removes the need for a build manager. That is, at every previous place I've worked there has been a person employed who's responsibility it is to construct a large chunk of the build script.&lt;br /&gt;&lt;br /&gt;When a customer wanted a release, or a version was to be sealed, the build manager would go through the changes made between version x and version y. In some places this would mean checking through their e-mails, in others it would mean checking differences in the version control repository, in yet others it would be checking the differences between reference databases.&lt;br /&gt;&lt;br /&gt;In all cases, that person's job could have been replaced by a script. It's just that no-one had the imagination to realise it.&lt;br /&gt;&lt;br /&gt;I'm sure that there are many ways in which the solution can be implemented, but for me they are all covered by this process:&lt;br /&gt;&lt;br /&gt;• Divide your database changes into:&lt;br /&gt;  • Things that can be ran many times without causing damage.&lt;br /&gt;  • Things that can only be ran once.&lt;br /&gt;  • Make sure that you have a structure that ensures that the things that can only be run once, only run once, and all the rest run every time.&lt;br /&gt;&lt;br /&gt;When it comes to making a build:&lt;br /&gt;• Find out which things need running&lt;br /&gt;• Run them, in the right order&lt;br /&gt;• Stop when you get an error&lt;br /&gt;For our Oracle build, this basically translates to a build script that incorporates 2 parts:&lt;br /&gt;&lt;br /&gt;First we have a patch runner. A generic script runner that will take an ordered list of scripts and ensure that every one of those scripts runs against the database once, and no more than once. If any of the scripts cause an exception then the whole thing stops.&lt;br /&gt;&lt;br /&gt;These scripts basically consist of table and data changes: the only components of an Oracle build script that we need to ensure only run once.&lt;br /&gt;&lt;br /&gt;The patches and the patch list are held in version control, along with the rest of the source code, so if we want to take a database up to version 2.4.5, we just check out version 2.4.5 and tell the patch runner to run. The patch list only contains those patches that were part of 2.4.5, and the patch runner makes sure it only installs the patches that have not already been run.&lt;br /&gt;&lt;br /&gt;Is it happens, we add another level of complexity on top of that. For every patch we have an additional 3 scripts. We have two that check the pre and post conditions for a patch. The pre conditions embody the assumptions that are made about the state of the database before the patch can be ran. The pre conditions are tested, and if they fail the patch doesn't run. That way, instead of picking apart data that has been mangled by a patch that should not have ran, we can look at a database to determine why the conditions required for the patch weren't met. A much easier proposition. When the patch is completed the post conditions are checked. These state the success&lt;br /&gt;conditions of the patch and ensure that when a patch is ran it does what was intended. Again, if the conditions aren't met then the upgrade stops.&lt;br /&gt;&lt;br /&gt;The third script is the reversal script. This can be used in a disaster recovery situation, when a patch has completed but must be removed. All four scripts are produced by a single developer... the one that requires the database to change.&lt;br /&gt;&lt;br /&gt;Second in the build, we have a script that re-installs absolutely everything else. &lt;br /&gt;Every view, trigger, package, procedure, function in the system is re-created, every time. You don't need someone to work out which packages have changed and ensure they get added to the build script if you always install everything. And what harm is done by always installing everything? It might take extra time to run the script, but the advantage is that the only thing the build script cares about is the target&lt;br /&gt;version... not the source version.&lt;br /&gt;&lt;br /&gt;Of course, the detail of the build script is more complicated than that, but that's the basic structure described. And with that structure we can take any version of the system and upgrade it to any other version. We can take an empty database and build the system in less than 5 minutes.&lt;br /&gt;&lt;br /&gt;And we use that fact to give ourselves the best present a developer can have...&lt;br /&gt;A Sandbox.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Make yourself a sandbox to play in&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Every decent developer I've worked with understands that you don't just dive in and make changes on a live server, and they all buy into the basic idea of version control for files.&lt;br /&gt;&lt;br /&gt;People instinctively get the idea:&lt;br /&gt;&lt;br /&gt;"If I'm working on this particular file then I want to be sure that I&lt;br /&gt;am the only person updating this file.&lt;br /&gt;When I save it and close it I need to know that when I open it&lt;br /&gt;again it'll be the same file.&lt;br /&gt;I need to know that I'm working in isolation."&lt;br /&gt;&lt;br /&gt;When you're developing, you need to have your own version of the source code you're changing, and have complete control over it.&lt;br /&gt;&lt;br /&gt;Version control tools like CVS and Subversion take this a step further and give you a private copy of the whole application. That way you can change any source code file and know that the changes you've made are private to you.&lt;br /&gt;&lt;br /&gt;Sometimes this idea is called 'the workspace', other times it's 'a sandbox'. Both are basically the same thing, but I like the metaphor of the sandbox... it's simple:&lt;br /&gt;&lt;br /&gt;I'm in my sandbox.&lt;br /&gt;I'm safe from the rest of the world and the rest of the world is safe from me.&lt;br /&gt;I can play.&lt;br /&gt;&lt;br /&gt;The idea is phenomenally successful when applied to source code. Probably because it's simply an extension of what any decent developer would instinctively do. That's why I'm so surprised that I so rarely see the idea extended to databases.&lt;br /&gt;&lt;br /&gt;I've worked at a few places where I got my own set of source code to play with, but invariably I had to install it into a database that was shared between the development team.&lt;br /&gt;&lt;br /&gt;Sometimes that DB would be specific to a branch on the development tree, other times it would just be a single conglomeration of all the different versions in an unstructured mass.&lt;br /&gt;&lt;br /&gt;It's like giving yourself a sandbox... and then putting it too close to the swings.&lt;br /&gt;&lt;br /&gt;You can be happily playing away, safe in the knowledge that you're completely unaffected by what other people are doing and BANG, another developer:&lt;br /&gt;&lt;br /&gt;• Updates the database, wiping out your test data&lt;br /&gt;• Overwrites your piece of code&lt;br /&gt;• Or even worse, introduces a subtle bug into some code that you use but haven't changed... you spend hours nursing your bad head, trying to track it down, and then find that the bug mysteriously disappears again.&lt;br /&gt;&lt;br /&gt;You think you're in isolation, but you're not.&lt;br /&gt;&lt;br /&gt;Once you get used to the fact that your world can change around you without warning, then you get more and more likely to assume that the bugs you see in your development are not your fault.&lt;br /&gt;&lt;br /&gt;The natural reaction is to blame the weakest link, and you'd never dream of accepting that you might be that link. Working in real isolation means that there's no ambiguity... you are the only link.&lt;br /&gt;&lt;br /&gt;Sandboxes have to be complete, or they're use is seriously compromised, and they're only complete if you have your own database.&lt;br /&gt;&lt;br /&gt;If you have your own database you can treat it fully as your own, and know that any changes that occur to it occur because you made them.&lt;br /&gt;&lt;br /&gt;When you need to integrate with development trunk you follow the simple pattern.. update your sandbox files, run the database upgrade and test.&lt;br /&gt;&lt;br /&gt;When you update your sandbox you're updating both the application source code and the build script, and since an idiot can run your build script then you can upgrade your database with no fuss.&lt;br /&gt;&lt;br /&gt;Not only are you using your sandbox to separate yourself from the rest of world, but you're also using it test your build script. If you have 10 developers working in 10 sandboxes then you immediately have 10 build testers as well.&lt;br /&gt;&lt;br /&gt;Traditionally, as the number of developers increases, then the fragility of the build also increases: since the build is changing more frequently it's more likely to break. But if every one of those developers is testing the build as they go along, then you can maintain the quality.&lt;br /&gt;&lt;br /&gt;Obviously, this kind of informal testing is no replacement for full blown testing, but any testing increases quality.&lt;br /&gt;&lt;br /&gt;So my argument is that I've if you've got 10 developers (or 10 pairs if you're doing it the XP way), then you need 10 databases...&lt;br /&gt;&lt;br /&gt;Now I can hear DBAs screaming: for most DBA's I've probably at least doubled the number of databases they manage. But it's not as bad as all that once you take a look at what a development database should actually be.&lt;br /&gt;&lt;br /&gt;The worry comes from the thought that a development database is the same species of beast as the live database. It isn't, not by a long way.&lt;br /&gt;&lt;br /&gt;The major difference between a live and development database is how their role defines the use of data. In a live database the single most important thing in that database is the data it holds. It is the sole purpose for the existence of that database. The live database without the live data is useless.&lt;br /&gt;&lt;br /&gt;In the development environment this is not the case; the most important thing in a development database is its structure.&lt;br /&gt;&lt;br /&gt;Its purpose is to work as a validation tool for the live database, to ensure that the application we're producing works against the live database and to allow us to develop the structure of that database in order to allow to us to develop the applications.&lt;br /&gt;&lt;br /&gt;It's important that we hold data that has a texture similar to that which exists in live, but it doesn't matter which data we hold. The data itself has no intrinsic value. We can generate our own.&lt;br /&gt;&lt;br /&gt;In fact, when we run unit tests on our PL/SQL code, this is exactly what we do; we generate our test data so we know exactly what form it is in. That way we can make clear statements about the form we expect it to be in when our PL/SQL has completed.&lt;br /&gt;&lt;br /&gt;We then use this generated test data as the normal data for our development. By clearing down and recreating test data regularly we are sure that any bugs we see in our development environment are caused by our code, not by some out of date and twisted set of data that was created by someone else half way though some development.&lt;br /&gt;&lt;br /&gt;Also, we don't need to hold the same volume of data. In live, every single piece of data is of importance and so you can't just remove it. In development we can limit ourselves to create only the data that is of importance to us at this point in time. &lt;br /&gt;&lt;br /&gt;If our development databases contain a very small amount of data, and the means to re-generate the data it needs, then you remove a large amount of the DBA's work.&lt;br /&gt;&lt;br /&gt;That is, most the work a DBA will do in maintaining a live database is purely down to the fact that it is a system that contains a large volume of critical data. It must always be available, it must serve data within certain performance requirements, and if the machine it's running on suddenly blows up then we must be able to recover the data. Quickly.&lt;br /&gt;&lt;br /&gt;Take away all that away and you've taken away most of the DBA's work in maintaining that database.&lt;br /&gt;&lt;br /&gt;So the approach we've taken is to get the DBA to create a single development database instance, and then within that we've created several independent schemas. Each schema is owned by a particular developer and they're responsible for the state of that schema.&lt;br /&gt;&lt;br /&gt;Each schema contains a mirror of the structure of the live system, with any changes that the latest bits of development have created. Each schema is a completely self contained working version of the system.&lt;br /&gt;&lt;br /&gt;OK, so the structure of the development workspace is slightly different to that of the live system; the live system has only the one application schema on each database instance, but we hide all that in the build script. The build script itself understands what type of database it's installing and changes its behaviour when we're upgrading a development database. As it happens, the only real change is replacing public synonyms with private synonyms for some objects, and then installing&lt;br /&gt;the test suite.&lt;br /&gt;&lt;br /&gt;I admit that it took a bit of work to get the structures right, and in making sure that differences between live and development were as small as possible but now that we have that structure in place we can create a new sandbox for a new developer in about 5 minutes... the amount of time it takes to run our build script. And every developer in the team can go into a sandbox and play, safe in the knowledge that NOTHING they do in that sandbox will affect anyone else.&lt;br /&gt;&lt;br /&gt;I don't think there's a single developer in the team that would give it up.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Obviously, in the last 18 months there's been a lot of other lessons that I'd take onto another project, but the combination of good version control policy, a simple build script and individual developer sandboxes have had a fundamental impact on our day-to-day life.&lt;br /&gt;&lt;br /&gt;The implementation of those foundations has allowed our developers to focus on the job at hand... the development of new a system. And it gives our DBA the confidence that the installation of a new version of the system won't cause him a weekend of headaches and pain. Rollouts of the system aren't trivial, and never will be. There are always going to be other project management issues to deal with... first line support and user training, for example. But we've got to a point where the actual upgrade of the database is not an issue.&lt;br /&gt;&lt;br /&gt;The development team love the pair programming and unit testing aspects of XP, but without the sandboxes we'd still have a team working with the usual frustrations caused by not working in isolation. Morale definitely improves when everyone feels as though they're in charge of their own space.&lt;br /&gt;&lt;br /&gt;And the solid version control policy allows us to manage the whole process of concurrent development without ever really thinking about it. We support live systems and develop new functionality side by side with very little pain.&lt;br /&gt;All in all, these three things just make our jobs a hell of a lot easier. And let us focus on what we do best, and ultimately, what we're paid for. Developing new systems... and drinking coffee.&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/XP" rel="tag"&gt;XP&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/version+control" rel="tag"&gt;version+control&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/sandbox" rel="tag"&gt;sandbox&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/workspace" rel="tag"&gt;workspace&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/UKOUG" rel="tag"&gt;UKOUG&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/OUG" rel="tag"&gt;OUG&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Andrew+Clarke" rel="tag"&gt;Andrew+Clarke&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Sue+Harper" rel="tag"&gt;Sue+Harper&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/SQL+Developer" rel="tag"&gt;SQL+Developer&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114244734428505086?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114244734428505086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114244734428505086' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114244734428505086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114244734428505086'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/03/uk-oug-and-running.html' title='UK-OUG and running'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-114059375543865190</id><published>2006-02-22T07:21:00.000Z</published><updated>2006-02-22T13:37:20.686Z</updated><title type='text'>Busy times and UK OUG</title><content type='html'>Once again I've been going through a quiet time; that the problem with something that's habit forming, sometimes you can fall out of the habit.&lt;br /&gt;&lt;br /&gt;There are three main reasons for not posting recently, and all translate to me being very busy...&lt;br /&gt;&lt;br /&gt;1) I'm getting married:  And along with getting married comes organising a wedding.  I wish someone would have told me that before I agreed!&lt;br /&gt;&lt;br /&gt;2) I'm training for a half marathon:  After managing my first 10km about 2 years ago I've been thinking about trying one... and a family member challenged me to one in my family's home town, so I thought 'what the hey'.&lt;br /&gt;&lt;br /&gt;3) I'm presenting for the first time at the &lt;a href="http://www.ukoug.org/calendar/show_event.jsp?id=2023" target="_BLANK"&gt;UK OUG in Slough on March 14th&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;When &lt;a href="http://radiofreetooting.blogspot.com/" target="_BLANK"&gt;Andrew Clarke&lt;/a&gt; surprised me by inviting me to participate, I couldn't really refuse, especially since he shares his name with my brother-in-law.  So I'll be talking about the things that I've learnt about Oracle development over the last 18 months, a time I've spent on an Extreme Programming driven project.  It'll be covering things that are outside of the core XP principles.  Rather it'll be those things that I would now say were essential in a project no matter what methodology was being followed.&lt;br /&gt;That doesn't mean to say that I don't think unit testing, pair programming, short iterations and close customer involvement aren't essential... it's more that I don't think I can add more to the discussion by focusing on other things.&lt;br /&gt;&lt;br /&gt;Anyway, for those who read this blog, the topics will probably be quite familiar.  Here's hoping it also makes sense.&lt;br /&gt;&lt;br /&gt;I look forward to seeing some of you there!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-114059375543865190?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/114059375543865190/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=114059375543865190' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114059375543865190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/114059375543865190'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/02/busy-times-and-uk-oug.html' title='Busy times and UK OUG'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113786042830258823</id><published>2006-01-21T16:08:00.000Z</published><updated>2006-01-22T13:44:09.823Z</updated><title type='text'>Developing an environment</title><content type='html'>Previously I've talked about how important I think that development databases are, and that I think that they should be proper sandboxes, protect from the outside world and each other.  I admit, I've kind of glossed over how that might be done, and so I wanted to correct that...&lt;br /&gt;&lt;br /&gt;In our current project we're building an application that will eventually replace an existing one.  Until it does so we need to ensure that our new application can live together with the legacy one, and will share the same data.  The two systems must exist symbiotically.&lt;br /&gt;&lt;br /&gt;The existing application, let's call it Legacy is a classic Forms or Powerbuilder and Oracle structure.  All it's objects exist in the Oracle user LEGACY, and are wrapped in public synonyms.&lt;br /&gt;Each users of the system is given an Oracle user that then accesses the LEGACY objects through the public synonyms.  Let's say have the users USER1, USER2 and USER3.&lt;br /&gt;So we have a structure that looks like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/3349/856/1600/Legacy.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; border: 1px solid #999; cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/3349/856/320/Legacy.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;The new application is to live within the same database.  We'll call that application NewApp, and install it into the new Oracle user NEWAPP.  As an Intranet delivered system (over HTTP) we decided that all user connections to the Oracle database would occur through the user NEWAPP.&lt;br /&gt;&lt;br /&gt;We decided that in order to insulate ourselves from Legacy, we would ensure that all access to the LEGACY objects would be done through a new set of private synonyms.  As an aside, this allowed us to develop a kind of 'name space'.  I.E. The private synonyms for the LEGACY objects are all prefixed with LE_.  That way we can tell which are LEGACY objects and which are NEWAPP and reduce the chances of clashing object names.&lt;br /&gt;&lt;br /&gt;Anyway, that then gives us the following structure in our live environment:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/3349/856/1600/LegacyAndNewApp.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; border: 1px solid #999; cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/3349/856/320/LegacyAndNewApp.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;We then state that we do not add new functionality into the LEGACY user.  I.E. All development for NewApp is done in the NEWAPP user.  We are not the owners of LEGACY and therefore do not have the ability to change LEGACY at will.  We are the owners of NEWAPP and therefore the contents of NEWAPP are entirely under our control.&lt;br /&gt;&lt;br /&gt;So how do we deal with this structure in our workspaces?&lt;br /&gt;&lt;br /&gt;One option is to build a full database per developer / pair.  Starting with an empty database we could either import a copy of LEGACY or build one from scripts.  Then by running our NewApp build script we can build the NEWAPP user as it would exist in the live environment.&lt;br /&gt;&lt;br /&gt;This may be appropriate for a small team of highly Oracle skilled developers, but I'm not too sure.&lt;br /&gt;With the large number of databases you then have an overhead in terms of maintaining those databases.  You either need a development DBA on hand to troubleshoot problems on each database, or you need each developer to be capable of dealing with those problems such as the listener not starting up, upgrading the DB to 9.2.0.7 to match the live environment, etc.  In addition, if each database is on a client machine, you run the risk of each database being configured in a slightly different way... being fiddled with by developers that don't necessarily understand the impact of settings being different in development as they are in live.&lt;br /&gt;&lt;br /&gt;In short, I'm not too sure.  I know it CAN work, and that it's far superior to not having individual workspaces... but we followed a different route.&lt;br /&gt;&lt;br /&gt;We decided we wanted a single development database, in which many workspaces could exist.  However, in order to have individual workspaces, we don't just need individual NEWAPP users, we also need individual LEGACY users.  OK, so we may not be changing the structure or functionality of the LEGACY user, but much of our data exists in that user.  If we're to be insulated from the changes made in other workspaces, then we need to be insulated from data changes as well as structural ones.&lt;br /&gt;&lt;br /&gt;So how do we implement the individual workspaces in a single database?&lt;br /&gt;&lt;br /&gt;The answer is actually surprisingly simple; we have a dual mode build script, to which we can state that we're building a development environment.&lt;br /&gt;&lt;br /&gt;If we're building into live / demo / any other non development environment then we build our private synonyms and issue our grants to LEGACY objects, then build / upgrade the NEWAPP user.&lt;br /&gt;&lt;br /&gt;If we're building into a development environment then we start off by creating our development version of the LEGACY user (lets call it DEVLEG1), then create our private synonyms and grants to DEVLEG1 instead of LEGACY.&lt;br /&gt;&lt;br /&gt;So if we were to build the development environments DEVNEW1/DEVLEG1, DEVNEW2/DEVLEG2 and DEVNEW3/DEVLEG3 then we'd have the following structures:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/3349/856/1600/DevLegacyAndNewApp.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; border: 1px solid #999; cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/3349/856/320/DevLegacyAndNewApp.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;If we then found that we needed to be able run the front end for Legacy in our development environments then we could add the creation of relevant users along with private synonyms to the appropriate DEVLEGx user, giving us the following structures:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/3349/856/1600/DevLegacyAndNewApp2.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; border: 1px solid #999; cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/3349/856/320/DevLegacyAndNewApp2.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;The result is that we have fully independant development workspaces whilst allowing the database to be centrally managed.  We can use our DBA team to keep the databases up and running and control the set-up whilst allowing us to develop the code freely without treading on each other's toes.&lt;br /&gt;The eagle eyed of you may notice the fact that our development databases may be a little deviod of data, not a great scenario.  However, we &lt;i&gt;have&lt;/i&gt; noticed that ourselves and put a solution in place.  More on that later...&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/db" rel="tag"&gt;db&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dbms" rel="tag"&gt;dbms&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/workspaces" rel="tag"&gt;workspaces&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/sandboxes" rel="tag"&gt;sandboxes&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113786042830258823?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113786042830258823/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113786042830258823' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113786042830258823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113786042830258823'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/01/developing-environment.html' title='Developing an environment'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113734799807964061</id><published>2006-01-15T17:56:00.000Z</published><updated>2006-01-15T18:02:04.736Z</updated><title type='text'>We need space to develop</title><content type='html'>I've talked previously about how important I think it is that individual developers (or pairs) get to work in their own sandbox (not least &lt;a href="http://robertbaillie.blogspot.com/2005/08/seminal-essay-and-difference-of.html" target="_BLANK"&gt;here&lt;/a&gt;).  This means that developers can experiment; can try avenues that would otherwise break the workspace for other developers; can refactor the database itself.&lt;br /&gt;&lt;br /&gt;Obviously, in order to do this, you need to actually have development databases for those developers to own.  This tends to lead to the DBA team announcing that you'd need far too many databases, that you simply can't be serious, that we've no idea how overworked they already are and that we can't expect them to take on another x databases, where x equals the number of developers.  Oh, and by the way... who's going to build them?  I can fit the first 3 in some time the middle of the year after next, god knows when the other 7 will get built.&lt;br /&gt;&lt;br /&gt;I can understand the reaction.  I'd be exactly the same if I thought that someone was suggesting that I doubled my workload.  Luckily for DBAs, that's most definitely not what I'm suggesting.&lt;br /&gt;&lt;br /&gt;The misunderstanding comes from the thought that a development database is the same species of beast as the live database.  It isn't, not by a long way.&lt;br /&gt;&lt;br /&gt;The major difference between a live and development database is how their role defines the use of data.  In a live database the single most important thing in that database is the data it holds.  It is the sole purpose for the existence of that database.  Fine, we reshape that database so that we can access the database in new and interesting ways, but all said and done, if that data wasn't there then the database would be useless.  In the development environment this is not the case.&lt;br /&gt;&lt;br /&gt;The most important thing in a development database is its structure.  Its purpose is to work as a validation tool for the live database, to ensure that the application we're producing works against the live database and to allow us to develop the structure of that database in order to allow to us to provide our application.  It's important that we hold data that has a texture similar to that which exists in live, but it doesn't matter which data we hold.  The data itself has no intrinsic value.  We can generate our own.&lt;br /&gt;In fact, when we run unit tests on our PL/SQL code, this is exactly what we do; we generate our test data so we know exactly what form it is in.  That way we can make clear statements about the form we expect it to be in when our PL/SQL has completed.&lt;br /&gt;&lt;br /&gt;Also, we don't need to hold the same volume of data. In live, every single piece of data is of importance and so you can't just remove it*. In development we can limit ourselves to create only the data that is of importance to us at this point in time.  If our development databases contain a very small amount of data, and the means to generate the data it needs, then you remove a large amount of the DBA's work.&lt;br /&gt;&lt;br /&gt;That is, most the work a DBA will do in maintaining a live database is purely down to the fact that it is a system that contains a large volume of critical data.  It must always be available, it must serve data within certain performance requirements, and if the machine it's running on suddenly blows up then we must be able to recover the data.  Quickly.&lt;br /&gt;Take away everything other than the 'it must always be available', and you've taken away most of the DBA's work.&lt;br /&gt;&lt;br /&gt;When we started development, we didn't tell our DBA that we needed 'x' new development databases.  We told our DBA that we needed 1.  We said we didn't need it backed up, just that we needed it to be up during working hours.  We said, make it about the same size as live and it'll always be big enough.&lt;br /&gt;We then ran our build scripts multiple times on that database, creating a schema for each of our pairs of developers.  This is the same build script we would use to upgrade live, it just has a development mode that allows us to say which schema name we want it to install into.&lt;br /&gt;The DBA has needed to do next to nothing with the database since it was created.&lt;br /&gt;&lt;br /&gt;Now I admit, we have only around 10 development databases at the moment.  Maybe the problem gets a lot harder to manage when you have 100 development databases. But then again, if you have 100 developers then I'm damn sure you've got more than 1 DBA.  You've got a DBA team.  And if you've got a team then surely someone in that team's got the time to build you enough databases to support your development team.  If not, then you've probably got more serious problems to deal with...&lt;br /&gt;&lt;br /&gt;Of course, none of this takes into account the fact that your customer needs to test the application, that you'll need to performance tune and test along side all the other systems running before you release and you need a test environment for duplicating issues once the system goes live.  But NONE of these things should be happening in your development environment... a topic for another post.  Later.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color:#EEC; border: 1px solid black; padding: 5px" &gt;* Actually, you often find that most the data in a live database is useless and only a small proportion is ever used. But try telling that to your customer and getting them to let you archive it...&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;,&lt;a href="http://www.technorati.com/tags/db" rel="tag"&gt;db&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dbms" rel="tag"&gt;dbms&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/workspaces" rel="tag"&gt;workspaces&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/sandboxes" rel="tag"&gt;sandboxes&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113734799807964061?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113734799807964061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113734799807964061' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113734799807964061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113734799807964061'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/01/we-need-space-to-develop.html' title='We need space to develop'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113690125872482630</id><published>2006-01-10T13:46:00.000Z</published><updated>2006-01-10T13:59:30.920Z</updated><title type='text'>Database Upgrade Links</title><content type='html'>Around August last year I started to put together some notes on writing database upgrade scripts.  It was kicked off after a discussion with &lt;a href="http://blog.niftypaint.nl/blog/2005/08/03/version-control-of-database-objects" target="_BLANK"&gt;Wilfred van der Deijl&lt;/a&gt;, based on experiences on my current project where I honestly we've got it about as right as it ever needs to be.&lt;br /&gt;&lt;br /&gt;For a while now I've been fiddling with FeedDigest to try to get it to categorise my entries so that I can produce a summary page, and I've finally given up.  So instead, I've put this entry together.  It's just a link page to each of my database upgrade entries...  I hope it's of use!&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner.html"&gt;Introduction&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner-rollbacks.html"&gt;Rollbacks&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-upgrade-scripts-why-all.html"&gt;Why all the effort?&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/09/database-patch-runner-dealing-with.html"&gt;Dealing with code&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner-table-centric.html"&gt;Table Centric Views&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/09/database-patch-runner-design-by.html"&gt;Design By Contract&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113690125872482630?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113690125872482630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113690125872482630' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113690125872482630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113690125872482630'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/01/database-upgrade-links.html' title='Database Upgrade Links'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113675250216787451</id><published>2006-01-08T20:32:00.000Z</published><updated>2006-01-08T21:48:17.010Z</updated><title type='text'>Google Pack</title><content type='html'>It's been a while since I've been 'in the blogging scene', and I suppose the only way to get back is to start posting again!  So, whilst this post may be small, I'm hoping it's beautiful.&lt;br /&gt;&lt;br /&gt;Google have decided to address that age old problem... getting your new PC from the point it's at when you first buy it to actually being useful and on the way to being safe to use on the web.&lt;br /&gt;&lt;br /&gt;They've put together the &lt;a href="http://pack.google.com/" target="_BLANK"&gt;'Google Pack'&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;OK, so I like Google Earth, but I'm not sure it's quite essential, but the rest:&lt;br /&gt;&lt;br /&gt;Google Desktop&lt;br /&gt;Picasa&lt;br /&gt;Google Toolbar for IE&lt;br /&gt;Norton Antivirus&lt;br /&gt;Ad-Aware&lt;br /&gt;Adobe Reader&lt;br /&gt;&lt;br /&gt;And THE most important one:&lt;br /&gt;Mozilla Firefox&lt;br /&gt;&lt;br /&gt;It's a stunning idea, though I'd probably add to that:&lt;br /&gt;&lt;a href="http://download.openoffice.org/2.0.1/index.html" target="_BLANK"&gt;Open Office&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.grisoft.com/doc/Programs/lng/us/tpl/tpl01" target="_BLANK"&gt;AVG&lt;/a&gt; (Ok, so you've already got Norton, but AVG is ACTUALLY free)&lt;br /&gt;&lt;a href="http://www.skype.com/download/" target="_BLANK"&gt;Skype&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.hitmanpro.nl/" target="_BLANK"&gt;Hitman Pro&lt;/a&gt; (Ad-aware is only one part of the anti spyware suite)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113675250216787451?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113675250216787451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113675250216787451' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113675250216787451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113675250216787451'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2006/01/google-pack.html' title='Google Pack'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113397997112542398</id><published>2005-12-07T17:53:00.000Z</published><updated>2005-12-07T18:26:11.140Z</updated><title type='text'>Valued estimation</title><content type='html'>In "User Stories Applied", Mike Coen puts forward a method for estimating the cost of developing stories (or any other piece of work).&lt;br /&gt;&lt;br /&gt;In summary:&lt;br /&gt;* The development team get together with the customer.&lt;br /&gt;* The customer describes a story.&lt;br /&gt;* The developers quiz the customer until they are happy they understand the story.&lt;br /&gt;* Each developer that wants to participate writes an estimate down.&lt;br /&gt;* All developers show their estimates.&lt;br /&gt;* The developer with the highest estimate states why they think it's so big.&lt;br /&gt;* The developer with the lowest estimate states why they thing it's so small.&lt;br /&gt;* The developers discuss the problem further until they're all happy they understand what's involved, and they estimate again.&lt;br /&gt;&lt;br /&gt;The loop keeps going round until the team all have the same estimate, or they're happy to accept a compromise on one.&lt;br /&gt;&lt;br /&gt;Once the stories are all estimated, they're put in a line of ascending cost and examined to make sure there are no discrepancies.  If there are, then that story is re-stimated.&lt;br /&gt;&lt;br /&gt;It works a treat, and the customers like it.&lt;br /&gt;&lt;br /&gt;In fact they like it so much that they've come up with a variation...&lt;br /&gt;&lt;br /&gt;Excluding the project manager, our customer team consists of two full time members and a number of people that work on our project only part-time.  One slight issue is that over the last 18 months the full time members are becoming more a part of the Information Systems team and less in touch with 'The Shop Floor'.  The part-time members help to ground them in the reality of the system.&lt;br /&gt;&lt;br /&gt;On the other hand, the part time members aren't available to instantly evaluate new stories, and prioritise them accordingly.&lt;br /&gt;&lt;br /&gt;So, once a week the customer team now get together in a 'Value and Take-up estimation'.  It works in the same way as the developer estimates except the customer team are evaluating the value that a given story will add to the system, and then the take-up level expected for that part of the system in a scale of one to ten,&lt;br /&gt;&lt;br /&gt;Obviously, the priority of a story is based on the combination of all three variables, Value / Take-up and Cost.  Still, as a general rule, a story that scores high on Value and high on Take-up is going to be a story that will be prioritised very highly unless the cost is extortionate!&lt;br /&gt;&lt;br /&gt;In fact the particular values may not be that important; there is no point discussing if a Value 8, Take-up 7 story is more important that a Value 7, Take-up 8 story on the basis of the raw values alone.&lt;br /&gt;&lt;br /&gt;Rather it's the forum for the discussion of the values that provides the real insight, and allows everyone to have their input.&lt;br /&gt;&lt;br /&gt;It's working to ensure that the team is working on what the whole customer team decide is the most important task, and not just the one with the loudest voice...&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/XP" rel="tag"&gt;XP&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Extreme+Programming" rel="tag"&gt;Extreme+Programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/programming" rel="tag"&gt;programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/estimating" rel="tag"&gt;estimating&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/user+stories" rel="tag"&gt;user+stories&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software+development" rel="tag"&gt;software+development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113397997112542398?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113397997112542398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113397997112542398' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113397997112542398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113397997112542398'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/12/valued-estimation.html' title='Valued estimation'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113172602899490944</id><published>2005-11-11T16:09:00.000Z</published><updated>2005-11-11T16:32:18.330Z</updated><title type='text'>Firefox past 10 percent</title><content type='html'>According to this week's New Scientist, Firefox has managed to get more than 10% of the web browser market.&lt;br /&gt;&lt;br /&gt;When I read that I was surprised.&lt;br /&gt;&lt;br /&gt;It seems that readers of this blog are much more discerning than your average internet user.  For some time now Firefox has been the browser used in more than HALF the requests to this site!&lt;br /&gt;&lt;table cellpadding="2" cellspacing="2" class="RealTable"&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;New Scientist report&lt;/td&gt;&lt;td&gt;Bobablog readers&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;MSIE&lt;/td&gt;&lt;td&gt;85.45%&lt;/td&gt;&lt;td&gt;44.42%&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Firefox / Mozilla&lt;/td&gt;&lt;td&gt;11.51%&lt;/td&gt;&lt;td&gt;51.44%&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Safari&lt;/td&gt;&lt;td&gt;1.75%&lt;/td&gt;&lt;td&gt;1.56%&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Netscape&lt;/td&gt;&lt;td&gt;0.77%&lt;/td&gt;&lt;td&gt;0.32%&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Opera&lt;/td&gt;&lt;td&gt;0.26%&lt;/td&gt;&lt;td&gt;1.56%&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;I'm sure that says more about bloggers and their readers than it does about this site in-particular...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113172602899490944?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113172602899490944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113172602899490944' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113172602899490944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113172602899490944'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/11/firefox-past-10-percent.html' title='Firefox past 10 percent'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113172518314319163</id><published>2005-11-11T15:59:00.000Z</published><updated>2005-11-11T16:06:23.246Z</updated><title type='text'>Non-techie Tip of the Month</title><content type='html'>Sorry to still be way off topic, but I was given a beautiful gem of a tip today.&lt;br /&gt;&lt;br /&gt;Have you ever accidentally written on a whiteboard with a permanent marker?&lt;br /&gt;&lt;br /&gt;If so, you'll know how painful it is trying to remove it again with solvents.  Far too much elbow grease required for my liking.&lt;br /&gt;&lt;br /&gt;Well, we had one of those moments today and someone piped up with a glorious bit of help.&lt;br /&gt;&lt;br /&gt;Simply write over the existing text with a proper whiteboard marker, then rub it off with a normal whiteboard rubber.&lt;br /&gt;&lt;br /&gt;Zero effort and a clean whiteboard!&lt;br /&gt;&lt;br /&gt;I promise that normal Oracle and PHP based services will be resumed soon...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113172518314319163?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113172518314319163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113172518314319163' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113172518314319163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113172518314319163'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/11/non-techie-tip-of-month.html' title='Non-techie Tip of the Month'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113144387236358372</id><published>2005-11-08T09:20:00.000Z</published><updated>2005-11-08T09:57:52.390Z</updated><title type='text'>Subversion de-perversion</title><content type='html'>&lt;a href="http://mikemason.ca/" target="_BLANK"&gt;Mr Mason&lt;/a&gt; has done it again.&lt;br /&gt;&lt;br /&gt;For those that use &lt;a href="http://subversion.tigris.org/" target="_BLANK"&gt;Subversion &lt;/a&gt;for their version control, &lt;a href="http://mikemason.ca/" target="_BLANK"&gt;Mike Mason's blog&lt;/a&gt; is a must subscribe.  He doesn't post often, but when he does it's invariably useful.  &lt;a href="http://mikemason.ca/2005/10/19/" target="_BLANK"&gt;His latest post&lt;/a&gt; covers splitting and merging Subversion repositories.  As usual, reading it leads to an "Of course that's how you it!" moment.&lt;br /&gt;&lt;br /&gt;If you haven't already picked it up, his book, "&lt;a href="http://www.amazon.com/gp/product/0974514063/103-8297256-9991855?v=glance&amp;n=283155&amp;n=507846&amp;s=books&amp;v=glance" target="_BLANK"&gt;Pragmatic Version Control with Subversion&lt;/a&gt;" is a great introduction to Subversion.&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Subversion" rel="tag"&gt;Subversion&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/version+control" rel="tag"&gt;version+control&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/CVS" rel="tag"&gt;CVS&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/VCS" rel="tag"&gt;VCS&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/SCM" rel="tag"&gt;SCM&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Mike+Mason" rel="tag"&gt;Mike+Mason&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113144387236358372?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113144387236358372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113144387236358372' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113144387236358372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113144387236358372'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/11/subversion-de-perversion.html' title='Subversion de-perversion'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-113135814567767940</id><published>2005-11-07T10:06:00.000Z</published><updated>2005-11-07T10:09:05.703Z</updated><title type='text'>Questionable Search...</title><content type='html'>I know I've been quiet for a while, but I'm still here.  There's things I'm going to get round to saying sooner or later, but a recent move has meant a distinct lack of internet access at home.&lt;br /&gt;&lt;br /&gt;For now though... my referrer tracking uncovered a hit to the site from an unexpected search, one that I'm not sure how to deal with...&lt;br /&gt;&lt;br /&gt;&lt;a href="http://search.msn.com/results.aspx?srch=105&amp;FORM=AS5&amp;q=what+does+smug+mean" target="_BLANK"&gt;What does smug mean?&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-113135814567767940?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/113135814567767940/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=113135814567767940' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113135814567767940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/113135814567767940'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/11/questionable-search.html' title='Questionable Search...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112930765990885515</id><published>2005-10-14T17:25:00.000+01:00</published><updated>2005-10-14T17:34:19.926+01:00</updated><title type='text'>Things I have learned this week</title><content type='html'>A couple of friends and I were joking over the weekend that if we all learned one thing each, every day for a year, then at the end of the year we'd be able to release the book 'Over 1001 things that 3 people learned in a year'&lt;br /&gt;&lt;br /&gt;Here's what I learned this week:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;08/10/2005: Cannon Street London Underground station used to have an ornate glass roof that was removed at the start of WW2.  It was moved to the countryside to avoid bomb damage.  The barn it was kept in was bombed a week later, completely destroying the glass roof.  Cannon Street station remained virtually untouched throughout the whole of the war.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;09/10/2005: Archduke Franz Ferdinand was assassinated in Sarajevo.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;10/10/2005: The ship the Queen Elizabeth 2 is always written down with the number 2 rather than the roman numerals II.  This is done in order to differentiate it from the Queen with the same name.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;11/10/2005: The most abundant metal in the human body is calcium&lt;/li&gt;&lt;br /&gt;&lt;li&gt;12/10/2005: The most abundant bird in the world is the domesticated chicken.  In 2003 the US ate 8.2 billion chickens, and if a chicken is grown for 7 weeks before being killed for meat, then in order for the 65 million people in the UK to be able to eat their average of 0.75 chickens a week then a total of 341.5 million chicken must exist at any given time in order to support the market.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;13/10/2005: It's not enough to learn from your mistakes, you have to actually remember that you learnt from them and put those lessons into action.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;14/10/2005: Whenever you find a gap in a test and you think "No, I won't put that test in, it'll never happen", it will happen, and it'll probably happen on the live system and give you a big headache.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I cannot make any guarantee as to the accuracy or relevancy of any of the facts, nor will I compensate anyone for the loss of time spent reading them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112930765990885515?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112930765990885515/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112930765990885515' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112930765990885515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112930765990885515'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/10/things-i-have-learned-this-week.html' title='Things I have learned this week'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112852920430937316</id><published>2005-10-05T17:12:00.000+01:00</published><updated>2005-10-05T17:20:04.323+01:00</updated><title type='text'>Casting a table: variations on a theme</title><content type='html'>A heads up from William Robertson and I've taken yet another look at the TABLE command (which allows you to issue SQL against a PL/SQL table as if it was a database table).  As he points out &lt;a href="http://robertbaillie.blogspot.com/2005/09/in-thing-simplified.html#c112803337970154020" target="_BLANK"&gt;here&lt;/a&gt;, it seems that the TABLE cast has trouble working out the data type when you're doing a SELECT *, but can often work out the data type for the same statement with the column list specified.&lt;br /&gt;&lt;br /&gt;Obviously, if I hung around on &lt;a href="http://asktom.oracle.com/" target="_BLANK"&gt;Ask Tom&lt;/a&gt; or &lt;a href="http://metalink.oracle.com/" target="_BLANK"&gt;Metalink&lt;/a&gt; I'd find it much easier to find these nuances out... and I'd do it in a far less public way.  But hey, I'd probably forget it then, wouldn't I! ;-)&lt;br /&gt;&lt;br /&gt;Anyway, I've put together 3 scripts that some illustrate slight variations that are available.  Note I've included DROP statements to get rid of any nasty residue, not to imply that you should drop these types in any real application.&lt;br /&gt;&lt;br /&gt;So, first up is a variation that uses a SELECT * and a TABLE + CAST selecting from a PL/SQL table of a primitive datatype&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SET SERVEROUTPUT ON SIZE 100000&lt;br /&gt;&lt;br /&gt;CREATE TYPE char_table_type AS TABLE OF VARCHAR2(1);&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;  --&lt;br /&gt;  char_table           char_table_type;&lt;br /&gt;  --&lt;br /&gt;  CURSOR cur_tab IS&lt;br /&gt;    SELECT *&lt;br /&gt;    FROM   TABLE( CAST( char_table AS char_table_type ) );&lt;br /&gt;  --&lt;br /&gt;BEGIN&lt;br /&gt;  --&lt;br /&gt;  char_table  :=   char_table_type('A','B','C','D','E','F','G','H','I');&lt;br /&gt;  --&lt;br /&gt;  FOR tab_rec IN cur_tab LOOP&lt;br /&gt;    DBMS_OUTPUT.PUT_LINE( tab_rec.column_value );&lt;br /&gt;  END LOOP;&lt;br /&gt;  --&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;DROP TYPE char_table_type;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The second variation uses a SELECT column list, and a TABLE selecting from a PL/SQL table of a primitive datatype.  Note that the column name is inferred.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SET SERVEROUTPUT ON SIZE 100000&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE TYPE char_table_type AS TABLE OF VARCHAR2(1);&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;  --&lt;br /&gt;  char_table           char_table_type;&lt;br /&gt;  --&lt;br /&gt;  CURSOR cur_tab IS&lt;br /&gt;    SELECT column_value&lt;br /&gt;    FROM   TABLE( char_table );&lt;br /&gt;  --&lt;br /&gt;BEGIN&lt;br /&gt;  --&lt;br /&gt;  char_table  :=   char_table_type('A','B','C','D','E','F','G','H','I');&lt;br /&gt;  --&lt;br /&gt;  FOR tab_rec IN cur_tab LOOP&lt;br /&gt;    DBMS_OUTPUT.PUT_LINE( tab_rec.column_value );&lt;br /&gt;  END LOOP;&lt;br /&gt;  --&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;DROP TYPE char_table_type;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The last variation uses a SELECT column list, and a TABLE selecting from a PL/SQL table of objects.  Note that the column name is no longer implied, but there is a requirement to construct objects in order to use this variation.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SET SERVEROUTPUT ON SIZE 100000&lt;br /&gt;&lt;br /&gt;CREATE TYPE char_table_row AS OBJECT ( letter VARCHAR2(1) );&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE TYPE char_table_type AS TABLE OF char_table_row;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;  --&lt;br /&gt;  char_table           char_table_type;&lt;br /&gt;  --&lt;br /&gt;  CURSOR cur_tab IS&lt;br /&gt;    SELECT letter&lt;br /&gt;    FROM   TABLE( char_table );&lt;br /&gt;  --&lt;br /&gt;BEGIN&lt;br /&gt;  --&lt;br /&gt;  char_table  :=   char_table_type( char_table_row ('A')&lt;br /&gt;     ,char_table_row ('B')&lt;br /&gt;     ,char_table_row ('C')&lt;br /&gt;     ,char_table_row ('D')&lt;br /&gt;     ,char_table_row ('E')&lt;br /&gt;     ,char_table_row ('F')&lt;br /&gt;     ,char_table_row ('G')&lt;br /&gt;     ,char_table_row ('H')&lt;br /&gt;     ,char_table_row ('I') );&lt;br /&gt;  --&lt;br /&gt;  FOR tab_rec IN cur_tab LOOP&lt;br /&gt;    DBMS_OUTPUT.PUT_LINE( tab_rec.letter );&lt;br /&gt;  END LOOP;&lt;br /&gt;  --&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;DROP TYPE char_table_type;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;DROP TYPE char_table_row;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;More investigation will take place, and I'm sure this won't be the last post on the topic!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/" rel="tag"&gt;&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/oracle" rel="tag"&gt;oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/table" rel="tag"&gt;table&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/cast" rel="tag"&gt;cast&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software+development" rel="tag"&gt;software+development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/9i" rel="tag"&gt;9i&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/ask+tom" rel="tag"&gt;ask+tom&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/metalink" rel="tag"&gt;metalink&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112852920430937316?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112852920430937316/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112852920430937316' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112852920430937316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112852920430937316'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/10/casting-table-variations-on-theme.html' title='Casting a table: variations on a theme'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112808170303655458</id><published>2005-09-30T12:55:00.000+01:00</published><updated>2005-09-30T13:01:43.053+01:00</updated><title type='text'>If I had a hammer...</title><content type='html'>I'm sure that pretty much every experienced developer out there has heard the proverb:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;If the only tool you have is a hammer, then everything's a nail.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Well, I've heard a few variations on the theme and so I've decided to start collecting them...&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Sometimes it's just a nail, and all you need is a hammer.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Don't ignore your hammer just because you heard someone else hit their thumb with theirs.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;When you're in the market for a new tool, make sure you're not buying yet another hammer.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;You can enhance the usefulness of your hammer by adding a chisel.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;A Rotary Electro-pneumatic hammer delivering 4600 hammer blows a minute is still just a hammer.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Anyone got any more?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112808170303655458?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112808170303655458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112808170303655458' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112808170303655458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112808170303655458'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/if-i-had-hammer.html' title='If I had a hammer...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112791352730014375</id><published>2005-09-28T13:59:00.000+01:00</published><updated>2005-09-28T15:18:01.613+01:00</updated><title type='text'>The In Thing - Simplified</title><content type='html'>I've had a few people visit the site looking for some help on the Oracle 9i TABLE command.  With this, you can issue SELECT statements against PL/SQL tables.&lt;br /&gt;&lt;br /&gt;Now, previously I'd posted about the self same command, in a series of posts here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/07/in-thing.html" target="_BLANK"&gt;http://robertbaillie.blogspot.com/2005/07/in-thing.html&lt;/a&gt;&lt;br /&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/07/in-thing-example.html" target="_BLANK"&gt;http://robertbaillie.blogspot.com/2005/07/in-thing-example.html&lt;/a&gt;&lt;br /&gt;&lt;a href="http://robertbaillie.blogspot.com/2005/07/in-thing-simpler-example.html" target="_BLANK"&gt;http://robertbaillie.blogspot.com/2005/07/in-thing-simpler-example.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Soon, I plan to put a few more notes together, starting from a simpler example.  But  for now I'll just thank Amis for the post &lt;a href="http://technology.amis.nl/blog/index.php?p=815" target="_BLANK"&gt;here&lt;/a&gt;, which has pointed out a complexity in my own example that I didn't realise...&lt;br /&gt;&lt;br /&gt;For some reason, the TABLE command cannot work out the data type of variable passed to it, and so will tend to throw an exception:&lt;br /&gt;  ORA-22905: cannot access rows from a non-nested table item&lt;br /&gt;&lt;br /&gt;As I'd only ever done this when working with package, I previously worked around this by coding a function that returns the content of the relevent package variable.  TABLE has no problem working out the datatype returned from a function.&lt;br /&gt;&lt;br /&gt;It turns out I could have got away with just a simple CAST.&lt;br /&gt;E.G.  SELECT * FROM TABLE( CAST( table_variable AS table_type ) )&lt;br /&gt;&lt;br /&gt;So, with that in mind, the first &lt;a href="http://robertbaillie.blogspot.com/2005/07/in-thing-example.html" target='_BLANK'&gt;'In Thing' example&lt;/a&gt; becomes this:&lt;br /&gt;&lt;br /&gt;The Example:&lt;br /&gt;&lt;br /&gt;In order to have something to access, we need the tables and data:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-size:8pt"&gt;&lt;br /&gt;CREATE TABLE product&lt;br /&gt;  ( id     NUMBER&lt;br /&gt;  , descr  VARCHAR2( 100 ) )&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;INSERT INTO product ( id, descr ) VALUES ( 1, 'one' );&lt;br /&gt;INSERT INTO product ( id, descr ) VALUES ( 2, 'two' );&lt;br /&gt;INSERT INTO product ( id, descr ) VALUES ( 3, 'three' );&lt;br /&gt;INSERT INTO product ( id, descr ) VALUES ( 4, 'four' );&lt;br /&gt;INSERT INTO product ( id, descr ) VALUES ( 5, 'five' );&lt;br /&gt;INSERT INTO product ( id, descr ) VALUES ( 6, 'six' );&lt;br /&gt;&lt;br /&gt;COMMIT;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;In order to perform the cast we need to declare a type for the row in the table, and then the table type itself:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-size:8pt"&gt;&lt;br /&gt;CREATE TYPE gt_id_type AS OBJECT ( id NUMBER )&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE TYPE gt_id_list_type AS TABLE OF gt_id_type&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then we define the package that will perform the cast.&lt;br /&gt;We have the following functions&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;GET_PRODUCTS, which is passed a table of IDs, and returns a ref cursor containing the products requested.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;GET_PRODS_BY_NAME, which is passed a string, and returns all the products that have a description containing that string.  This function uses GET_PRODUCTS to return the product details&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-size:8pt"&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE product_pkg AS&lt;br /&gt;  TYPE gt_product_cur IS REF CURSOR;&lt;br /&gt;  FUNCTION get_products ( pt_id_list  gt_id_list_type )&lt;br /&gt;                                    RETURN gt_product_cur;&lt;br /&gt;  FUNCTION get_prods_by_name ( pc_product_name   VARCHAR2 )&lt;br /&gt;                                    RETURN gt_product_cur;&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE PACKAGE BODY product_pkg AS&lt;br /&gt;  --&lt;br /&gt;  gt_id_list     gt_id_list_type;&lt;br /&gt;  --&lt;br /&gt;  FUNCTION get_products&lt;br /&gt;    ( pt_id_list  gt_id_list_type )&lt;br /&gt;    RETURN gt_product_cur IS&lt;br /&gt;    --&lt;br /&gt;    vt_product_cur gt_product_cur;&lt;br /&gt;    --&lt;br /&gt;  BEGIN&lt;br /&gt;    --&lt;br /&gt;    gt_id_list := pt_id_list;&lt;br /&gt;    --&lt;br /&gt;    OPEN vt_product_cur FOR&lt;br /&gt;      SELECT *&lt;br /&gt;      FROM   product&lt;br /&gt;      WHERE  id IN ( SELECT id&lt;br /&gt;                     FROM   TABLE( CAST( gt_id_list AS gt_id_list_type ) ) );&lt;br /&gt;    --&lt;br /&gt;    RETURN vt_product_cur;&lt;br /&gt;    --&lt;br /&gt;  END;&lt;br /&gt;  --&lt;br /&gt;  FUNCTION get_prods_by_name&lt;br /&gt;    ( pc_product_name   VARCHAR2 )&lt;br /&gt;    RETURN gt_product_cur IS&lt;br /&gt;    --&lt;br /&gt;    vt_product_ids   gt_id_list_type;&lt;br /&gt;    --&lt;br /&gt;  BEGIN&lt;br /&gt;    --&lt;br /&gt;    SELECT  gt_id_type( id )&lt;br /&gt;    BULK COLLECT&lt;br /&gt;    INTO    vt_product_ids&lt;br /&gt;    FROM    product&lt;br /&gt;    WHERE   descr LIKE '%'|| pc_product_name|| '%';&lt;br /&gt;    --&lt;br /&gt;    RETURN get_products( vt_product_ids );&lt;br /&gt;    --&lt;br /&gt;  END;&lt;br /&gt;  --&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Finally, we have some code to run the GET_PRODS_BY_NAME function and return its set of values.&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-size:8pt"&gt;&lt;br /&gt;SET SERVEROUTPUT ON SIZE 1000000&lt;br /&gt;&lt;br /&gt;DECLARE&lt;br /&gt;  --&lt;br /&gt;  vt_cur          product_pkg.gt_product_cur;&lt;br /&gt;  vr_product_rec  product%ROWTYPE;&lt;br /&gt;  --&lt;br /&gt;BEGIN&lt;br /&gt;  --&lt;br /&gt;  vt_cur := product_pkg.get_prods_by_name( 't' );&lt;br /&gt;  --&lt;br /&gt;  LOOP&lt;br /&gt;    --&lt;br /&gt;    FETCH vt_cur INTO vr_product_rec;&lt;br /&gt;    EXIT WHEN vt_cur%NOTFOUND;&lt;br /&gt;    DBMS_OUTPUT.PUT_LINE( vr_product_rec.descr );&lt;br /&gt;    --&lt;br /&gt;  END LOOP;&lt;br /&gt;  --&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And the output:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-size:8pt"&gt;&lt;br /&gt;two&lt;br /&gt;three&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Disclaimer:  I've not yet managed to test this out with access from a PHP layer...&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/oracle" rel="tag"&gt;oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/table" rel="tag"&gt;table&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/cast" rel="tag"&gt;cast&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software+development" rel="tag"&gt;software+development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/9i" rel="tag"&gt;9i&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Amis" rel="tag"&gt;Amis&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112791352730014375?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112791352730014375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112791352730014375' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112791352730014375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112791352730014375'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/in-thing-simplified.html' title='The In Thing - Simplified'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112749366472890614</id><published>2005-09-23T17:36:00.000+01:00</published><updated>2005-09-23T17:41:36.853+01:00</updated><title type='text'>Friday afternoon...</title><content type='html'>Friday afternoons always seem to lead to pointless conversations, which sometimes lead to classic moments of comedy...&lt;br /&gt;&lt;br /&gt;During an E-mail conversation about being vegetarian:&lt;br /&gt;&lt;br /&gt;"In fact humans aren't carnivores at all. We meat eaters are technically eating carrion, which is meat that is dead for several days or weeks.  That's why they hang cows and stuff up to enhance their flavour. We eat carrion,  I.E. like vultures and hyeenas. This means we shouldn't kill at all, just eat what we find.  I just happen to find it in the supermarket. It's not my fault there's a load of people who go around murdering animals and leave them in shops for me!"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112749366472890614?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112749366472890614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112749366472890614' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112749366472890614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112749366472890614'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/friday-afternoon.html' title='Friday afternoon...'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112713651937359223</id><published>2005-09-19T14:07:00.000+01:00</published><updated>2005-09-19T14:28:39.383+01:00</updated><title type='text'>Google maps?  PAH!</title><content type='html'>Ever since Google released the API for &lt;a href="http://maps.google.com" target="_BLANK"&gt;Google Maps&lt;/a&gt; it seems the whole of the internet has gone crazy for it.  Cool things have appeared like &lt;a href="http://boneill.ninjagrapefruit.com/wp-content/bbc/newmaps/" target="_BLANK"&gt;an index of the current BBC news stories on a map of the UK&lt;/a&gt;.  Add to that the fact that &lt;a href="http://earth.google.com/" target="_BLANK"&gt;Google Earth&lt;/a&gt; is pretty tasty too and you'd be forgiven for thinking that no other mapping tools existed.&lt;br /&gt;&lt;br /&gt;But... there is, and one of them does something cool and useful that Google Maps doesn't.&lt;br /&gt;&lt;br /&gt;I run.  Not much, but enough to manage a 10km run without passing out.  And when I train I like to make sure that I know exactly how far I'm travelling.  I like to plan my training schedule so I know that on Monday I'll do 5km, on Wednesday I'll do 2km fast + 1km jog + 2km fast + 1km jog.  I know, I know, I can't help it.&lt;br /&gt;&lt;br /&gt;Anyway. Google Maps doesn't help me there.  But &lt;a href="http://www.map24.com" target="_BLANK"&gt;Map 24...&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Map 24 has a really handy little ruler tool.  Point and click a rubber band round the route you run and BOSH, the distance travelled rounded to the nearest 100th of a mile (or thereabouts).  Very nice for working out 1km / 3km / 5km laps round your local streets.&lt;br /&gt;Oh, and it does a really cool wooshy zoom thing when you put an address in!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Google" rel="tag"&gt;Google&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Google+maps" rel="tag"&gt;Google+maps&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Google+earth" rel="tag"&gt;Google+earth&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Map+24" rel="tag"&gt;Map+24&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112713651937359223?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112713651937359223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112713651937359223' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112713651937359223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112713651937359223'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/google-maps-pah.html' title='Google maps?  PAH!'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112687470235458163</id><published>2005-09-16T13:37:00.000+01:00</published><updated>2005-09-16T14:03:29.496+01:00</updated><title type='text'>Feeling Smug</title><content type='html'>We've just gone live with a 40 user pilot of the latest version of the product we're developing, and once again I've been reminded why we work in an &lt;a href="http://www.extremeprogramming.org/" target="_BLANK"&gt;extreme programming&lt;/a&gt; kind of way, and why sometimes I just plain love this job!&lt;br /&gt;&lt;br /&gt;Feedback, from the coal face...&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;"This is really, really good. You've done a great job."&lt;/li&gt;&lt;br /&gt;&lt;li&gt;"It's very straightforward, isn't it?" &lt;/li&gt;&lt;br /&gt;&lt;li&gt;"I'm loving this! You can tell that a lot of thought has gone into this"&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;And my personal favourite:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;"I thought I was going to hate it - but I love it!" &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/extreme+programming" rel="tag"&gt;extreme+programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/XP" rel="tag"&gt;XP&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/rollout" rel="tag"&gt;rollout&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112687470235458163?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112687470235458163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112687470235458163' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112687470235458163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112687470235458163'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/feeling-smug.html' title='Feeling Smug'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112678859494428240</id><published>2005-09-15T13:47:00.000+01:00</published><updated>2005-09-15T13:49:54.946+01:00</updated><title type='text'>Ch-ch-ch-ch-Chaaaanges</title><content type='html'>Well, after a couple of weeks of fiddling with HTML and CSS I've finally managed to replace the blog's bog standard Blogger template with a home grown one...&lt;br /&gt;It may not be perfect, but at least it's mine!&lt;br /&gt;&lt;br /&gt;Comments are more than welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112678859494428240?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112678859494428240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112678859494428240' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112678859494428240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112678859494428240'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/ch-ch-ch-ch-chaaaanges.html' title='Ch-ch-ch-ch-Chaaaanges'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112644186852152063</id><published>2005-09-11T12:44:00.000+01:00</published><updated>2005-09-11T16:47:21.870+01:00</updated><title type='text'>Easy RSS syndication with FeedDigest</title><content type='html'>Thanks to a &lt;a href="http://andrewbeacock.blogspot.com/2005/08/add-fresh-rss-content-to-your-website.html" target="_BLANK"&gt;heads up&lt;/a&gt; from &lt;a href="http://andrewbeacock.blogspot.com" target="_BLANK"&gt;Andrew Beacock&lt;/a&gt;, I've taken a bit of a look at &lt;a href="http://www.feeddigest.com/" target="_BLANK"&gt;FeedDigest&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It's a very simple syndication site that allows you to easily put together RSS feeds and then produce HTML versions of them for placing on your site.&lt;br /&gt;&lt;br /&gt;I can see how this sort of tool can really start to push the use of RSS.  For me all it's meant that Bobablog now has the last 5 &lt;a href="http://www.orablogs.com/orablogs/" target="_BLANK"&gt;OraBlogs&lt;/a&gt; posts and the last 3 &lt;a href="http://bobaphotoblog.blogspot.com/" target="_BLANK"&gt;BobaPhotoBlog&lt;/a&gt; posts.  But there's no reason why it &lt;i&gt;should&lt;/i&gt; stop there.&lt;br /&gt;&lt;br /&gt;You could put up a feed of your &lt;a href="http://del.icio.us" target="_BLANK"&gt;del.icio.us&lt;/a&gt; bookmarks, the latest news from the &lt;a href="http://news.bbc.co.uk/" target="_BLANK"&gt;BBC&lt;/a&gt;, a central collection of your task lists from &lt;a href="http://www.backpackit.com/" target="_BLANK"&gt;BackPack&lt;/a&gt;, or (using the search facility) a short list of your own posts on a particular topic.&lt;br /&gt;&lt;br /&gt;The reason I think this is cool isn't because it does anything new... it doesn't.  The reason is that it does things in a way that means that non developers can do it.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/feed" rel="tag"&gt;feed&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/digest" rel="tag"&gt;digest&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/feeddigest" rel="tag"&gt;feeddigest&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/rss" rel="tag"&gt;rss&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/syndication" rel="tag"&gt;syndication&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/feed+digest" rel="tag"&gt;feed+digest&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Andrew+Beacock" rel="tag"&gt;Andrew+Beacock&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112644186852152063?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112644186852152063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112644186852152063' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112644186852152063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112644186852152063'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/easy-rss-syndication-with-feeddigest.html' title='Easy RSS syndication with FeedDigest'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112635289157640418</id><published>2005-09-10T12:41:00.000+01:00</published><updated>2006-01-10T13:56:41.686Z</updated><title type='text'>Database Patch Runner:  Design by Contract</title><content type='html'>So let's say that you've managed to put together a build script that will install the latest version of your database with the minimum of work and you've got your developers using the build to upgrade their own workspaces.&lt;br /&gt;&lt;br /&gt;How can you push the patch runner? Actually &lt;b&gt;test&lt;/b&gt; the upgrade and get easy to analyse information back from a nightly or continuous integration build?  How can you protect your live databases and ensure that patches are only ever applied to databases when those databases are in the right state to receive those patches?&lt;br /&gt;&lt;br /&gt;Bertrand Meyer developed the concept of &lt;i&gt;Design by Contract&lt;/i&gt;. He suggested a technique whereby for each method there will be a contract, stated in terms of:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Preconditions:  These conditions should be true before the function executes.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Postconditions: Given the preconditions are upheld, the function guarantees that on completion the postconditions will be true.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;In some languages (e.g. Eiffel) this contract is enforced, and if either side of the contract is broken the method throws an exception.&lt;br /&gt;&lt;br /&gt;Our patch runner works in a similar way.  Whenever a patch is written, pre and postcondition scripts are written.  The precondition script embodies the assumptions about / requirements for the state of the &lt;br /&gt;database immediately before the patch runs. The postconditions state the shape the database should be in once the patch has completed.&lt;br /&gt;&lt;br /&gt;Immediately before running a given patch the patch runner will run the corresponding precondition script.  The patch will not then be run unless the precondition script both exists and runs to completion.  Only if and when the precondition script runs and reports no errors will the patch be applied.&lt;br /&gt;Once the patch is complete the postcondition script is executed. If the postcondition script is missing, or reports a failure, then the build stops and the error is clearly reported.&lt;br /&gt;Only when the pre, patch and post scripts have completed is the patch log updated to state that the patch  has been applied.  If any point of the process reports an error then the patch log is updated to state that the patch failed, and for what reason.&lt;br /&gt;&lt;br /&gt;For the majority of patches, the pre and postconditions are fairly trivial, and can appear to be overkill.  For example, if you're adding an unpopulated column to a table then the precondition is that the column does not exist, the postcondition is that it does.&lt;br /&gt;&lt;br /&gt;The real advantage comes when you write data migrations.  For example, a patch is needed that will move column X from table A to table B.  A requirement may be that every value that was in B must exist in at least one column in table A, that no data is lost during the migration.&lt;br /&gt;A precondition could be that there is at least one destination row in table A for each value.  By checking this condition before the patch starts we can stop the patch from destroying data.&lt;br /&gt;&lt;br /&gt;Another example may be the reforming of some tables that contain monetary values.  It may be required that whilst data is being moved between rows or columns, that the sum total of all values in a particular set of columns be the same on completion of the patch as it was at the start.  The precondition script can store the original sum totals, the postcondition can then check the resulting totals after the patch is complete.&lt;br /&gt;&lt;br /&gt;Producing the pre and postcondition scripts focus the developer on two things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;What assumptions am I making about the structure of the data I am about to transform, and which of those assumptions are critical to my patch's success?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;How can I measure the success of my patch, and what requirements do I have of the resulting data?&lt;/li&gt;&lt;/ul&gt;Both of these things are very different to the usual focus of:  What processes do I need to apply to the data?  However, the result of focusing on the first two will usually result in a clear path to the third.&lt;br /&gt;&lt;br /&gt;We've found that using this structure greatly increases the feedback we get from our builds.&lt;br /&gt;&lt;br /&gt;If you can get your DBA to buy into the idea, you can produce an overnight test build using the most recent backup from your live system.  Overnight, automatically copy live, upgrade it and have your build produce a report on the success of the current build against the current candidate for upgrade.  This report will warn you if the data in the live system is moving away from the assumptions you originally had when producing your patches.  It is much easier to deal with such issues when you find out during your development cycle, rather than during or, even worse, days &lt;i&gt;after&lt;/i&gt; the upgrade has been applied to the production environment.  An upgrade patch becomes a very reliable thing when it's been ran 50 times against 50 different sets of data, each time reporting its success of failure.&lt;br /&gt;&lt;br /&gt;In addition, the postcondition scripts act as clear documentation as to the aim of the patch.  This is of great use if and when you find a performance problem with a particular patch.  Rewriting a patch 2 months after it was first put together is so much easier when you have a postcondition script to act as both documentation and sanity check.&lt;br /&gt;&lt;br /&gt;As a possible variation, on finding failures it should be possible for the patch runner to automatically rollback the failed change, taking the database back to the state immediately before the patch started.  In some environments this may be critical, however we've not yet found a pressing need and therefore in our version we do not automatically rollback failures.&lt;br /&gt;&lt;br /&gt;Of course, the mechanism is only as good as the pre and postcondition scripts that are produced, but then you ensure quality by always pair programming don't you ;-)&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/DbC" rel="tag"&gt;DbC&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Design+by+Contract" rel="tag"&gt;Design+by+Contract&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/db" rel="tag"&gt;db&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/dbms" rel="tag"&gt;dbms&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112635289157640418?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112635289157640418/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112635289157640418' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112635289157640418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112635289157640418'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/database-patch-runner-design-by.html' title='Database Patch Runner:  Design by Contract'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112612144753090853</id><published>2005-09-07T20:22:00.000+01:00</published><updated>2006-01-10T22:15:33.660Z</updated><title type='text'>The Database Patch Runner: Dealing with code</title><content type='html'>Previously I've talked about the database patch runner as a way of easily and consistently getting database changes into version control, onto databases and under automated frameworks. But one topic I think I glossed over was: Which database objects fall under the patch runner, and how do you deal with those that don't.&lt;br /&gt;&lt;br /&gt;A big advantage of the patch runner is that it groups functional changes together and will ensure that those changes are applied to a given database once and only once. A disadvantage is that it removes the object centric view of changes from version control and puts it into the database. For tables, I really don't see this as a problem. For packages, procedure and functions this is simply unacceptable; we need to keep source code grouped in the more traditional object centric manner.&lt;br /&gt;&lt;br /&gt;We could have developers make their changes to the individual procedures and suchlike and then get them to add the source code to the patches one they've finished. But they'd probably forget a file every now and again, and would moan about the task. We could have a build manager monitor the version control repository and generate the list of source code files that are needed for the jump between any two given tagged versions of the system and build patch files upon request. But that would mean that we would need to employ a build manager to do this job.&lt;br /&gt;&lt;br /&gt;Ideally we want a solution that will minimise the &lt;span style="font-style: italic;"&gt;overall&lt;/span&gt; work in managing the build script, not just the work for the developer.&lt;br /&gt;&lt;br /&gt;One of the reasons why we use the patch runner is to ensure that destructive code, or code that changes the underlying structure of a database cannot be run more that once. The implication of this is that statements which can be ran multiple times without destroying structure or content, &lt;span style="font-style: italic;"&gt;due to their intrinsic nature&lt;/span&gt;, do not need to form part of the patch runner.&lt;br /&gt;&lt;br /&gt;That is, if a procedure was to be dropped and re-created, the only impact would be to temporarily invalidate any other procedures calling that procedure. Oracle can deal with this and can be set to re-compile dependencies when a procedure is called. The overall structure and content of the database is not changed by re-creating a procedure. The intrinsic nature of the procedure means that it can be re-installed without damaging the database.&lt;br /&gt;&lt;br /&gt;This is not so when dealing with tables. If a table was to be dropped and re-created*, the impact would be to loose the data previously held in that table. The result of re-creating a table that has not changed is that the overall structure will remain the same, but the content will not.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 1px solid #888; font-size:75%; background: #DDD;"&gt;* Re-creating a table here being: dropping the table, cascading the drop of any keys on / to that table, and re-creating both the table and those keys.&lt;br /&gt;This is not a small amount of work in itself, and if this was not done, the structure would also change.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This difference is crucial in understanding which objects need to fall under the control of the patch runner and which objects do not, as well as understanding how we can then deal with the objects that do not.&lt;br /&gt;&lt;br /&gt;Of course, there is nothing stopping you from putting any particular set of objects under the control of the patch runner if you feel it is appropriate. For example, indexes can be dropped and re-created without any risk of loss of content and so you may regard these as outside the patch runner's concerns. However, indexes on large tables can take time to create and most of the data migrations that take place in the patch runner scripts would benefit from those indexes. For these reasons it may sometimes be appropriate to put the indexes into patch scripts.&lt;br /&gt;&lt;br /&gt;For us, the split between patch runner and non patch runner is as such:&lt;br /&gt;&lt;br /&gt;Patch Runner:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tables&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Indexes&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Materialized Views&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Non Patch Runner:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Grants / Synonyms&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Views&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Packages / Functions / Procedures&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;The list isn't exhaustive by any means; it covers the objects we use. As any new object type comes into focus we get together to decide if it should be installed by the patch runner.&lt;br /&gt;&lt;br /&gt;So, knowing which objects do not fall into the realm of the patch runner, we then need to actually deal with those objects.&lt;br /&gt;&lt;br /&gt;As stated above, the primary factor for deciding when an object should fall under outside of the patch runner's remit is if that object can be freely replaced with a new version of that object with no further work. We use that to our advantage, and state that since all objects outside of the patch runner &lt;span style="font-style: italic;"&gt;can&lt;/span&gt; be replaced when they have changed, then all objects outside of the patch runner &lt;span style="font-style: italic;"&gt;will&lt;/span&gt; be replaced irrespective of whether that object has changed or not.&lt;br /&gt;&lt;br /&gt;That is, our patch runner sits within a larger build script. In our case this build script is a master batch file and a collection of SQL scripts, though I see no reason why this shouldn't be Unix scripts, ANT tasks, or whatever. The overarching build scripts contain sections that will install objects that are required before the patch runner can execute, and others that require the patch runner to have completed its task before they are installed. In every case, each of these scripts install &lt;span style="font-style: italic;"&gt;all&lt;/span&gt; objects required by the database, not caring if they previously existed in the correct form.&lt;br /&gt;&lt;br /&gt;In some cases a single script will create the full set of a single object type; all grants are created in a single script, as are synonyms. In other cases a single script will exist for each individual object; each package specification has a script to itself, separated from its corresponding package body. In adding a new object to the database schema, the developer needs to add that object to the relevant controlling build script.&lt;br /&gt;&lt;br /&gt;This setup allows us to separately manage the version control for each individual object where that object is sufficiently important, whilst managing the number of scripts that exist for the less verbose creation statements.&lt;br /&gt;&lt;br /&gt;By ensuring that all objects are created during every build sequence we simplify two processes:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Implementing a change in a given object requires only that the create script is edited, therefore minimising the work a developer must do to implement that change.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Building the release does not require knowledge of the source version, only the target version, therefore minimising the work required in generating a release.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;The result is that whilst the objects fall within a highly structured build script, the developer need not think about it except when adding new objects. The PL/SQL source code can be managed like any other source code, each component falling under version control as an individual component.&lt;br /&gt;&lt;br /&gt;We acknowledge that the build script could be better, and that the controlling install scripts &lt;span style="font-style: italic;"&gt;could&lt;/span&gt; be generated (run all files from directory x, or run all files with extension '.xxx'), though in reality the maintenance of this script is minimal (a single number of minutes each week). Also, putting this in place would have an impact only on the build script, not on the individual object create scripts.&lt;br /&gt;&lt;br /&gt;Additionally, it can appear to be overkill to install the full set of source code on each build, even small patch releases. However, installing source code onto an Oracle database is generally pretty fast. We find that the build script spends far more time running data migrations than installing code.&lt;br /&gt;&lt;br /&gt;We exclusively use packages (no standalone procedures, functions or triggers), and so our build scripts are simple: We can install all the package specifications, then all the package bodies and can be sure that they will compile; dependencies do not affect the order of installation.&lt;br /&gt;Views &lt;span style="font-style: italic;"&gt;are&lt;/span&gt; installed in an order dictated by dependencies, though this does not prove difficult. If it did I'm sure we could 'force create' them.&lt;br /&gt;&lt;br /&gt;Simplicity aside, I'm certain that even if we had difficult interdependencies with a very large number of objects, we would first test the ability to manage their creation in this way.&lt;br /&gt;In short, I acknowledge that for larger projects there may be difficulties with the approach in the simplest form, but I'm more than confident that the basic theory holds. If not, I'd like to hear why, since our small (ish) project will inevitably become a very large one, as is the way with database applications.&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112612144753090853?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112612144753090853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112612144753090853' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112612144753090853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112612144753090853'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/database-patch-runner-dealing-with.html' title='The Database Patch Runner: Dealing with code'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112564846762081633</id><published>2005-09-02T09:00:00.000+01:00</published><updated>2005-09-02T09:07:47.630+01:00</updated><title type='text'>A little more conversation</title><content type='html'>In my usual style, I'm finding it difficult to walk away from a conversation on Wilfred van der Deijl's blog, this time with a guy called Chuck.&lt;br /&gt;&lt;br /&gt;His comment can be found &lt;a href="http://www.oratransplant.nl/2005/08/03/version-control-of-database-objects/#comment-195" target="_BLANK"&gt;here&lt;/a&gt;, with my reply following close behind.&lt;br /&gt;&lt;br /&gt;I wouldn't normally link to a comment like this, but I feel the message I'm trying to get across with the Patch Runner is well illustrated by my confusions about Chuck's company's approach.&lt;br /&gt;&lt;br /&gt;As he clearly points out, the CMM level 3 accreditation states that the process is repeatable, but not necessarily appropriate.&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/CMM" rel="tag"&gt;CMM&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Wilfred+van+der+Deijl" rel="tag"&gt;Wilfred+van+der+Deijl&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112564846762081633?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112564846762081633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112564846762081633' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112564846762081633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112564846762081633'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/little-more-conversation.html' title='A little more conversation'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112564723696382276</id><published>2005-09-02T08:41:00.000+01:00</published><updated>2005-09-02T08:47:16.970+01:00</updated><title type='text'>WTF</title><content type='html'>For a while now I've been reading the consistently amusing &lt;a href="http://www.thedailywtf.com/forums/12/ShowForum.aspx" target="_BLANK"&gt;Daily WTF&lt;/a&gt;.  Well now, thanks to William Robertson, James Padfield, Thai Rices and Adrian Billington we have the &lt;a href="http://oracle-wtf.blogspot.com/" target="_BLANK"&gt;Oracle WTF&lt;/a&gt;.  It's started well...&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/WTF" rel="tag"&gt;WTF&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/internet" rel="tag"&gt;internet&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/" rel="tag"&gt;&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112564723696382276?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112564723696382276/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112564723696382276' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112564723696382276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112564723696382276'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/09/wtf.html' title='WTF'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112422139381034248</id><published>2005-08-16T20:38:00.000+01:00</published><updated>2006-01-10T13:56:15.960Z</updated><title type='text'>Database Upgrade Scripts – Why all the effort?</title><content type='html'>On &lt;a href="http://blog.niftypaint.nl/blog/" target="_BLANK"&gt;Wilfred van der Deijl's blog&lt;/a&gt;, Chuck (amongst other things) effectively asks:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.niftypaint.nl/blog/2005/08/03/version-control-of-database-objects/#comment-184" target="_BLANK"&gt;"How much effort do you dedicate to total automation when you still need someone smart enough to [deal with failures]".&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In order to address this I think I first want to address the question, what are we trying to do with automation.&lt;br /&gt;The aim of is to make it easy to re-run the upgrade with as little effort as possible, with as many configruations as possible, thus testing the upgrade as much as possible and hopefully making the upgrade solid and reliable.  Of course we can't make sure that the upgrade works every time with every configuration of data, but we can make sure it runs successfully a lot more often than it doesn't.&lt;br /&gt;&lt;br /&gt;This gives us confidence in our ability to change the databases, and in doing so makes us more likely to re-work difficult data structures rather than work around them.  This means that our database is better to work with, which makes our jobs easier.&lt;br /&gt;&lt;br /&gt;Also, whenever we make a problem free roll-out we increase the confidence the rest of the business has in our ability to do our job.  It makes them more open to receiving more upgrades more often and makes them easier to roll them out when we do.  This means our customers get their bug fies earlier and their enhancements earlier, and can therefore give us feedback on where to make more enhancements earlier in the software's lifespan.&lt;br /&gt;&lt;br /&gt;In short, making the upgrades solid helps us write better software by allowing us to focus on the software rather than the software roll-out, and the customer on the possibilities of new software rather than software failure..&lt;br /&gt;&lt;br /&gt;How does automation give us this? Well, the ability to automate builds does not, it's the way in which those automated builds are used that does:  Why not make it possible for an overnight process to perform the upgrade to as many different production databases as you can gather the resources to do?&lt;br /&gt;&lt;br /&gt;In putting together an automated build plan we want builds on test databases that occur every night, using recent (that day's?) backups of production systems.  We want to be able to run through the upgrade as it would occur on live as many times as possible, and we want to do that without draining our resources to the point that it's all we are doing.&lt;br /&gt;&lt;br /&gt;If we can have unattended backups of our live system, unattended rebuilds of that live system on a test server, unattended upgrading of that database and then unattended testing of the resultant application, then we can eaily test the upgrade of the live system from the moment the project starts to the night before the production upgrade takes place.&lt;br /&gt;&lt;br /&gt;If we do this, then the odds are that by the time we decide to upgrade the production system we know about that last minute data problem that will cause the build to fail.  We'll have fixed the problem before the upgrade started, and we'll have dealt with in when we're not in a panic, we'll have done all this without teams of users waiting for the production database to come back up.&lt;br /&gt;&lt;br /&gt;Our aim is not to have a completely unskilled worker to perform the task of upgrading the production database, but to make sure the upgrade will go smoothly far more often than not.  We want that standby DBA to stay on standby.&lt;br /&gt;&lt;br /&gt;That seems like worth a bit of effort to me!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Wilfred+van+der+Deijl" rel="tag"&gt;Wilfred+van+der+Deijl&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112422139381034248?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112422139381034248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112422139381034248' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112422139381034248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112422139381034248'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/database-upgrade-scripts-why-all.html' title='Database Upgrade Scripts – Why all the effort?'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112387279672521731</id><published>2005-08-12T19:46:00.000+01:00</published><updated>2006-01-15T18:03:06.790Z</updated><title type='text'>A seminal essay and a difference of opinion</title><content type='html'>Thanks to &lt;a href="http://blog.niftypaint.nl/blog/2005/08/12/database-agility/" target="_BLANK"&gt;Wilfred van der Deijl's post here&lt;/a&gt;, I've just read a &lt;a href="http://www.martinfowler.com/articles/evodb.html" target="_BLANK"&gt;January 2003 article&lt;/a&gt; by &lt;a href="http://www.martinfowler.com/" target="_BLANK"&gt;Martin Fowler&lt;/a&gt; and Pramod Sadalage... and I can't believe that I had previously missed it!&lt;br /&gt;&lt;br /&gt;Martin and Pramod very clearly discuss the overarching issues that appear once you start to look at database development in an agile manner, and you can very easily see from their discussion how important a clear and solid patch runner structure is to successful agile database development.&lt;br /&gt;&lt;br /&gt;Not long after this article was written we were just starting to think about the same practices and many of the conclusions we came to matched.  Not least the need to implement sandbox databases for each workspace and the need for consistent test data across those databases.&lt;br /&gt;&lt;br /&gt;However, our conclusions did differ in one fundamental way, and I find it difficult to understand Martin and Pramod's (M&amp;P) approach in this particular area.&lt;br /&gt;&lt;br /&gt;My issue relates directly to the propagation of database changes across the development team.  In M&amp;P's approach, database changes are automatically applied to the development workspaces as they occur, managed by the team's DBA.  They point out that: &lt;br /&gt;&lt;br /&gt;'people are usually concerned that automatically updating developers databases underneath them will cause a problem, but we found that it worked just fine'&lt;br /&gt;&lt;br /&gt;I am one of those concerned people.  I am concerned in the same way that I would be if I was told that the source code I was working with would be automatically updated at regular intervals as changes occurred.  Let me rephrase the above statement with that in mind:&lt;br /&gt;&lt;br /&gt;'people are usually concerned that automatically updating developers source code underneath them will cause a problem, but we found that it worked just fine'&lt;br /&gt;&lt;br /&gt;Suddenly the statement does not seem quite so reasonable!&lt;br /&gt;&lt;br /&gt;In order to develop I need to clearly understand what is within my control and what is outside of my control.  I need to be able to rely on the known state of my own workspace.  For me, this is a founding principle behind the idea of the development workspace as a sandbox.&lt;br /&gt;A given version of an application is developed to run against a given version of the database.  Because of this, the database is as much a part of the workspace as the rest of the source code.&lt;br /&gt;&lt;br /&gt;There is also the issue of the database change being applied in advance of the related code change being  checked into the version control system, and the manual process therefore required.  In M&amp;P's process they will notify the DBA of the changes required once they are decided upon.  At some point in the near future the DBA will then make the changes to the central databases and the changes will propagate to the development workspaces.  As this happens there are three clear risks that I can see:&lt;br /&gt;&lt;br /&gt;1.The database schema change is made a significant amount of time before or after the associated code change is committed to version control.&lt;br /&gt;2.The change required by the developer is miscommunicated to the DBA and an incorrect change is applied.&lt;br /&gt;3.The change is mistakenly applied to development databases that are following a different branch to that in which the change is made.&lt;br /&gt;&lt;br /&gt;In all the above cases, the effective result is that the change may invalidate a particular component of the application due to the development (or integration) database being out of step with the source code being ran against that database.&lt;br /&gt;I believe all this risks can be mitigated against by making the following changes to the described process:&lt;br /&gt;&lt;br /&gt;1.Make the developers responsible for producing the actual patches that will be eventually be ran against all databases, development, through integration to live.  These patches should form part of an clear structured patch runner, and should be placed in version control alongside the rest of the application source code.&lt;br /&gt;&lt;br /&gt;2.Make each developer responsible for the maintenance of their own database workspace.  Make it easy for a developer to upgrade their database, and make them do so using those scripts that will be ran against all other instances of the database.  Make it part of the routine that immediately after updating their source code workspace they upgrade their database.&lt;br /&gt;&lt;br /&gt;3.Put the database upgrade into the automated build / continuous integration test suite and ensure that the build being performed by the automated build is exactly the same as that which would be ran on the live system.&lt;br /&gt;&lt;br /&gt;These practices have the added advantage of minimising the amount of work required by the DBA, who can exist in a more advisory role within the team.&lt;br /&gt;&lt;br /&gt;Of course, there are times where data migrations will take a non trivial amount of time to complete.  In these cases it is important that developers are warned of the fact and are able to schedule times when these changes take place so as to minimise the impact on their work.&lt;br /&gt;&lt;br /&gt;Having said this, agile database development is a new venture for most everybody involved, and work such as this should not be underestimated, and cannot understated.  There will likely be many different ideas on this topic, coming from many different people, and it's a pleasure to be involved.  It is very easy to forget just how much database development has evolved in the last 10 years!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/extreme+programming" rel="tag"&gt;extreme+programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;&lt;/span&gt;, &lt;a href="http://www.technorati.com/tags/Martin+Fowler" rel="tag"&gt;Martin+Fowler&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Pramod+Sadalage" rel="tag"&gt;Pramod+Sadalage&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112387279672521731?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112387279672521731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112387279672521731' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112387279672521731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112387279672521731'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/seminal-essay-and-difference-of.html' title='A seminal essay and a difference of opinion'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112367944505103970</id><published>2005-08-10T13:57:00.000+01:00</published><updated>2005-08-12T08:38:21.143+01:00</updated><title type='text'>Named Notation Parameters in easy to read unit test shock!</title><content type='html'>One of the better kept secrets of Oracle is the ability to use 'named parameter passing' over 'positional parameter passing'.  Oracle covers it &lt;a href="http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96624/08_subs.htm#853" target="_BLANK"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;That is, if I want to call a procedure that has three parameters that have defaults, and I want to a value pass into the third parameter, I can do so by referencing the name of the third parameter….&lt;br /&gt;&lt;br /&gt;Give the function definition:&lt;br /&gt;&lt;pre&gt;    FUNCTION insert_employee( pn_department_id  NUMBER := NULL&lt;br /&gt;                            , pn_manager_id     NUMBER := NULL&lt;br /&gt;                            , pc_employee_name  VARCHAR2       ) RETURN NUMBER;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Can be called by performing the following:&lt;br /&gt;&lt;pre&gt;    vn_employee_id := insert_employee( pc_employee_name =&gt; 'Rob Baillie' );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OK, so in the example given it may make a lot more sense to just order the parameters a little better, like have the employee name first, but you get the idea.&lt;br /&gt;&lt;br /&gt;So, it looks cool, and you may be able to think of a few instances where it may prove useful.  It can be handy if you think your procedure signatures are  likely to change and you have an out parameter you want to keep to the end of the procedure definition...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    FUNCTION insert_employee( pc_employee_name IN     VARCHAR2 &lt;br /&gt;                            , pn_department_id IN     NUMBER   := NULL&lt;br /&gt;                            , pn_manager_id    IN     NUMBER   := NULL&lt;br /&gt;                            , pc_error_message    OUT VARCHAR2         ) RETURN NUMBER;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It can be regarded as tidy make sure that error message parameter is at the bottom, but without having named parameter notation you would never get the benefit of the default parameters, and would have to change all your calls if you ever added a new field between the department and manager id.&lt;br /&gt;&lt;br /&gt;Quite handy, but nothing really earth shattering.&lt;br /&gt;&lt;br /&gt;The place where we’ve found the most use is in unit tests...&lt;br /&gt;&lt;br /&gt;Lets say we’re writing a test for the function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    FUNCTION insert_holiday( pn_employee_id   IN     NUMBER&lt;br /&gt;                           , pd_from_date     IN     DATE&lt;br /&gt;                           , pd_to_date       IN     DATE&lt;br /&gt;                           , pc_error_message    OUT VARCHAR2  ) RETURN BOOLEAN;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Lets say that this function has many different ways it can fail:  Any of the parameters being NULL, the from data being later than the to date, the total number of days over the allocation...&lt;br /&gt;&lt;br /&gt;In each case the function will fail in the same way.  Fail to insert the holiday record, return FALSE and set pc_error_message to the reason why it failed.&lt;br /&gt;&lt;br /&gt;In order to test for this conditions we write a simple failure check procedure with a signature along the lines of:&lt;br /&gt;&lt;pre&gt;    PROCEDURE check_insert_holiday_fails( pc_context      VARCHAR2&lt;br /&gt;                                        , pn_employee_id  NUMBER    := 7438&lt;br /&gt;                                        , pd_from_date    DATE      := TRUNC( SYSDATE )  + 10&lt;br /&gt;                                        , pd_to_date      DATE      := TRUNC( SYSDATE )  + 17 )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Each of the insert_holiday’s input parameters are duplicated on the check procedure, and set to be valid values for that procedure.&lt;br /&gt;Check_insert_holiday_fails calls insert_holiday with the passed in values and goes on to check for the correct three error conditions:  No increase in number of holiday records, returning false and passing back an error message.&lt;br /&gt;&lt;br /&gt;The pc_context is appended to each of the assertion texts in order to give a nice readout.  So, for example, the check false assertion may be (using UtPlsql):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    utAssert.this( 'When ' || pc_context ||', insert_holiday returns false', NOT vb_result );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The idea is that if the procedure was called with only the context value specifed, the call to the tested insert_holiday function would be successful (and the test would fail, if that makes sense!).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, we have a single procedure that will check that a failure state is returned, covering all the components.  We can then call this with our error states, using the named parameter notation in order to only change the parameters we are interested in changing.  E.G.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;    check_insert_holiday_fails( 'invalid employee_id'    , pn_employee_id =&gt; -1 );&lt;br /&gt;    check_insert_holiday_fails( 'NULL  employee_id'      , pn_employee_id =&gt; NULL );&lt;br /&gt;    check_insert_holiday_fails( 'from date after to date', pd_from_date   =&gt; SYSDATE + 5, pd_to_date =&gt; SYSDATE + 2 );&lt;br /&gt;    check_insert_holiday_fails( 'from date in past'      , pd_from_date   =&gt; SYSDATE - 1 );&lt;br /&gt;    check_insert_holiday_fails( 'to date in past'        , pd_to_date     =&gt; SYSDATE - 1 );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The use of the named notation means that only the parameters important to the failure are stated, rather than the full list.  This makes it easier to see each individual test case clearly.   If the procedure being tested has a lot of parameters, then the advantage becomes very clear!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/unit+testing" rel="tag"&gt;unit+testing&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/extreme+programming" rel="tag"&gt;extreme+programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/test" rel="tag"&gt;test&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/testing" rel="tag"&gt;testing&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Update: Sorry for the chnage in permalink address. I just couldn't handle the typo in the title!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112367944505103970?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112367944505103970/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112367944505103970' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112367944505103970'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112367944505103970'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/named-notation-parameters-in-easy-to.html' title='Named Notation Parameters in easy to read unit test shock!'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112334852218179396</id><published>2005-08-06T18:07:00.000+01:00</published><updated>2005-08-06T18:15:22.193+01:00</updated><title type='text'>Haloscan Ping 0.3 (alpha 2) released</title><content type='html'>For those that are interested, a new version of Haloscan Ping has been released.  Thanks go to Andrew Beacock and Ryan Cullen for their input!&lt;br /&gt;&lt;br /&gt;This version fixes a couple of minor bugs with the first alpha...&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Atom feeds with no content, but a summary tag are now recognised correctly&lt;/li&gt;&lt;br /&gt;&lt;li&gt;NULL entries no longer cause the ping screen to crash&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If an entry is shorter than the minimum number of characters, it will still be chopped off to the nearest sentence&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;What we're currently working on for the next version:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Ability to send multiple pings in a single submit&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Automatically stripping tags from the content&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;As with the previous version, if anyone wants it, please mail me!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;,&lt;a href="http://www.technorati.com/tags/Andrew+Beacock" rel="tag"&gt;Andrew+Beacock&lt;/a&gt;,&lt;a href="http://www.technorati.com/tags/Ryan+Cullen" rel="tag"&gt;Ryan+Cullen&lt;/a&gt; &lt;a href="http://www.technorati.com/tags/Firefox" rel="tag"&gt;Firefox&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/extensions" rel="tag"&gt;extensions&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Haloscan" rel="tag"&gt;Haloscan&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/trackback" rel="tag"&gt;trackback&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Mozilla" rel="tag"&gt;Mozilla&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/browser" rel="tag"&gt;browser&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/internet" rel="tag"&gt;internet&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/web" rel="tag"&gt;web&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/tech" rel="tag"&gt;tech&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/technology" rel="tag"&gt;technology&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112334852218179396?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112334852218179396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112334852218179396' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112334852218179396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112334852218179396'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/haloscan-ping-03-alpha-2-released.html' title='Haloscan Ping 0.3 (alpha 2) released'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112334515441340294</id><published>2005-08-06T17:16:00.000+01:00</published><updated>2006-01-10T13:57:19.113Z</updated><title type='text'>The Database Patch Runner: Table Centric Views</title><content type='html'>On &lt;a href="http://blog.niftypaint.nl/blog" target="_BLANK"&gt;Wilfred van der Deijl's blog&lt;/a&gt; we got into a nice discussion about grabbing a table centric history of the patches applied to a database when using a functional patch centric upgrade organisation.&lt;br /&gt;&lt;br /&gt;I think I've made it obvious in the past that I massively favour the functional grouping, since I think it makes the job of the developer and the person performing the upgrade that much clearer. However, I acknowledge the perceived need for a table history, and started thinking about how you might produce it.&lt;br /&gt;&lt;br /&gt;Just as I was getting to some sort of conclusion, Wilfred put together a couple of great ideas... and I thought I'd give them a critical review.&lt;br /&gt;&lt;br /&gt;Forgive me for this Wilfred... I really wanted to get this down, I think it's a great topic!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Group the patches by table, then add interpreted comments to the files in order to state which patch a given ALTER TABLE statement belongs with&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I'm not sure about this solution for a simple reason.  I don't like organising the source files by tables; I think this makes the developer's job that much harder.&lt;br /&gt;&lt;br /&gt;In producing a patch the developer needs to update several files and cannot easily see the order in which these changes are applied.&lt;br /&gt;&lt;br /&gt;I simple situation where this becomes very important is where a column needs to be moved from one table to another.  In order to do so, I cannot see how this can be done without changing at least two files; one that adds the new column, and one that drops the old..  In addition, you need to find somewhere to put the data migration script.  Does this go into to the DDL file for the table having the column added?  Or do you could add a third file into the mix.&lt;br /&gt;&lt;br /&gt;I have found in the past that the create column and data migration get put into the table scripts and there is a manual process added to ensure that the existing column gets dropped.  The process is then forgotten in the heat of the production upgrade and the redundant column remains in the schema.&lt;br /&gt;&lt;br /&gt;In addition, with the table centric grouping it is easy for a developer to miss a step in the DDL, a column isn't added to a table, for example.  By having the functional grouping it's easier to spot this mistake since the developer has a single file that lists all the DDL applicable for that functional change.&lt;br /&gt;&lt;br /&gt;Finally, there is a need to add that extra step... adding the comments to the table create scripts.  I have an objection to this in the same way that I have an objection to JavaDoc comments.  People simply don't add them, and nobody notices / cares until such time as those missing comments are crucial.  By then it's too late.  The comments &lt;i&gt;could&lt;/i&gt; be policed, but whenever I see something that needs policing I see something that needs changing so the policing is no longer needed.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Another way would be to stick to your way with a SQL script per patch and just store all statements in the database... [snip] ...  have system triggers that log all DDL statement against particular objects&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I love this idea.  I think this has got a lot going for it.  The big advantage of this technique is that the person writing the patch does not need to change the way they work in order for it to be implemented.&lt;br /&gt;&lt;br /&gt;As part of the patch runner installation it can add a system trigger to the current schema that will log the changes to the schema as they occur.  This detail log can state which object that has changed, the change that was made, and the patch that issued the change.&lt;br /&gt;&lt;br /&gt;The trigger can be notified of which patch is currently running (as stated by Wilfred) by the patch runner logging the name of the current patch in a package variable, a temporary table or some other session specific temporary store.&lt;br /&gt;&lt;br /&gt;Once the patch runner has completed it can then produce a report stating which changes were applied by that upgrade, grouped by table.  It can produce a report of all changes ever made to the database.  If so required it could list the changes classified by some arbitrary grouping of objects (system functional area / table sizes).&lt;br /&gt;&lt;br /&gt;As Wilfred states, there is a downside to this... the data is in the database, rather than version control. It is generated &lt;i&gt;after&lt;/i&gt; the change has been applied.  However, this data can be made available in version control fairly easily.  The simplest solution is to have the report run regularly on a development or test server. Copy the report into version control and away you go.  If you're running a nightly build then this can be done automatically each night from the test server in advance of the codeset being tagged.&lt;br /&gt;&lt;br /&gt;Alternatively you can provide a simple user interface in order to query this information more interactively. If, for example, you're developing browser based applications this will be a fairly simple tool to produce within your normal toolset.&lt;br /&gt;&lt;br /&gt;Additionally, such a system DDL trigger can be used to report on ANY changes made to the database, not just those applied through the official upgrade process.  Having produced reports that will list object changes from the database it would be a trivial task to change those reports to list any objects that have changed without being ran through the patch runner.  It may be that in a strict environment a change made outside of the patch runner (and therefore the standard application upgrade) could be blocked.  Obviously, any developer or DBA that knows the structure will be able to work around it without too much work, but this then means that you are certain that the person doing so is consciously making a change outside of the accepted process, rather than because they are not aware that such a process exists.&lt;br /&gt;&lt;br /&gt;As I've stated earlier, on Wilfred's blog, I'm not sure our organisation has a need for the table centric view of the changes made to a database.  In fact I strongly urge those people who do so to ask why it is they do.  I'd love to hear the reasons why... I feel as though I'm missing something!&lt;br /&gt;&lt;br /&gt;That said, I think the second solution proposed by Wilfred is spot on, and if we were looking to produce a table centric view we would definitely follow that avenue first.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Wilfred+van+der+Deijl" rel="tag"&gt;Wilfred+van+der+Deijl&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112334515441340294?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112334515441340294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112334515441340294' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112334515441340294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112334515441340294'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/database-patch-runner-table-centric.html' title='The Database Patch Runner: Table Centric Views'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112333183194877723</id><published>2005-08-06T13:29:00.000+01:00</published><updated>2005-08-06T13:37:11.950+01:00</updated><title type='text'>Rant:  You want my money?</title><content type='html'>To all you companies out there that want to take my money, and want me to pay you by credit card, on the internet.  When you ask for my credit card number, you've got three choices:&lt;br /&gt;&lt;br /&gt;1 - Build the entryboxes so that it doesn't matter if I put spaces or dashes in, just let my format the number 1234-1234-1234-1234, strip the number out and just use that.&lt;br /&gt;&lt;br /&gt;2 - Build the entry boxes so that I can't put spaces, dashes or too many characters in and make it clear that I can't.  That way I'll type my credit card number with just the numbers.&lt;br /&gt;&lt;br /&gt;3 - Let me type a combination of spaces, dashes and numbers in the way that's natural to me, then tell me I'm an idiot.  Give me messages like "Your credit card number has too many characters, please try again", or (my personal favourite) "You have entered your card number with spaces, please enter just numbers".&lt;br /&gt;&lt;br /&gt;Guess which option will reduce the chances of me using your company next time.  Guess which option your company most likely uses.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112333183194877723?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112333183194877723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112333183194877723' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112333183194877723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112333183194877723'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/rant-you-want-my-money.html' title='Rant:  You want my money?'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112326697399893148</id><published>2005-08-05T19:29:00.000+01:00</published><updated>2006-01-10T13:57:39.266Z</updated><title type='text'>The Database Patch Runner - Rollbacks</title><content type='html'>In response to my earlier post &lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner.html" target="_BLANK"&gt;here&lt;/a&gt;, &lt;a href="http://andrewbeacock.blogspot.com/" target="_BLANK"&gt;Andrew Beacock&lt;/a&gt; asked a couple of seemingly small questions.&lt;br /&gt;&lt;br /&gt;First of all, he asked for more detail.  I'm going to address that in a later post.&lt;br /&gt;&lt;br /&gt;The second question, I didn't really expect.  He basically asked “How do you rollback a patch?”&lt;br /&gt;&lt;br /&gt;I'm assuming that he's asked this question in relation to the point I made that went along these lines:  Since the patch runner stops running patches whenever one of the fails, then recovery is simply a case of rolling back the changes that the patch had already made and then restarting the patch runner.  If the runner ensures it doesn't attempt to re-run any successful patches, but does re-run the failed one then it effectively picks up from where it left off and goes on its merry little way.&lt;br /&gt;&lt;br /&gt;The point I was trying to make was that since the patch runner stops, then you don't get into the situation where you're attempting to fix a problem that occurred half way through the upgrade by getting to the end that then picking up the mess it left in its wake.  Instead you know &lt;i&gt;where&lt;/i&gt; the upgrade failed, the patch log states it clearly; you know &lt;i&gt;what&lt;/i&gt; the patch was trying to do, since you have a functional grouping of changes in the patch, and this gives you context for the failed change; you know that you only have to worry about the failure that is reported, not the hidden data problems that the rest of the upgrade generated because the first patch didn't do its job properly.&lt;br /&gt;&lt;br /&gt;So how do you rollback the changes in order to get the patch up and running again?&lt;br /&gt;&lt;br /&gt;To be honest, there's nothing I can think of other than getting your hands dirty; rolling up your sleeves and getting into the database.  Picking apart what happened, piecing together the story and trying to gaffer tape up the problems.&lt;br /&gt;&lt;br /&gt;However, you can make that job easier:&lt;br /&gt;&lt;br /&gt;The big thing is... &lt;b&gt;Think about transactions in your patch&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Every DDL statement issues an implicit COMMIT, so don't pepper your patch with DDL, leaving data in a transient state committed in the database.    Think about the order in which you do things in order to reduce the chances that a failure at any given point leaves data in an invalid state.&lt;br /&gt;&lt;br /&gt;Ensure that your 'add to schema' DDL comes first.  That way, if you're moving two columns between tables, and the second column's create fails, you don't get stuck in a situation where you need to reverse the first change or apply the second one manually.&lt;br /&gt;&lt;br /&gt;Ensure that your 'remove from schema' DDL comes last.  For the same reason as above.  It's much easier to deal with if you know that your data migrations have completed and it's just a case of removing those columns manually and marking the patch as complete.&lt;br /&gt;&lt;br /&gt;If you can get away with it, don't issue commits in your patch's data migration until it's completed.  Admitted, if you're moving millions of rows of data around, this may not be possible.  But think about it... do you really need to issue that commit every 10,000 records, or can you get away with running the whole lot through first?  That way, when you get a failure, the patch runner can ROLLBACK and you know what state the data is in... the state it was before the patch started.&lt;br /&gt;&lt;br /&gt;If you can't get away without committing regularly, or the patch simply takes to long to risk having to  repeat it, think about how you can make the migration restartable.  That is, store a watermark, make your patch keep track of where it was so that it can restart after a failure.  Make sure that the patch can handle being restarted without it trying to re-do work it has already done.  If it really is that risky, why not check that you have enough space in your rollback segments before you generate GBytes of data?&lt;br /&gt;&lt;br /&gt;Ideally you want to make sure that your patch doesn't fail, but with a bit of thought before you write the patch, you can make your life easier when it does.&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112326697399893148?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112326697399893148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112326697399893148' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112326697399893148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112326697399893148'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/database-patch-runner-rollbacks.html' title='The Database Patch Runner - Rollbacks'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112322865670636437</id><published>2005-08-05T08:53:00.000+01:00</published><updated>2005-08-05T08:58:16.393+01:00</updated><title type='text'>Comments back</title><content type='html'>Comments should be running normally again now.  The offending Haloscan comments javascript has been replaced with the original Blogger javascript.&lt;br /&gt;&lt;br /&gt;I had inadvertently switched over to Haloscan comments without switching off the Blogger comments.  So adding a comment when you arrived from a permalink and clicking on 'Post a comment' gave you a Blogger comment that couldn't be seen from the main page.&lt;br /&gt;&lt;br /&gt;Ah well, you live and learn!  Normal commenting has now been resumed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112322865670636437?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112322865670636437/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112322865670636437' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112322865670636437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112322865670636437'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/comments-back.html' title='Comments back'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112318948785915691</id><published>2005-08-04T22:00:00.000+01:00</published><updated>2005-08-04T22:04:47.866+01:00</updated><title type='text'>Comment problems</title><content type='html'>Just so you know, I'm having trouble with the comments on here...&lt;br /&gt;&lt;br /&gt;I've managed to install two competing sets of comments.&lt;br /&gt;Haloscan is dealing with the comments of the main page, then Blogger is dealing with the comments that you see when you look at an individual post.  Look at &lt;a href="http://robertbaillie.blogspot.com/2005/08/database-patch-runner.html"&gt;this post&lt;/a&gt; and you'll see what I mean.&lt;br /&gt;&lt;br /&gt;Over the weekend I'll try to make sure I fix the problem...&lt;br /&gt;For now, please accept my apologies :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112318948785915691?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112318948785915691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112318948785915691' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112318948785915691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112318948785915691'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/comment-problems.html' title='Comment problems'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112314293912138030</id><published>2005-08-04T08:40:00.000+01:00</published><updated>2006-01-10T13:55:49.250Z</updated><title type='text'>The Database Patch Runner</title><content type='html'>Over &lt;a href="http://blog.niftypaint.nl/blog/2005/08/03/version-control-of-database-objects" target="_BLANK"&gt;here&lt;/a&gt; Wilfred van der Deijl has raised the time old question when dealing with databases.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How do you easily upgrade a database from an arbitrary version to a new particular version with the smallest amount of effort.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I responded with a brief description &lt;a href="http://blog.niftypaint.nl/blog/2005/08/03/version-control-of-database-objects/#comment-165" target="_BLANK"&gt;here &lt;/a&gt;, which caused Amihay Gohen to ask a question or two over E-mail.  I thought I'd put together something a little more coherent and detailed here, rather than &lt;i&gt;just&lt;/i&gt; send it on to Amihay directly.&lt;br /&gt;&lt;br /&gt;This is the basic solution...&lt;br /&gt;&lt;br /&gt;Whenever a developer / pair of developers need to change the database they write a patch. This patch contains any alter tables / data migrations / etc. A single file contains the full set of changes for a single conceptual change and may cover changes to several tables.&lt;br /&gt;&lt;br /&gt;The patches are written in PL/SQL procedures.  That is, each single set of changes is implemented in a single procedure.  Each DDL command is issued in an EXECUTE IMMEDIATE, or similar, as is any DML that references a table or column that does not yet exist.  The patch is written in a procedure so that we can use the Oracle exception handler to propagate fatal errors in the code to a controlling procedure, something that is far more difficult to do in a script.&lt;br /&gt;&lt;br /&gt;These patch procedures are then installed by a "patch runner".  This runner is guided by a .txt list of patches that is created by the developers.  If a developer wants to add a patch then they write the procedure and add it to the patch list.  The runner then uses this patch list to tell it which patches need to be installed, and in what order.  The runner is written in PL/SQL, using UTL_FILE and DBMS_SQL to install the procedures.&lt;br /&gt;&lt;br /&gt;The runner logs when a patch starts, in a table, and marks the completion or failure of that patch when it finishes.  By looking in the patch log before it runs a patch, the patch runner ensures that it never attempts to run a patch that has previously been marked as complete.&lt;br /&gt;&lt;br /&gt;In addition, if any patch fails then the patch runner aborts, stating that it failed. "Crash don’t trash!".  If the patch runner is restarted then it will attempt to re-run any patches that previously failed.  This makes it easier to restart a failed upgrade if ever required.  You reverse out the changes already made by the failed patch and then rerun the patch runner.&lt;br /&gt;&lt;br /&gt;The runner itself is installed in a single PL/SQL script, and we make sure that it installs all the components it needs before it starts, and tears itself down once its finished.  It does not rely on any other part of the database or application in order to work.  Since there are no dependencies outside of the patch runner install script, we can use a single patch runner for all of our database based applications, with no changes.&lt;br /&gt;&lt;br /&gt;The installation and upgrade of the patch_log table is dealt with in the more traditional manner of a single CREATE TABLE at the start followed by ALTER TABLE statements as it changes over time.  The patch log is not destroyed down with the runner.  &lt;br /&gt;&lt;br /&gt;So, in summary, you check out a given tagged version of the application to run against an arbitrary database and run the patch runner. It loads the list of patches that are applicable for that tagged version. It runs over each patch in turn and checks if it has previously ran. If it has not, then it runs the patch.&lt;br /&gt;By the time it reaches the end of the list it has ran all the patches, none of them twice. You can even run the patch runner twice in succession and guarantee that the second run will not change the state of the database.&lt;br /&gt;&lt;br /&gt;The key to that is then to make sure that you use the build often. Get your developers their own database workspaces and make sure that that rebuild and upgrade them often.  By repeating the upgrades often you ensure that they basically work before they get moved into a test environment with the bigger worry of large volumes of data.&lt;br /&gt;&lt;br /&gt;The patch runner is, of course, only a small part of the database build, and a smaller part of the whole application build.  So the runner needs to put into the context of a larger build script.  We use batch files here in order to manage our database build process, but I see no reason why other, for complete scripting languages shouldn't be used, or ANT.  The runner is, after all, just a PL/SQL script.&lt;br /&gt;&lt;br /&gt;As an aside, we also write tests for the patches, checking the pre and post conditions are valid… but that’s a very different topic! &lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/database" rel="tag"&gt;database&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/upgrade" rel="tag"&gt;upgrade&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/agile" rel="tag"&gt;agile&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/patch" rel="tag"&gt;patch&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Wilfred+van+der+Deijl" rel="tag"&gt;Wilfred+van+der+Deijl&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Amihay+Gohen" rel="tag"&gt;Amihay+Gohen&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112314293912138030?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112314293912138030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112314293912138030' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112314293912138030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112314293912138030'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/database-patch-runner.html' title='The Database Patch Runner'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112291465688528941</id><published>2005-08-01T17:38:00.000+01:00</published><updated>2006-01-25T21:16:43.250Z</updated><title type='text'>Oracle Firefox</title><content type='html'>Whilst it doesn't give you anything that a bookmark doesn't give you anyway, that little search box next to the address bar still feels pretty handy.&lt;br /&gt;&lt;br /&gt;And if you're an Oracle developer, then &lt;a href="http:// tahiti.oracle.com" target="_BLANK"&gt;tahiti.oracle.com&lt;/a&gt; is pretty handy too.&lt;br /&gt;&lt;br /&gt;Well, someone out there has put the two together &lt;a href="http://awads.net/wp/2005/08/01/oracle-firefox-search-plugin/" target="_BLANK"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Not sure I'll use it &lt;i&gt;that&lt;/i&gt; often, but it's worth a look!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update: 25/01/06&lt;/b&gt;&lt;br /&gt;Turns out I use this absolutely loads.  If you haven't installed it yet... get it on!&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Oracle" rel="tag"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Firefox" rel="tag"&gt;Firefox&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/extension" rel="tag"&gt;extension&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/oracle+documentation" rel="tag"&gt;oracle+documentation&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Mozilla" rel="tag"&gt;Mozilla&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Eddie+Awad" rel="tag"&gt;Eddie+Awad&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112291465688528941?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112291465688528941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112291465688528941' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112291465688528941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112291465688528941'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/08/oracle-firefox.html' title='Oracle Firefox'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112274172036884619</id><published>2005-07-30T17:38:00.000+01:00</published><updated>2005-07-31T11:56:41.173+01:00</updated><title type='text'>Some screenshots of the Haloscan Ping Firefox Extension</title><content type='html'>The configuration screen...&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/3349/856/1600/Config-Ping.gif"&gt;&lt;img style="margin:10px;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/3349/856/320/Config-Ping.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And the Ping Screen...&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/3349/856/1600/Send-Ping.gif"&gt;&lt;img style="margin:10px;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/3349/856/320/Send-Ping.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;,&lt;a href="http://www.technorati.com/tags/Andrew+Beacock" rel="tag"&gt;Andrew+Beacock&lt;/a&gt; &lt;a href="http://www.technorati.com/tags/Firefox" rel="tag"&gt;Firefox&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/extensions" rel="tag"&gt;extensions&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Haloscan" rel="tag"&gt;Haloscan&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/trackback" rel="tag"&gt;trackback&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Mozilla" rel="tag"&gt;Mozilla&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/browser" rel="tag"&gt;browser&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/internet" rel="tag"&gt;internet&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/web" rel="tag"&gt;web&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/tech" rel="tag"&gt;tech&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/technology" rel="tag"&gt;technology&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112274172036884619?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112274172036884619/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112274172036884619' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112274172036884619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112274172036884619'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/some-screenshots-of-haloscan-ping.html' title='Some screenshots of the Haloscan Ping Firefox Extension'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112273921390345919</id><published>2005-07-30T16:46:00.000+01:00</published><updated>2005-07-31T11:56:13.696+01:00</updated><title type='text'>Trackbacks are getting easier</title><content type='html'>Ever since I was introduced to trackbacks a couple of months ago I've had a worry about them... they're difficult to setup.  So I thought I'd do something about it.&lt;br /&gt;&lt;br /&gt;After a bit of help from my good friend &lt;a href="http://www.andrewbeacock.com/" target="BLANK"&gt;Andrew Beacock&lt;/a&gt;, I got my head around trackbacks and set up a &lt;a href="http://www.haloscan.com"&gt;Haloscan&lt;/a&gt; account so I could get them installed onto my own blog.  Getting it up and running was pretty straight forward, but then the tricky bit arrived... actually performing a trackback ping request.&lt;br /&gt;&lt;br /&gt;In order to make a request you need to go to the Haloscan account and enter the trackback ping URL for the post that you want to ping. You then need to enter the details of your own blog... the blog title, post link, title and extract.  It all seemed like a lot of hard work to me.&lt;br /&gt;&lt;br /&gt;So here's what I did:&lt;br /&gt;&lt;b&gt;I wrote a Firefox extension to send trackback ping requests to Haloscan&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;In order to use it you need to set it up:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Install the extension&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Configure it with the address of your blog feed (rss and atom are supported).  You'll find it under Tools -&gt; Haloscan Ping&lt;br /&gt;&lt;li&gt;Log into Haloscan&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;In order to send a ping request:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Write your blog entry and publish it&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Go to the blog you want to ping, and select the trackback URL&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Right-click and pick Ping Haloscan&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pick the entry from your blog to ping with and click OK&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Simple as that!&lt;br /&gt;&lt;br /&gt;At the moment I'd regard the extension as being at alpha stage, so for now I'm sending it out on request.  If you'd like to try it out then mail me and I'll send you a copy.&lt;br /&gt;Once I've got some positive feedback for it I'll try to publish it on &lt;a href="https://addons.mozilla.org/extensions/" target="_BLANK"&gt;Mozilla Update&lt;/a&gt; and &lt;a href="http://extensionroom.mozdev.org/" target="_BLANK"&gt;Mozdev&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt;You can mail me &lt;a href="javascript:spamSafeEmail('bobalicious.bob')"&gt;here...&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;,&lt;a href="http://www.technorati.com/tags/Andrew+Beacock" rel="tag"&gt;Andrew+Beacock&lt;/a&gt; &lt;a href="http://www.technorati.com/tags/Firefox" rel="tag"&gt;Firefox&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/extensions" rel="tag"&gt;extensions&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Haloscan" rel="tag"&gt;Haloscan&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/trackback" rel="tag"&gt;trackback&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Mozilla" rel="tag"&gt;Mozilla&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/browser" rel="tag"&gt;browser&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/internet" rel="tag"&gt;internet&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/web" rel="tag"&gt;web&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/blog" rel="tag"&gt;blog&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/software" rel="tag"&gt;software&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/tech" rel="tag"&gt;tech&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/technology" rel="tag"&gt;technology&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112273921390345919?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112273921390345919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112273921390345919' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112273921390345919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112273921390345919'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/trackbacks-are-getting-easier.html' title='Trackbacks are getting easier'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112264278422339652</id><published>2005-07-29T13:43:00.000+01:00</published><updated>2005-07-30T17:05:30.016+01:00</updated><title type='text'>Index Cards</title><content type='html'>In our version of XP, Stories get written on index cards.&lt;br /&gt;&lt;br /&gt;Text on the front, in big marker pen describes the user action in a single sentence.&lt;br /&gt;The aim is to get the people involved to think in small chunks, and to think about the fundamentals of each piece of work.&lt;br /&gt;&lt;br /&gt;Text on the back, in biro describes any particular behaviour required, we call them the acceptance criteria.  The aim is to get people to think about the exceptional requirements of a story, the bits that the simple view of the action doesn't cover, to think of the little gotchas that'll appear.&lt;br /&gt;&lt;br /&gt;They work extremely well, focussing the mind on the job in hand.&lt;br /&gt;&lt;br /&gt;But there's something I think we've missed the point of:  For each story there is only one card.&lt;br /&gt;&lt;br /&gt;Here are my thoughts...&lt;br /&gt;&lt;br /&gt;Since there's only one card, the card needs to be with the people that are currently working on it.  If the story's still being worked out, it's with the customer, if it's being developed then it's with the pair working on it.&lt;br /&gt;&lt;br /&gt;That means that if the customer wants to change their mind after the story's been started by the development team then they need to find the pair that's currently working on it and speak to them.&lt;br /&gt;Fair enough, we could have the stories stored electronically and then put a system in place to ensure that the pair are notified of any change as soon as its put onto the system.  It wouldn't be difficult, the customer enters each story on the system, revising it until they think it's ready.  The story gets marked as ready to start when they've had a discussion with the development team.  Each pair would register which story they're working on, marking the story as in progress.  And so, and so on.&lt;br /&gt;&lt;br /&gt;Or, we could do the simple thing and use natural capabilities of the card; do the simplest thing that will work.&lt;br /&gt;&lt;br /&gt;Simplicity is only part of the issue.  The big advantage of actually tracking down the pair and speaking to them is that you then get into a conversation.  A notification is one way, a conversation is two way.  And things that are written down are open to interpretation.&lt;br /&gt;&lt;br /&gt;Conversation is crucial to the success of XP, and keeping the story on the card just seems to me to be a great way of keeping that conversation going.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Extreme+Programming" rel="tag"&gt;Extreme+Programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/user+stories" rel="tag"&gt;user+stories&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112264278422339652?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112264278422339652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112264278422339652' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112264278422339652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112264278422339652'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/index-cards.html' title='Index Cards'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112246931512903816</id><published>2005-07-27T13:59:00.000+01:00</published><updated>2005-07-27T14:03:12.466+01:00</updated><title type='text'>The second saddest post ever</title><content type='html'>The other thing I love about regular expressions is that you can NEVER test them enough.  You always find yet another bit of text that doesn't behave itself.  There's always better ways of doing what you're doing.  And if you ever post a blog entry about one, you always have to follow it up with another that corrects it...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  [\w\W]{200,}?[\.!\?\)"\n\r]+&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Damn!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112246931512903816?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112246931512903816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112246931512903816' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112246931512903816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112246931512903816'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/second-saddest-post-ever.html' title='The second saddest post ever'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112245124119309908</id><published>2005-07-27T08:45:00.000+01:00</published><updated>2005-07-27T09:48:43.786+01:00</updated><title type='text'>RTFM (again)</title><content type='html'>When I first started pair programming it took me about 2 hours to come to terms with the fact that sometimes you need to ask for help, and that it's OK to look in a manual.  You won't lose face with your pair for doing that.&lt;br /&gt;&lt;br /&gt;It took our boss about 2 weeks to notice the increase in traffic on uk.php.net caused by my arrival.&lt;br /&gt;&lt;br /&gt;So why is it that whenever I'm coding on my own I will tend to try to hack through the undergrowth with a pen knife to get to the solution rather than ask someone if I can borrow their chainsaw?&lt;br /&gt;&lt;br /&gt;The latest example is a problem I was having with a firefox extension.&lt;br /&gt;&lt;br /&gt;I wanted the user to be able to select a piece of text, right click, pick my extension and have a dialog box where one of the fields contains the selected text.&lt;br /&gt;&lt;br /&gt;Since I &lt;i&gt;already knew&lt;/i&gt; how to do this I flew straight in and spent ages fiddling round in the dialog box's code with parent.getSelection(), parent.window.getSelection, parent.window.context.getSelection() and every other combination including parents, windows and selections that I could think of.  Not a chance.&lt;br /&gt;&lt;br /&gt;For some reason I couldn't get the text from the parent window.  I suspect that firefox does not regard the window the user sees as the parent window, but I've not bothered looking to find out...&lt;br /&gt;&lt;br /&gt;Anyway, quite early on in I thought to mayself:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;'This would be easy if you could just pass arguments into the dialog box.  I could just get the selection in the code that kicks off the dialog box and there we go'.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;But since I knew window.open I knew this wasn't possible.&lt;br /&gt;&lt;br /&gt;An hour later I decided to take a look at openDialog, which is the call I was actually making to open the window.  It turned out that yep, you can pass arguments into the child dialog box.&lt;br /&gt;&lt;br /&gt;If only I'd just accepted that I don't know everything and looked at it an hour earlier I'd have saved myself a lot of stress.&lt;br /&gt;&lt;br /&gt;Note to self... RTFM!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112245124119309908?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112245124119309908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112245124119309908' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112245124119309908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112245124119309908'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/rtfm-again.html' title='RTFM (again)'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112238390398146416</id><published>2005-07-26T14:16:00.000+01:00</published><updated>2005-07-26T14:18:23.990+01:00</updated><title type='text'>CSS Rules</title><content type='html'>We love CSS here, we try to use it as much as possible to ensure that the look and feel of our application is easily skinnable.  We're pretty good at it, and we get by.&lt;br /&gt;&lt;br /&gt;This guy though... now HE knows what he's doing :-)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://xerocool.innereyes.com/uk_flag_made_with_css_only.php" target="_BLANK"&gt;UK Flag in CSS only&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112238390398146416?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112238390398146416/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112238390398146416' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112238390398146416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112238390398146416'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/css-rules.html' title='CSS Rules'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112238252070663097</id><published>2005-07-26T13:29:00.000+01:00</published><updated>2005-07-26T13:55:20.713+01:00</updated><title type='text'>Hardware Tokens</title><content type='html'>In our office, we all sit around a couple of sets of desks.  You may say that we're definitely not 'geographically challenged'.  Each pair can pretty clearly see every other pair.&lt;br /&gt;&lt;br /&gt;We don't store any source code on our local machines.  Since we're developing a PHP application we need to deliver it though a web server and it access a nice big Oracle database, and guess what... we've got a whole department geared up to supporting web servers and databases.  So we don't have local installs of databases and webservers, we don't administrate them, and none of us want to.&lt;br /&gt;&lt;br /&gt;So our workspaces live on a network drive.  Yep, Win CVS is a bit of a pain over a network, but for now we're living with it.  Our PHP files live on a web server so we can access them without running any uploads or anything.&lt;br /&gt;&lt;br /&gt;We've given our workspaces numbers, prefixed with the name of the project.  The source code lives in a folder with the name of the workspace and the database lives in a schema with the name of the workspace.  Your pair is working in workspace 'Laurel1', you get set of source code 'Laurel1' and database schema 'Laurel1'.  Nice and simple.&lt;br /&gt;&lt;br /&gt;The only problem is, if no one individual owns a particular workspace, then how do we know which pair is allowed to work in which workspace?&lt;br /&gt;&lt;br /&gt;We have mutually exclusive hardware tokens.  Or, to put it another way... we have paper flags that we put into holsters taped onto the back of our flat screen monitors.&lt;br /&gt;If you've got the Laurel1 flag then you've got the Laurel1 workspace, and only your pair is allowed to change the workspace.&lt;br /&gt;If you 've not got the Laurel1 flag then you can't change the Laurel1 workspace.&lt;br /&gt;&lt;br /&gt;Since everyone's living so close, it's easy to see the flags.  In the very rare situation where a pair is working remotely, they phone up a proxy to get their flag, a humorous effigy is made of one of the pair, and the flag is given to the effigy.&lt;br /&gt;&lt;br /&gt;We have a spare workspace too...&lt;br /&gt;&lt;br /&gt;We call it integrate*.  It's the workspace where the integration takes place.  In order to do a large scale commit you must have the integrate workspace token.  If you don't then you don't commit.  The pair with the token is the pair that's currently doing an integration.  It has the advantage of a separate build machine since you're forced to check in from your dev space and check out to the integrate space and everyone can see you're doing it.&lt;br /&gt;&lt;br /&gt;Our hardware tokens make it easy to see who's working where.&lt;br /&gt;&lt;br /&gt;And they were fun to make.&lt;br /&gt;&lt;br /&gt;&lt;span class="technoratitag"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tags/Robert+Baillie" rel="tag"&gt;Robert+Baillie&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/Extreme+Programming" rel="tag"&gt;Extreme+Programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/pair+programming" rel="tag"&gt;pair+programming&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/workspaces" rel="tag"&gt;workspaces&lt;/a&gt;, &lt;a href="http://www.technorati.com/tags/development" rel="tag"&gt;development&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;* actually, we call it autotest, but if we got a new one we'd call it integrate... honest.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112238252070663097?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112238252070663097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112238252070663097' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112238252070663097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112238252070663097'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/hardware-tokens.html' title='Hardware Tokens'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-10846234.post-112230897635123344</id><published>2005-07-25T17:23:00.000+01:00</published><updated>2005-07-26T08:57:14.080+01:00</updated><title type='text'>The saddest post ever</title><content type='html'>One of the things that I had to come to terms with about 18 months ago was the fact that I needed to understand regular expressions.&lt;br /&gt;The Oracle world has yet to really embrace these strange beasts, and so I was completely hidden from their glory.&lt;br /&gt;&lt;br /&gt;Now, I've reached the point where I'm going to do possibly the saddest thing in the whole world.  I'm going to post a recent regular expression onto this blog... here... now.&lt;br /&gt;&lt;br /&gt;For a soon to arrive firefox extension, I needed to extract a number of complete sentences from a piece of text that numbered at least 100 characters in length.  Here's how I did it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;     (.|\n){200,}?[\.!\?\)"\n\r]+&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OK, if you use apostrophes as quotation marks it falls down a little, but then you deserve a slap anyway ;-)&lt;br /&gt;&lt;br /&gt;For being so sad, I must now go and whip myself with a birch branch.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Updated:&lt;/b&gt; OK, so I'm hoping that it now deals with newlines properly as well ;-)  Serves me right!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10846234-112230897635123344?l=robertbaillie.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://robertbaillie.blogspot.com/feeds/112230897635123344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=10846234&amp;postID=112230897635123344' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112230897635123344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/10846234/posts/default/112230897635123344'/><link rel='alternate' type='text/html' href='http://robertbaillie.blogspot.com/2005/07/saddest-post-ever.html' title='The saddest post ever'/><author><name>Rob Baillie</name><uri>http://www.blogger.com/profile/06513796097645814224</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='23' height='32' src='http://photos1.blogger.com/hello/75/6480/320/IMG_0706-1.jpg'/></author><thr:total>0</thr:total></entry></feed>
