<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:gd="http://schemas.google.com/g/2005" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;Ck4AQXozeip7ImA9Wx5TFU8.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662</id><updated>2010-07-30T16:15:40.482-04:00</updated><title>Abakas</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.abakas.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.abakas.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>667</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/Abakas" /><feedburner:info uri="abakas" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;Dk8FR386eip7ImA9Wx5TFUw.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-3558910408512807739</id><published>2010-07-30T11:53:00.004-04:00</published><updated>2010-07-30T14:33:36.112-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-30T14:33:36.112-04:00</app:edited><title>Are You Really a Team?</title><content type="html">These days, many of us work in Agile teams or in integrated SCRUM teams. We're no longer the development team and the test team and the design team. Instead, we're one team working together to ship product. At least, that's what we tell people.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If this is  you, though, are you &lt;i&gt;really&lt;/i&gt; a team? Is this really all one group going toward a single goal?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Three simple questions will help figure this out:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Do you assign tasks to individuals?&lt;/li&gt;&lt;li&gt;Do developers do test tasks when the team is crunched? Do testers help with system configurations when the team is trying to get set up for new development?&lt;/li&gt;&lt;li&gt;When a bug is found in the field, who gets yelled at and tries to figure out how to prevent that kind of problem from making it out to customers again? Your testers? Or your team?&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;If you're still playing "developer" and "tester" roles, then it doesn't matter whether you call yourself one team or two, you're still restricting yourself. You're still saying that you do your bit, the other guy will do his bit, and if we're all good and lucky, then good things will happen. That's not a team. That's a group of people.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A team is a group of people working toward the same goal. Some have more experience than others in different things, but if you're really a team, then each member will happily do whatever is standing between the team and release.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The difference between a group and a team is very simple:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Teams succeed together and teams fail together. &lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Groups pass or fail, each individual alone.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;If you want to be a team, be a team, not a group.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-3558910408512807739?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/HaDpVQVvqNw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/3558910408512807739/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/are-you-really-team.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/3558910408512807739?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/3558910408512807739?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/HaDpVQVvqNw/are-you-really-team.html" title="Are You Really a Team?" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>3</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/are-you-really-team.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0YNRnkyeSp7ImA9Wx5TE0g.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-1136651773641216801</id><published>2010-07-28T13:26:00.004-04:00</published><updated>2010-07-28T17:06:37.791-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-28T17:06:37.791-04:00</app:edited><title>Tales from the Phone Screen</title><content type="html">I've been doing a lot of phone screening lately, and there have been some real doozies. I've heard all sorts of things:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;"I left my last job because everyone was incompetent but me. It went through two whole teams like this." &lt;/i&gt;&lt;/div&gt;&lt;div&gt;Wow - ego coming through! There are definitely incompetent people out there but to run into such a high proportion  says that you either have truly terrible luck .... or that there's something wrong with your idea of competence.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;"Oh, my weakness is I don't do automation."&lt;/i&gt;&lt;/div&gt;&lt;div&gt;This is only strange once you know that the position is for a QA Automation Engineer.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;"I'm just looking for a job before my skills get too rusty."&lt;/i&gt;&lt;/div&gt;&lt;div&gt;On the one hand, this one is kind of sad. On the other hand, he's not doing anything to keep up or expand his skills, apparently, and that's a problem. I don't want to have to push for every new learning experience.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There are a lot of decent resumes out there. Phone screens really weed a whole lot out, though. Be careful what you say; the hiring manager is probably listening.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-1136651773641216801?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/iGw-D4Q4JMg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/1136651773641216801/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/tales-from-phone-screen.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/1136651773641216801?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/1136651773641216801?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/iGw-D4Q4JMg/tales-from-phone-screen.html" title="Tales from the Phone Screen" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/tales-from-phone-screen.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEANQX8-cCp7ImA9Wx5TEUo.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-6047400685340482494</id><published>2010-07-26T15:19:00.004-04:00</published><updated>2010-07-26T15:33:10.158-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-26T15:33:10.158-04:00</app:edited><title>Jira and SOAP: Part III</title><content type="html">The saga of using Perl code to communicate via Jira's SOAP API. See &lt;a href="http://blog.abakas.com/2008/12/perl-soap-interface-to-jira.html"&gt;part I&lt;/a&gt; and &lt;a href="http://blog.abakas.com/2010/07/perl-soap-interface-to-jira-part-ii.html"&gt;part II&lt;/a&gt; here. In  part I, we logged and updated issues. In part II, we were able to get status information about an issue. In our final part, we'll actually reopen the issue:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#####################################################################&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# Reopen a Jira issue&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @param    params{issue}      The issue key (e.g., PERMA-12345)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @param    params{assignee}   The intended assignee&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @return   The jira issue number&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;##&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sub reopenIssue {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my ($self, %params) = assertMinArgs(1, @_);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  $params{issue } ||= '';&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my %issue;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  $issue{issueKey} = SOAP::Data-&gt;type(string =&gt; $params{issue});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  $issue{assignee} = $params{assignee};&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  $issue{summary} = $self-&gt;_getIssueSummary(issue =&gt; $params{issue});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  $issue{newStatus} = SOAP::Data-&gt;type(string =&gt; "3"); #reopened&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $result = $self-&gt;_request('progressWorkflowAction', 1, \%issue);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  return $result;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's pretty simple, in the end. We're using the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;progressWorkflowAction&lt;/span&gt; call, which changes an issue from one status to another. We simply create our issue object with all the required attributes (in our case, summary, assignee, the new status, and the issue key) and then call progressWorkflowAction. This call will fail if the user doesn't have permission to make that transition, or if the transition doesn't apply (e.g., attempting to close a closed issue).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And that ends the Jira and SOAP tutorial, at least for now. Good luck!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-6047400685340482494?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/3zh3Oyj4D_Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/6047400685340482494/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/jira-and-soap-part-iii.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6047400685340482494?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6047400685340482494?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/3zh3Oyj4D_Q/jira-and-soap-part-iii.html" title="Jira and SOAP: Part III" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/jira-and-soap-part-iii.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkABR3k5eip7ImA9WxFaGE4.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-5871152413954264530</id><published>2010-07-22T10:34:00.000-04:00</published><updated>2010-07-22T17:39:16.722-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-22T17:39:16.722-04:00</app:edited><title>Interview Questions: Junior Candidates</title><content type="html">&lt;div&gt;When I'm interviewing candidates, I like to ask a lot of questions and present a lot of different types of problems to see how the candidate thinks. There are some things that I look for in more junior candidates specifically. For junior candidates, I'm interviewing for potential more than for what they've done, and that shades what I ask.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;"You've been dropped in the middle of  the Linux kernel, never having worked on kernels before, and asked to fix a thread deadlock. How do you go about it?"&lt;/b&gt;&lt;/div&gt;&lt;div&gt;With this question, I'm looking to see how the candidate learns. Our system is not particularly small, so I want to see how they approach it. Answers that imply they will try to learn the entire system before doing anything are generally not good; this isn't something you can hold entirely in your head without living in it for a while, and I want someone who's going to jump in. Incidentally, we use Linux, so this question isn't totally off base.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;"When you're working on a project in a team, how often do you check in? Any tricks or preferences there?"&lt;/b&gt;&lt;/div&gt;&lt;div&gt;With this question, I want to see how the candidate works in a team, and how the candidate approaches source control. Some candidates will go away for weeks or months and build a huge, beautiful, complete thing, and then have a heck of a time checking in because all the other code has changed in the meantime (this is a red flag). Other candidates check in near constantly, which means lots of reviews (also a red flag). Every team falls somewhere along this spectrum; I want to see if the candidate matches our rhythm, or is even aware there is a rhythm to match.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;"Your team of five has been handed a feature to build: you're going to build the crash recovery mechanism. How do you split up the work?"&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Junior candidates won't actually be dividing work, but they'll be on a team, and I want to know how they're going to work with that team. One danger of really junior candidates is that they mostly have experience working on school projects or isolated code areas, and often alone. Our company doesn't work that way, and we need to know that a candidate is going to be comfortable building part of a feature when the other part is being built by a teammate at the same time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Most of these questions revolve around seeing how a junior candidate works in a team environment, since they likely don't have this much experience. We can teach coding skills, we can teach learning techniques, and we can teach development practices, but only to someone who is aware there is something to learn.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-5871152413954264530?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/XYLPk2dvWEY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/5871152413954264530/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/interview-questions-junior-candidates.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5871152413954264530?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5871152413954264530?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/XYLPk2dvWEY/interview-questions-junior-candidates.html" title="Interview Questions: Junior Candidates" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/interview-questions-junior-candidates.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0IMQXs7fyp7ImA9WxFaFk4.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-7575812683433857991</id><published>2010-07-20T09:13:00.001-04:00</published><updated>2010-07-20T09:13:00.507-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-20T09:13:00.507-04:00</app:edited><title>Find the Oracle</title><content type="html">In testing, an oracle is something we look to that can define the "should" of an application. The oracle (or oracles) tell us what an application should do, how the UI ought to look, how the application ought to perform, etc.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Oracles can take many forms:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;requirements specifications&lt;/li&gt;&lt;li&gt;screen mockups&lt;/li&gt;&lt;li&gt;other (competing or complimentary) products&lt;/li&gt;&lt;li&gt;style guides (yours or someone else's)&lt;/li&gt;&lt;li&gt;similar or related functions in the same application&lt;/li&gt;&lt;li&gt;a previous or alternate version of the application being tested (think porting projects)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;When you start to test your application, one of your first jobs is to find the oracle(s). This will help guide your future tests, bugs identified, and behavioral expectations.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sometimes finding the oracle is easy; for example, if you're handed a requirements specification, then you've got at least one of your oracles. Other times, you'll have to get creative.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For example, a friend recently approached me with a problem:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;"We're processing files, and we discovered that on this file server with 573million files, we detected 19 million ZIP files. Is that reasonable?"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is a problem in search of an oracle. It's infeasible at that scale to hand-check all of the files. We're not yet sure we can trust our detection software, though; after all, that's what we're testing. So what's our oracle?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Seems hopeless. "Hopeless" is just another word for "needs creativity". We can do this.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Our problem revolves around the percentage of files on a file server that are of a certain type. There are two ways I think to go about this:&lt;/div&gt;&lt;div&gt;1. run one or more other file type detectors and see how much they agree with my results&lt;/div&gt;&lt;div&gt;2. find other file servers (or reports about other file servers) with a typical file type breakdown. Academic papers, storage vendors, and OS vendors are great sources for this kind of information.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What would you use for an oracle in this situation?&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-7575812683433857991?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/VC6AFh03kWk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/7575812683433857991/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/find-oracle.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/7575812683433857991?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/7575812683433857991?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/VC6AFh03kWk/find-oracle.html" title="Find the Oracle" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>3</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/find-oracle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UESHk5fSp7ImA9WxFaEk0.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-247350315102767356</id><published>2010-07-14T16:48:00.009-04:00</published><updated>2010-07-15T10:46:49.725-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-15T10:46:49.725-04:00</app:edited><title>Perl SOAP Interface to Jira (part II)</title><content type="html">A while ago, we wrote a &lt;a href="http://blog.abakas.com/2008/12/perl-soap-interface-to-jira.html"&gt;bit of Perl code that uses Jira's SOAP interface&lt;/a&gt; to log bugs, add comments, update tickets, and do other fun stuff. I recently extended this to get the current status of an issue out of Jira. We use this to look for bugs that are closed that we're still updating (whoops!).&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's the code:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;######################################################################&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# Issue a request to the Jira server and get a response.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @param cmd            The name of the command being sent&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @param checkResult    Whether or not to check the result of the command&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @param params         The parameters of that command&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @return               The result of the command&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;##&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sub _request {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my ($self, $cmd, $checkResult, $params) = assertMinArgs(3, @_);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $soap = SOAP::Lite-&gt;proxy(&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    "https://jira.permabit.com/rpc/soap/jirasoapservice-v2?wsdl");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $auth = $soap-&gt;login($self-&gt;{jiraUser}, $self-&gt;{jiraPwd});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $s = SOAP::Serializer-&gt;new;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $doThis;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  if ($cmd eq "addComment") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'issue'},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;                          $params-&gt;{'comment_obj'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "getComponents") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'project'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "getStatuses") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result());&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "createIssue") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'issueDef'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "updateIssue") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'issue'},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;                          [$params-&gt;{'rfv'}]);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "getIssueById") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'issueId'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "getIssue") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'issueKey'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "getStatuses") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'issueKey'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "getVersions") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'project'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } elsif ($cmd eq "getIssuesFromFilter") {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    $doThis = $soap-&gt;$cmd($auth-&gt;result(), $params-&gt;{'filterId'});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  } else {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    croak("Unexpected command: $cmd");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  if ($checkResult &amp;amp;&amp;amp; defined $doThis-&gt;faultcode) { # whoops something went wrong&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    croak("Error running command: $cmd\nGot: " . $doThis-&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;faultstring&lt;/span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  return $&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;doThis&lt;/span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;snip&gt;&lt;/snip&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#####################################################################&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# Get issue Status by key&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @param    params{issueKey}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;# @return   The jira issue number&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;##&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sub getIssueStatus {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my ($self, %params) = assertMinArgs(1, @_);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  $params{issueKey} ||= '';&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my %issue;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  $issue{issueKey} = SOAP::Data-&gt;type(string =&gt; $params{issueKey});&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $jStatuses = $self-&gt;_request('getStatuses',1);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $jIssue = $self-&gt;_request('getIssue', 1, \%issue);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $jIssueStatusId = $jIssue-&gt;result()-&gt;{'status'};&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  my $jIssueCurrStatus;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  foreach my $status (@{$jStatuses-&gt;result()}) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    if ($status-&gt;{'id'} eq $jIssueStatusId) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;      $jIssueCurrStatus = $status-&gt;{'name'};&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;    }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  return $&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;jIssueCurrStatus&lt;/span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first method is what actually submits the SOAP-encoded request to &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;Jira&lt;/span&gt;. There are different request types with different parameters, but the ones were interested in are:&lt;/div&gt;&lt;div&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;getStatuses&lt;/span&gt;: takes in the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;authentication&lt;/span&gt; token only, and returns all statuses defined in &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;Jira&lt;/span&gt; (regardless of project)&lt;/div&gt;&lt;div&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;getIssue&lt;/span&gt;: given the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;authentication&lt;/span&gt; token and an issue key (the thing you see in the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;UI&lt;/span&gt;: e.g., "&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;QA&lt;/span&gt;-1"), this returns the status. &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;Jira&lt;/span&gt; returns the ID of the status - for example, "1" - and we use the statuses we grabbed to translate that to a human-readable status name - for example, "open".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Once we have the status we can do things like &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;automatically&lt;/span&gt; reopen issues if we're getting logs for an occurrence (hey, it must have happened again, so why not reopen the issue?).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You may choose to do this differently, but hopefully it will give you some pointers on how to handle the Jira interface.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-247350315102767356?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/BFSCOpsHv-0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/247350315102767356/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/perl-soap-interface-to-jira-part-ii.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/247350315102767356?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/247350315102767356?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/BFSCOpsHv-0/perl-soap-interface-to-jira-part-ii.html" title="Perl SOAP Interface to Jira (part II)" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/perl-soap-interface-to-jira-part-ii.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UGQX87fyp7ImA9WxFbGUk.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-5355008746278082334</id><published>2010-07-12T09:27:00.001-04:00</published><updated>2010-07-12T09:27:00.107-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-12T09:27:00.107-04:00</app:edited><title>Sprint Renegade</title><content type="html">When you're constructing a sprint backlog, there are almost always more things to do than you could possibly handle. Marketing wants some things, sales could close a big deal if only they had some new feature, product management feels like this other thing would be a great differentiator, support has a few requests for additional logging, oh, and development also has a few technical tasks (build restructuring, etc.) they really ought to handle.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So much to do, and unfortunately, those technical tasks often simply don't make it into the sprint. Over time, not having that technical keep up causes increases in technical debt and slows down delivery. Eventually, you have a lot of technical tasks, and yet everyone else still wants new features. Velocity is slowing down, builds are taking longer, and there's more general muttering about things not being clean in the development and test environments.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's time to introduce the sprint renegade.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The sprint renegade is someone who effectively leaves the team for a sprint. He goes off and simply works on tech tasks to make it easier for the rest of the team to meet the obligations of the backlog. This reduces your potential velocity for that sprint by one person, but it boosts your long-term velocity by taking care of some of the drag on the team. Repeat this for each sprint until velocity is back up where you want it to be.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The general idea of a sprint renegade is that internal tech tasks are very hard to sell onto a backlog, and they will often lose in the face of customer-facing tasks. If that's true in your company, sometimes drastic measures are in order, temporarily.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If your technical debt has crept up to a point where it's interfering with delivering, find yourself a renegade. You'll be glad you did.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-5355008746278082334?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/nlOqWDWtF_8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/5355008746278082334/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/sprint-renegade.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5355008746278082334?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5355008746278082334?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/nlOqWDWtF_8/sprint-renegade.html" title="Sprint Renegade" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/sprint-renegade.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYEQH4_cCp7ImA9WxFbFkw.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-816028203359638956</id><published>2010-07-08T14:35:00.000-04:00</published><updated>2010-07-08T14:35:01.048-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-08T14:35:01.048-04:00</app:edited><title>Around the Software</title><content type="html">When we're estimating a feature or a story or a software change, we almost always think of the following:&lt;div&gt;&lt;ul&gt;&lt;li&gt;time to design the feature&lt;/li&gt;&lt;li&gt;time to implement the feature&lt;/li&gt;&lt;li&gt;time test the feature&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;And we come out with an estimate: "2 engineers and 1 tester for a week" (or whatever).&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That estimate fails to account for all the other work that needs to be done. We have yet to estimate:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;test infrastructure extensions&lt;/li&gt;&lt;li&gt;other infrastructure extensions (e.g., do we need to provision a hosted site for centralized client logging?)&lt;/li&gt;&lt;li&gt;documentation modification or creation&lt;/li&gt;&lt;li&gt;configuration analysis and best practices development (to answer the questions customers have)&lt;/li&gt;&lt;li&gt;marketing, presentation, and other collateral updates&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Just because the product code has been written, and even if the tests have been done, a feature isn't done yet. Don't forget to plan for - and actually do - all the stuff around the software.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-816028203359638956?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/epoWHvyiShg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/816028203359638956/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/around-software.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/816028203359638956?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/816028203359638956?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/epoWHvyiShg/around-software.html" title="Around the Software" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/around-software.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEGQX4zfCp7ImA9WxFbFE8.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-3516768059851872572</id><published>2010-07-06T09:57:00.001-04:00</published><updated>2010-07-06T09:57:00.084-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-06T09:57:00.084-04:00</app:edited><title>Code Smell: Model Mismatch</title><content type="html">For any system, there are several potential models of that system:&lt;div&gt;&lt;ul&gt;&lt;li&gt;the user's mental model of how the system works and how its different parts relate&lt;/li&gt;&lt;li&gt;the model in the code, implied by the objects and their relationships&lt;/li&gt;&lt;li&gt;the design of the system (hopefully this matches the code, but not always!)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;When all the models match, you have an effective system. When the models don't match, that's a code smell. At it's core, a code smell is a warning of future difficulty. And when the user's model doesn't match the code, it's going to be hard to add features the user wants because the code has different ideas.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For example, I'm working on a project that is a basic web application. Users sign up and are put into teams for contests. Everyone on the team that wins the contest gets a prize, and then the whole process starts over with new contests and new prizes. A user made a request to be able to see how much their team had won, total. Sounds fairly simple. It wasn't.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There was a discrepancy in the models. The user's mental model of the system said that "teams win prizes". The underlying system code was that "contests have prizes".  This model mismatch meant that instead of simply getting all prizes for a team, the developer had to get all the contests a team had participated in and then get all the prizes awarded for that contest and then filter by team. It was real work!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So the developer tried to refuse to do the request because "the system isn't designed that way." Now we've got a real problem. There are three ways forward:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Refuse to fix it.&lt;/b&gt; This is tempting sometimes, but it's an unacceptable answer in many cases. It's a user request, and assuming it gets approved, you'll need to implement it. Features aren't chosen by how hard they are, but by their value. Get over it - "it's hard" is not an excuse.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Fix it&lt;/b&gt;. It's possible to make it work, even if it's in a roundabout way. Note that this does make your code more closely resemble a mountain of spaghetti.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Reconcile models.&lt;/b&gt; Whether you change the user's model (often implied through the user interface) or change your underlying system design, the right thing to do often is to reconcile the models. This is how you ultimately clean up the code smell - by &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;rearchitecting&lt;/span&gt;, refactoring, or modifying the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;UI&lt;/span&gt; and documentation so that what the user understands matches the design and matches the code. &lt;/li&gt;&lt;/ul&gt;&lt;div&gt;When you get a request or a bug, and your first response is "oh, that's weird" or "oh, that's a lot harder than it sounds", ask yourself what the model is that came to that request. You may have just found a code smell.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-3516768059851872572?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/FH3SMDhYtEw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/3516768059851872572/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/code-smell-model-mismatch.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/3516768059851872572?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/3516768059851872572?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/FH3SMDhYtEw/code-smell-model-mismatch.html" title="Code Smell: Model Mismatch" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/code-smell-model-mismatch.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYHQnkzfyp7ImA9WxFbEEo.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-6215892619630371367</id><published>2010-07-02T09:01:00.003-04:00</published><updated>2010-07-02T09:08:53.787-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-02T09:08:53.787-04:00</app:edited><title>Severity What?</title><content type="html">I've seen a lot of project orders that include language like this:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;"The product shall not ship with any known unresolved severity 1 or severity 2 defects."&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Severity 1 is usually defined as crash, data loss, and/or total system unavailability. (In my team, we call this "the big oops".) Severity 2 is usually defined as loss of core functionality of the system.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But when the project's late, those definitions sometimes get changed because, "darnit, we've got to ship!"&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And then something like &lt;a href="http://www.cio.com.au/article/351489/risks_known_when_pay_system_went_live/?eid=-1050"&gt;this&lt;/a&gt; happens.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Beware changing definitions - there be dragons!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-6215892619630371367?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/3wJy2nYtP_A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/6215892619630371367/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/07/severity-what.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6215892619630371367?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6215892619630371367?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/3wJy2nYtP_A/severity-what.html" title="Severity What?" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.abakas.com/2010/07/severity-what.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QGSXYyeSp7ImA9WxFUGU4.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-6490049725447574151</id><published>2010-06-30T10:28:00.004-04:00</published><updated>2010-06-30T18:02:08.891-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-30T18:02:08.891-04:00</app:edited><title>Up To Date</title><content type="html">&lt;div style="text-align: left;"&gt;I've been in more than one series of events like this:&lt;/div&gt;&lt;div&gt;"We're starting to put down some dates for the Sweetums* release."&lt;/div&gt;&lt;div&gt;"Okay. What's in it?"&lt;/div&gt;&lt;div&gt;"Stories X, Y, and Z. We've put a brief writeup of each in Jira.**"&lt;/div&gt;&lt;div&gt;(pause while we go look at the stories and estimate the test effort on each as well as the estimates on how many bugs will come through, etc.)&lt;/div&gt;&lt;div&gt;"All right, we're probably looking at 4 weeks between feature freeze and code freeze, and 2 weeks after code freeze for final verification."&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And then time passes. We start coding tests, looking at early builds, etc. Development, well, develops.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Inevitably, something changes. We might get a new customer who really wants a feature, or we might find a bug that needs to be fixed for the next release, or we decide to drop a story that seemed important but turned out not to really matter. Net net, something changes. So we have another conversation:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;"What's the new estimate for this release?"&lt;/div&gt;&lt;div&gt;"Umm.... is everything updated in Jira?"&lt;/div&gt;&lt;div&gt;"Yeah, except we're not doing story Y, and we're doing story W."&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At this point the conversation needs to stop. We have a process. We have a place that defines what's in a release. Let's use it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you've defined a place to keep information - the contents of a release, bugs and what's been done on them, employee phone numbers - then that place should be kept up to date. It seems like a lot of work sometimes, to keep updating the information location. It's worth it, though. It saves a lot of other people time, and it works to train them to trust the information location. If it's up to date always, then people will come to trust it. You'll spend less time explaining status and more time working.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And less time on status and more time working sounds great to me.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;* We name our releases after muppets where I work. This is Sweetums:&lt;/div&gt;&lt;div&gt;&lt;a href="http://3.bp.blogspot.com/_hrCjqEFBjyE/TCu6oBhlI5I/AAAAAAAAAN4/g2X274VwwO0/s1600/images.jpeg"&gt;&lt;img src="http://3.bp.blogspot.com/_hrCjqEFBjyE/TCu6oBhlI5I/AAAAAAAAAN4/g2X274VwwO0/s400/images.jpeg" border="0" alt="" id="BLOGGER_PHOTO_ID_5488685767647896466" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 104px; height: 78px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;** We keep our stories in Jira so we can track them and do things like attach bugs to stories, etc.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-6490049725447574151?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/aejn2F3tmkA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/6490049725447574151/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/up-to-date.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6490049725447574151?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6490049725447574151?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/aejn2F3tmkA/up-to-date.html" title="Up To Date" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_hrCjqEFBjyE/TCu6oBhlI5I/AAAAAAAAAN4/g2X274VwwO0/s72-c/images.jpeg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/up-to-date.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcCQXk4eSp7ImA9WxFUF04.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-5959834890908966859</id><published>2010-06-28T10:25:00.004-04:00</published><updated>2010-06-28T10:41:00.731-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-28T10:41:00.731-04:00</app:edited><title>An Error</title><content type="html">What's wrong with this test code?&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;def testFailureMode&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt; my result = runMyGreatTestCommandHere&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt; if result.error&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;   puts "Test PASSED. We wanted to make sure it errored properly"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt; else&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;   puts "Test FAILED. Expected to error."&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt; end&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;On the surface, it's not so bad. It runs a test that should produce an error, and makes sure an error is produced. Let's dig one step deeper.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We're testing a failure mode, which means we're expecting not just any error, but an error we induced. For example, let's say we're testing login with a bad password, and want to see that there's an error "incorrect password". However, this test would pass if any other error occurred. If the page never loaded, or returned a stack trace instead of a login page, this test would pass just fine. After all, it got an error!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When you're testing failure modes, make sure not only that you get an error, but that you get the error you meant to get.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-5959834890908966859?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/WM1NNoRuHsw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/5959834890908966859/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/error.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5959834890908966859?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5959834890908966859?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/WM1NNoRuHsw/error.html" title="An Error" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/error.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8CQX86fCp7ImA9WxFUFEU.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-8565189376877025205</id><published>2010-06-25T11:45:00.006-04:00</published><updated>2010-06-25T12:54:20.114-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-25T12:54:20.114-04:00</app:edited><title>Reboot Heuristic</title><content type="html">Let's say you had to reboot your primary work computer, right now. How much would you lose? &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;How many windows do you have open with things half done?&lt;/div&gt;&lt;div&gt;How many emails partially written?&lt;/div&gt;&lt;div&gt;How many IMs to read? To respond to?&lt;/div&gt;&lt;div&gt;How many browser tabs with articles to read?&lt;/div&gt;&lt;div&gt;How many IDE windows with various code changes?&lt;/div&gt;&lt;div&gt;How many tests running in various terminals?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And now you have to reboot.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;How many different tasks do you have to finish or at least get to a stopping spot before you could reboot?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Your primary workstation shows you how much of a multitasker you think you are. &lt;a href="http://www.thenewatlantis.com/publications/the-myth-of-multitasking"&gt;You're not really multitasking, though&lt;/a&gt;. You're simply not finishing things - maybe a lot of things.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm not particularly paper dependent - there are zero pieces of paper on my desk as I write this - so I use the reboot heuristic to remind myself to finish things. Ready?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;If I have to touch more than three things before I would be willing to reboot, then I can't start something new until I've finished at least one.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The reboot heuristic is a reminder to myself that I need to finish things, not just start them. If I write an email and get partway through before I remember that test I wanted to start, that's okay. Repeat several times, though, and I'm spending more time switching between tasks than actually doing tasks. So three is my limit: when I'm doing more than three things, it's time to finish something.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Starting things is great. Finishing things is better. Multitasking is part of working these days, but not letting it get out of control - that's a heuristic I need.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-8565189376877025205?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/Ho7rAq5Llhc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/8565189376877025205/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/reboot-heuristic.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8565189376877025205?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8565189376877025205?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/Ho7rAq5Llhc/reboot-heuristic.html" title="Reboot Heuristic" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/reboot-heuristic.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8MQnkzfip7ImA9WxFUE04.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-1950372506111760285</id><published>2010-06-23T18:25:00.000-04:00</published><updated>2010-06-23T18:41:23.786-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-23T18:41:23.786-04:00</app:edited><title>On Telling Somebody</title><content type="html">&lt;div&gt;Testing is about information. We do things to systems in order to get information about the system. From there, we make educated guesses about the systems likely behavior in the future. For example, I might test an installation on a certain hardware configuration, and then from there make statements about the installer's likely behavior on the same hardware configuration at a customer site. Such statements probably take the form, "yup, it'll work", and they can be made explicitly or they can be made implicitly ("Pass").&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Testing can be broken down into those two parts:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;doing something and seeing what happens&lt;/li&gt;&lt;li&gt;predicting future events&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;If you haven't done something and seen what happens, you haven't tested. If you haven't told someone about it, you haven't tested.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's easy to get caught up in the doing, but don't forget the communication side of it. Tell someone - in a test plan, in an email, in a green bar in your IDE, in conversation. You're not done until you've told someone.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-1950372506111760285?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/NPEqefDyFgk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/1950372506111760285/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/on-telling-somebody.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/1950372506111760285?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/1950372506111760285?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/NPEqefDyFgk/on-telling-somebody.html" title="On Telling Somebody" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/on-telling-somebody.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUQAQH4_fSp7ImA9WxFUEkw.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-6235033219389341797</id><published>2010-06-22T08:45:00.000-04:00</published><updated>2010-06-22T09:29:01.045-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-22T09:29:01.045-04:00</app:edited><title>"Why" Is a Question</title><content type="html">"Why don't we test X?"&lt;div&gt;"Why do we wait to check in until the tests are done?"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All too often, we take these questions as a suggestion that we change our behavior. "Why don't you foo?" is something we take as "Do foo." "Why do we do bar?" is a question we take as "Don't do bar."&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But it is a question, not a directive.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sometimes the correct response really is, "No good reason. Let's change that." Sometimes, though, there is a reason why we do things. Maybe we don't test X because we're getting rid of that feature, or we don't have the hardware to do it. Maybe we do wait to check in until the tests are done to make sure our coverage stays high and we don't create technical debt.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Answer the question. Then change your behavior - but only if that's the right answer to the question.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-6235033219389341797?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/6NZNDXCz520" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/6235033219389341797/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/why-is-question.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6235033219389341797?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6235033219389341797?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/6NZNDXCz520/why-is-question.html" title="&quot;Why&quot; Is a Question" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/why-is-question.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUQDRHo7cCp7ImA9WxFVF0U.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-6707638743808910408</id><published>2010-06-17T09:13:00.004-04:00</published><updated>2010-06-17T10:02:55.408-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-17T10:02:55.408-04:00</app:edited><title>Test Near Development</title><content type="html">&lt;div style="text-align: left;"&gt;Test Driven Development (TDD) is a great idea. You write some tests that say, basically, "this is what should happen." Then you write some code to make it so. It's really rather like waving your magic wand (or keyboard).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://1.bp.blogspot.com/_hrCjqEFBjyE/TBon1i7G52I/AAAAAAAAANw/fI-Va-d-LVE/s1600/images.jpeg"&gt;&lt;img src="http://1.bp.blogspot.com/_hrCjqEFBjyE/TBon1i7G52I/AAAAAAAAANw/fI-Va-d-LVE/s400/images.jpeg" border="0" alt="" id="BLOGGER_PHOTO_ID_5483739297138272098" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 90px; height: 135px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Repeat this often enough, the idea goes, and you have yourself a pretty good system, and a pretty good amount of code coverage so you can refactor safely.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All great things.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;TDD is hard. It's really difficult to do once you get beyond the first little bit. After all, as you keep adding and modifying tests, every change starts to take a little longer. Not much longer, but enough you notice and start to lose focus. And if it takes more than 8 seconds for something to happen - say, your tests to finish - you're likely to lose focus. (See &lt;a href="http://books.google.com/books?id=sf48ooRs7hgC&amp;amp;lpg=PP1&amp;amp;dq=isbn%3A1434995550&amp;amp;pg=PA73#v=snippet&amp;amp;q=8%20seconds&amp;amp;f=false"&gt;David Cornish and Dianne Dukette: The Essential 20&lt;/a&gt;).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The benefits of TDD, however, accrue as long as the tests exist when the feature exists. This holds true no matter if the feature is a single method or an entire new component. The goal of having tests to enable safe refactoring, to guide the implementation, is still met. We're just not dogmatic about product code vs test code: both product and tests are required; get there in the way that works for you as an engineer.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So on our team we practice test near development. We don't force people to write their tests first. Instead, we ensure that tests exist when the feature is complete. In practice, some engineers write a little code, then write some tests, then refactor a little code, then refactor some tests, then write some more code. The last thing written is usually the acceptance tests. We end up in the same place, but we've allowed the tests to come before, during, and after the development effort. I've taken to calling this "Test Near Development".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And you know what? It works for us.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Give it a shot sometime, if you're having trouble with TDD. It might be a gateway to help get you to full TDD, or it might just give you better test coverage that your team can sustain.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-6707638743808910408?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/oYf2-1_MOqI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/6707638743808910408/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/test-near-development.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6707638743808910408?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6707638743808910408?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/oYf2-1_MOqI/test-near-development.html" title="Test Near Development" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_hrCjqEFBjyE/TBon1i7G52I/AAAAAAAAANw/fI-Va-d-LVE/s72-c/images.jpeg" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/test-near-development.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0IBQHc4fip7ImA9WxFVF04.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-7961089523501962393</id><published>2010-06-16T16:23:00.002-04:00</published><updated>2010-06-16T19:39:11.936-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-16T19:39:11.936-04:00</app:edited><title>Asking</title><content type="html">Software is a community phenomenon. For almost everything, no matter what you're doing, someone else out there is doing or has done something similar. Maybe they're not doing the same big-picture thing, but someone else is doing that small task you have before you now.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You can ask for help. And you're really very likely to get an answer, but only if you ask the right way.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Whether you ask your team a question, or send out a request to a mailing list, or hit up a support forum, or hop onto an IRC channel, you need to remember a few basic things:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Describe what you're trying to do.&lt;/b&gt; Give the user some context so they don't suggest something that makes no sense. Don't take more than about three sentences to do this, at most.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Indicate you've tried something.&lt;/b&gt; This does two things: (1) shows you're trying; and (2) prevents people from suggesting something you've tried.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Ask a question. &lt;/b&gt;Make sure you actually ask a question. Be as specific as possible in describing what you need to move forward.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;Doing these things alone will greatly increase your chances of getting an answer. You've made it easier to understand your question, and therefore to answer it. Asking seems so easy, doesn't it?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-7961089523501962393?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/wss240GZon0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/7961089523501962393/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/asking.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/7961089523501962393?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/7961089523501962393?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/wss240GZon0/asking.html" title="Asking" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/asking.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIEQX48eyp7ImA9WxFVFUw.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-6889522464489886868</id><published>2010-06-14T06:15:00.003-04:00</published><updated>2010-06-14T06:15:00.073-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-14T06:15:00.073-04:00</app:edited><title>No, We Shouldn't Document That</title><content type="html">"We should document that" is not an uncommon phrase. It usually means "something is a little weird and our users might take it the wrong way, or maybe they already have taken it wrong." It's a place where you're doing something your users will find confusing or non-intuitive. Consider these examples:&lt;div&gt;&lt;ul&gt;&lt;li&gt;Our login page asks for "username" but we expect you to provide an email as your username. People keep clicking "Forgot username", so we should document that your username is the email you signed up with.&lt;/li&gt;&lt;li&gt;When patching the high-availability system, support must patch the passive half of the pair first. Patching the active half first leads to system downtime. We should document that for support.&lt;/li&gt;&lt;li&gt;We can register our system in DNS at installation time (go us!) but only if you provide the username and password of someone with permission to do so.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Every "we should document that" is a bug until proven otherwise. &lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you have to document it, most of the time that means you've designed it or implemented it in a confusing way. So yes, please document it. Then log it as a bug and make it less confusing. Sometimes that's as simple as changing a label - "email" instead of "username". Sometimes it's harder, like making a high-availability configuration patchable from either half with transparent failover. Sometimes, it's even dependent on a third-party system, and involves moving the documentation into the product (e.g., "please enter the username and password for a user with permission to create DNS entries") rather than removing documentation entirely.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So no, we shouldn't document that. Instead, we should fix that. Let's quit with the post facto workarounds and start actually fixing the weirdness in our application up front.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-6889522464489886868?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/OGG7oigBI34" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/6889522464489886868/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/no-we-shouldnt-document-that.html#comment-form" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6889522464489886868?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/6889522464489886868?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/OGG7oigBI34/no-we-shouldnt-document-that.html" title="No, We Shouldn't Document That" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>8</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/no-we-shouldnt-document-that.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4AR3Y5fyp7ImA9WxFVEkw.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-4516356586269427552</id><published>2010-06-10T17:39:00.001-04:00</published><updated>2010-06-10T19:52:26.827-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-10T19:52:26.827-04:00</app:edited><title>Found Time</title><content type="html">Unfortunately, often things take longer than planned. A meeting runs over, a quick bug fix takes hours, a feature that should have taken a few days takes nearly a week. Sometimes, though, the inverse is true. That hour-long meeting breaks up in 15 minutes. The bug fix that looked hard turned out to be a simple one-line change (and was elegant, too!). The time you would have spent is found time, and found time is a gift.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So what do you do with your found time?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You can go straight on to the next thing - your next backlog item, the next bug, the next meeting.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But you don't have to.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I use found time to do the nagging little things. These are things that are so unimportant you won't get to them normally. Nonetheless, they're small bumps in my day, things that don't go quite as smoothly as I wish. These are things that when I'm sitting at my desk, I won't schedule them because there's so much more important to do. But they're things that I run into every day.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I had a few hours today of found time. I used them to:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Update a script that spit out information in rows, and I always wind up sending that information to other people in columns. I made it spit out columns. Total time saved: 30 seconds, tops, but boy was that annoying having to transpose it. No more transposing!&lt;/li&gt;&lt;li&gt;Upgrade all my Rails gems. I'm just starting on a new project, and using the most current ones will be useful later on.&lt;/li&gt;&lt;li&gt;Update the &lt;a href="http://www.abakas.com"&gt;Abakas web site&lt;/a&gt; with some clearer services information.&lt;/li&gt;&lt;li&gt;Install &lt;a href="http://www.panic.com/transmit/"&gt;Transmit 4&lt;/a&gt;, which is a lot faster than the previous version.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Not one of these things was hugely important. Now that I've done a few of them, though, my day will be a bit smoother, and I'll be better able to concentrate on the truly important things.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Found time is a free gift. It's unscheduled and its yours. Use some of it for doing the nagging little things. Think of it as spring cleaning for your day!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-4516356586269427552?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/mdbatn05fu0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/4516356586269427552/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/found-time.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/4516356586269427552?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/4516356586269427552?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/mdbatn05fu0/found-time.html" title="Found Time" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/found-time.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEIMQHg8eSp7ImA9WxFVEE8.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-7424498894940124258</id><published>2010-06-08T15:04:00.004-04:00</published><updated>2010-06-08T15:49:41.671-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-08T15:49:41.671-04:00</app:edited><title>My Current Test Stack</title><content type="html">&lt;div&gt;I've been doing some functional testing on web applications. The applications are relatively simple, written in Ruby on Rails, and provide the following user interfaces:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;a GUI&lt;/li&gt;&lt;li&gt;SOAP and REST APIs&lt;/li&gt;&lt;/ul&gt;My current testing stack for functional tests is:&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Rspec.&lt;/b&gt; I use this for testing models primarily, and some of the controllers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Cucumber. &lt;/b&gt;This is where I do most of my testing of workflow, business logic, and integration between pieces. A lot of the tests take the form of "sign up through the GUI, register a machine through an API, upload user data through the API, view usage data through the GUI" (and variations thereon).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Selenium.&lt;/b&gt; I use this for the very few tests that cucumber can't handle because of JavaScript, rendering niceties, or other browser-type concerns.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm in the market to learn a new toy (I mean... tool!), so I'm turning it over to you, readers. What about this stack should I swap out and for what kinds of tests?&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-7424498894940124258?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/XI8cBcl6EUY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/7424498894940124258/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/my-current-test-stack.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/7424498894940124258?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/7424498894940124258?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/XI8cBcl6EUY/my-current-test-stack.html" title="My Current Test Stack" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/my-current-test-stack.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8CRHwzeSp7ImA9WxFWGU8.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-3761556121154061483</id><published>2010-06-07T11:32:00.001-04:00</published><updated>2010-06-07T12:41:05.281-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-07T12:41:05.281-04:00</app:edited><title>Analyze With Purpose</title><content type="html">Analysis is a double-edged sword. While in theory a quest for understanding is laudable, understanding rarely actually fixes a problem. In the end, our focus has to be on fixing a problem; analysis is one step on the way.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Analysis is a means to an end, not an end by itself.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead, we must perform analysis with an eye toward what we want to accomplish. Don't say, "we'd like to understand behavior X". Instead say, "the customer is seeing behavior X and wants behavior Y. Let's figure out how those two behaviors are different under the covers."&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Don't say, "that unexpected performance improvement should be analyzed." Instead say, "we'd like to make sure that the performance increase we're seeing isn't due to measurement error, some change in system input we haven't noticed, or caused by some transient state."&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In other words, don't simply set analysis or even understanding as a goal. Instead, set a goal of what you will do with that analysis and that understanding. That way you know when to stop analyzing and when to start doing.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-3761556121154061483?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/j11IOwW_2es" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/3761556121154061483/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/analyze-with-purpose.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/3761556121154061483?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/3761556121154061483?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/j11IOwW_2es/analyze-with-purpose.html" title="Analyze With Purpose" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/analyze-with-purpose.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMDQXw-fCp7ImA9WxFWFkU.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-8952772214315559008</id><published>2010-06-04T16:09:00.000-04:00</published><updated>2010-06-04T16:47:50.254-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-04T16:47:50.254-04:00</app:edited><title>"Can't"</title><content type="html">&lt;div&gt;How many of us have heard this phrase?&lt;/div&gt;&lt;div&gt;"We can't do that!"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Or even better...&lt;/div&gt;&lt;div&gt;"You can't do that!"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Almost every time, the person (or us) is perfectly capable of doing that, whatever "that" is. What we really mean is, "please oh please don't do that!" or "I'd really rather not do that". We say "can't". We mean "won't".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The trouble is that "can't" turns it into a dare. "You can't do that!" "Oh yeah? Watch me!". Whoops.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Telling someone they can't do something often has two effects:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Makes you sound rather whiny&lt;/li&gt;&lt;li&gt;Makes the person more likely to do it if only to prove that yes, they can&lt;/li&gt;&lt;li&gt;Makes the person more defensive of his position&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Take away the dare. Not "you can't do that!" but "here's why this should be done another way". You're much more likely to prevent "that" from happening (whatever it is) if you can provide an alternative rather than a dare.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-8952772214315559008?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/udGsB1YMSk0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/8952772214315559008/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/cant.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8952772214315559008?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8952772214315559008?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/udGsB1YMSk0/cant.html" title="&quot;Can't&quot;" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/cant.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYFSH0zfCp7ImA9WxFWFUU.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-5957669220012004698</id><published>2010-06-03T09:43:00.004-04:00</published><updated>2010-06-03T12:55:19.384-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-03T12:55:19.384-04:00</app:edited><title>CAST 2010</title><content type="html">I try not to do these shameless plugs too often, but I'm excited about this one.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm presenting a session at this year's CAST 2010 on Communicating with NonTesters. We'll talk about how to present information in a way that whoever you're talking to really understands it. Throughout the session we'll cover the dreaded "status report", the art of writing a defect explanation for a (probably angry) customer, and discuss how to write a risk assessment that doesn't make you look like you're panicking over nothing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'd love to see you there!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's the conference info:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p style="text-align: center; color: rgb(49, 101, 57); font-size: 24px; font-weight: bold;"&gt;Attend CAST 2010&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p style="text-align: center; font-weight: bold"&gt;The 5th annual Conference of the Association for Software Testing&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p style="text-align: center; font-weight: bold"&gt;August 2-4, 2010, Grand Rapids, Michigan, USA&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p style="text-align: center; font-style: italic; color: rgb(49, 101, 57); font-size: 24px; font-weight: bold;"&gt;"Skills in Testing"&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;About CAST&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;CAST reflects the AST's core mission: to build community amongst scholars, practitioners, and students for the advancement of the practice of software testing. In 2010, CAST aims to leverage peer collaboration to build an enhanced understanding of how various skills influence tester effectiveness.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;CAST offers a unique opportunity to learn and confer with others that simply isn’t found at other conferences. Each scheduled session allocates time for facilitated “open season” discussions that encourage participants to question and challenge the presentation. What takes place in the hallways, at receptions, and during meals and lightning talks truly sets CAST apart; for many attendees, the greatest value is derived from the opportunity to discuss and delve into the topics that matter to them.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p style="text-align: center;"&gt;More information and Registration: &lt;a href="http://www.CAST2010.org/"&gt;www.CAST2010.org&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-5957669220012004698?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/A_QprdIxs1U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/5957669220012004698/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/cast-2010.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5957669220012004698?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/5957669220012004698?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/A_QprdIxs1U/cast-2010.html" title="CAST 2010" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/cast-2010.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMNRHw-fyp7ImA9WxFWFEU.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-8555492528665872126</id><published>2010-06-02T08:43:00.004-04:00</published><updated>2010-06-02T08:58:15.257-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-02T08:58:15.257-04:00</app:edited><title>Change Tactics</title><content type="html">Given a problem, there are generally several ways to approach it. The problem might be an issue occurring at a customer site, or a test we want to conduct, or a scaling design goal, etc. In any of these situations, we have lots of choices in how we tackle it. We might start by looking at what changed to cause the issue to occur. We might begin with reviewing the architecture of massively scaling services similar to ours. We might use code review to identify possible flaws that we want to exercise with unit testing code.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All those are great, and worth trying.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But what if it's not working?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Is that "what changed?" question you've been asking yourself about a problem that showed up at a client site not getting you any answers? Did that massively scalable architecture that seems like it would provide some good ideas lead you down a rathole of trying to map your square peg to their round role?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Time to change tactics. If it's not working, stop doing it, and try something else.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So how do we know when to change it up versus when to pursue a given tactic?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;As a rule of thumb, I try to give no more than 20% of my estimate to any given tactic. &lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let's say, for example, that I'm designing a test of a new module. If I estimated that the whole test would take me 5 days, and after one day I'm nowhere, it's time to rethink my tactics. If it's only been an hour or two, well, I might just not have taken it far enough yet or given the tactic enough time. If it's been two days and I'm still nowhere, I'm ratholing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As with any heuristic, there are exceptions. Sometimes it's worth spending longer on a particularly tricky, slow or likely tactic. Just make that a conscious decision. About 20% of the way through your overall estimate, stop. Ask yourself if you're really getting anywhere with this tactic. If not, time to change.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We'd all like our first tactics to work consistently. Often, they will. For those times it won't, pull yourself out of the rathole and change tactics.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-8555492528665872126?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/9h3K-VtCr_Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/8555492528665872126/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/06/change-tactics.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8555492528665872126?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8555492528665872126?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/9h3K-VtCr_Y/change-tactics.html" title="Change Tactics" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.abakas.com/2010/06/change-tactics.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4DR3k_cSp7ImA9WxFWEEU.&quot;"><id>tag:blogger.com,1999:blog-8341729382206275662.post-8692408579481765878</id><published>2010-05-28T16:30:00.000-04:00</published><updated>2010-05-28T17:09:36.749-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-28T17:09:36.749-04:00</app:edited><title>"Technical"</title><content type="html">"I'm looking for a technical tester." This is code.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This really means one of several things:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;I want someone who can code (developer-lite)&lt;/li&gt;&lt;li&gt;I want someone who will create a robust automated test suite and tools to run it&lt;/li&gt;&lt;li&gt;I want someone who knows what a mouse is and has a prayer of understanding a command line.&lt;/li&gt;&lt;li&gt;I want someone who knows how to understand and manipulate a system, and to apply thoughts derived from computer science to a mental model of how it works.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;These are all legitimate needs. It's up to you to figure out which one is your true "technical tester".&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Testers who are primarily coders are great. They make wonderful tools engineers (really just a name for someone who writes code used by testers). They are great for testing APIs and other things where interaction with the system under test is done through code.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Testers who don't code much are fine. They can still think deeply about a system. Some of them are great with usability and user interface because they don't think about the code but about the system. Testers like this can also usually put together scripts and other helper code snippets when they need to as well.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Testers who don't code at all are fine. They're more limited in what they can do, but the tester who can't work on Linux is limited as well, just in a different way.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Don't tell me you need a technical tester. Tell me you need a tester who can do X, Y, and Z. Only if you're specific about your needs can I find you a tester who actually meets those needs.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'd like to thank &lt;a href="http://blogs.stpcollaborative.com/matt/2010/05/28/are-testers-going-away/"&gt;Matt Heusser&lt;/a&gt; for the inspiration for this post.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8341729382206275662-8692408579481765878?l=blog.abakas.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Abakas/~4/LxquD6zkpWU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.abakas.com/feeds/8692408579481765878/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.abakas.com/2010/05/technical.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8692408579481765878?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8341729382206275662/posts/default/8692408579481765878?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Abakas/~3/LxquD6zkpWU/technical.html" title="&quot;Technical&quot;" /><author><name>Catherine Powell</name><uri>http://www.blogger.com/profile/15459370385548771048</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="00422608762543242492" /></author><thr:total>3</thr:total><feedburner:origLink>http://blog.abakas.com/2010/05/technical.html</feedburner:origLink></entry></feed>
