<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>Rally Engineering Blog</title>
	
	<link>http://www.rallydev.com/engblog</link>
	<description>Stay current with Rally's engineering team.</description>
	<lastBuildDate>Wed, 21 Jul 2010 00:20:26 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/rallyengblog" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="rallyengblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">rallyengblog</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><item>
		<title>An Easy Way to Test Abstract Classes with Mockito</title>
		<link>http://www.rallydev.com/engblog/2010/07/20/an-easy-way-to-test-abstract-classes-with-mockito/</link>
		<comments>http://www.rallydev.com/engblog/2010/07/20/an-easy-way-to-test-abstract-classes-with-mockito/#comments</comments>
		<pubDate>Wed, 21 Jul 2010 00:20:26 +0000</pubDate>
		<dc:creator>Garston Tremblay</dc:creator>
				<category><![CDATA[Automated Testing]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=731</guid>
		<description><![CDATA[One problem that you may have encountered when attempting to write a test for an abstract class is that, well, you can&#8217;t instantiate it.  So how do you test it?
I was wondering the same thing a few weeks ago.  I had the following abstract class (simplified for readability and understanding):

public abstract class MyAbstractClass {

 protected [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F07%2F20%2Fan-easy-way-to-test-abstract-classes-with-mockito%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F07%2F20%2Fan-easy-way-to-test-abstract-classes-with-mockito%2F" height="61" width="51" /></a></div><p>One problem that you may have encountered when attempting to write a test for an abstract class is that, well, you can&#8217;t instantiate it.  So how do you test it?</p>
<p>I was wondering the same thing a few weeks ago.  I had the following abstract class (simplified for readability and understanding):</p>
<pre class="brush: jscript;">
public abstract class MyAbstractClass {

 protected String getDefaultOrder() {
 return getDefaultFetch().split(&quot;,&quot;)[0];
 }

 protected abstract String getDefaultFetch();
}
</pre>
<p>I wanted to write a simple test that would verify that getDefaultOrder() would perform as expected and return the first word in the comma-delimited string returned by getDefaultFetch().</p>
<p>Luckily, I stumbled upon Mockito&#8217;s thenCallRealMethod() method.  This made the test easy to write:</p>
<pre class="brush: jscript;">
public void defaultOrderShouldBeFirstWordInDefaultFetch() {
 MyAbstractClass abstractClass = mock(MyAbstractClass.class);

 when(abstractClass.getDefaultFetch()).thenReturn(
 &quot;Rank,FormattedID,Name,PlanEstimate,Priority,Owner,ClosedDate,CreationDate&quot;
 );

 when(abstractClass.getDefaultOrder()).thenCallRealMethod();

 assertEquals(abstractClass.getDefaultOrder(), &quot;Rank&quot;);
 }
</pre>
<p>As you can see, we&#8217;re telling Mockito that when the getDefaultOrder() method gets called on the mock object &#8220;abstractClass&#8221;, Mockito should execute the real getDefaultOrder() defined in our MyAbstractClass class.  This is contrary to Mockito&#8217;s default behavior of simply returning the default value for the return type of the method called on the mock object (in this case, for getDefaultOrder()&#8217;s return type of String, Mockito would return null).</p>
<p>So there you go: an easy way to test methods in abstract classes!</p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=731&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/07/20/an-easy-way-to-test-abstract-classes-with-mockito/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A nifty equality trick with mockito (or how to verify without calling verify)</title>
		<link>http://www.rallydev.com/engblog/2010/07/13/a-nifty-equality-trick-with-mockito-or-how-to-verify-without-calling-verify/</link>
		<comments>http://www.rallydev.com/engblog/2010/07/13/a-nifty-equality-trick-with-mockito-or-how-to-verify-without-calling-verify/#comments</comments>
		<pubDate>Tue, 13 Jul 2010 20:15:35 +0000</pubDate>
		<dc:creator>Ryan Scott</dc:creator>
				<category><![CDATA[Automated Testing]]></category>
		<category><![CDATA[Engineering Practices]]></category>
		<category><![CDATA[Refactoring]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=717</guid>
		<description><![CDATA[When testing interactions with mocks, some common code patterns can be difficult to test.
We recently encountered a case in a controller where we were talking to a wrapper for a third party service. Our wrapper would new-up a data transfer object based on the parameters provided by the controller and pass that query object to [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F07%2F13%2Fa-nifty-equality-trick-with-mockito-or-how-to-verify-without-calling-verify%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F07%2F13%2Fa-nifty-equality-trick-with-mockito-or-how-to-verify-without-calling-verify%2F" height="61" width="51" /></a></div><p>When testing interactions with mocks, some common code patterns can be difficult to test.</p>
<p>We recently encountered a case in a controller where we were talking to a wrapper for a third party service. Our wrapper would new-up a <a href="http://en.wikipedia.org/wiki/Data_transfer_object" target="_blank">data transfer object</a> based on the parameters provided by the controller and pass that query object to the third party.</p>
<p>Controller code:</p>
<pre class="brush: java;">
    public ModelAndView doSomethingUseful(
            @RequestParam(&quot;name&quot;) String name,
            @RequestParam(&quot;order&quot;) String order)
    {
        Iterable&lt;MyModel&gt; queryResult
                = serviceWrapper.queryByName(name, order);

        return new ModelAndView(&quot;myView&quot;, &quot;myModels&quot;, queryResult);
    }
</pre>
<p>Service wrapper code:</p>
<pre class="brush: java;">
    public Iterable&lt;MyModel&gt; queryByName(String name, String order) {
        ThirdPartyDTO dto = new ThirdPartyDTO();
        dto.setParameter(&quot;name&quot;, name);
        dto.setOrder(order);
        dto.setFilter(&quot;type&quot;, MyModel.class.getName());
        return service.query(dto);
    }
</pre>
<p>While the code in both classes is fairly concise and easy to read, it is difficult to verify that the correct query logic was applied to the DTO. Because the DTO is constructed in the method call on the wrapper and never returned, it&#8217;s hard to write a test for that method. We could construct the DTO in the controller and make the service easy to test, but that would have just moved the problem. We could have introduced a factory to return new instances of the DTO then mocked that to return mocks and verify that the correct methods were called, but mocks returning mocks is a code smell.</p>
<p>What we settled on was introducing an intermediate class:</p>
<pre class="brush: java;">
public class DTOBuilder {
    private Class aClass;
    private String order;
    private String name;

    public DTOBuilder(Class aClass) {
        this.aClass = aClass;
    }

    public DTOBuilder withName(String name) {
        this.name = name;
        return this;
    }

    public DTOBuilder orderBy(String order) {
        this.order = order;
        return this;
    }

    public ThirdPartyDTO buildDTO() {
        ThirdPartyDTO dto = new ThirdPartyDTO();
        dto.setParameter(&quot;name&quot;, this.name);
        dto.setOrder(this.order);
        dto.setFilter(&quot;type&quot;, this.aClass.getName());
        return dto;
    }
}
</pre>
<p>Now we can write tests verifying that when buildDTO is called, the correct values are set on the resulting DTO. It&#8217;s a simple test to write, and it doesn&#8217;t involve any mocks. Additionally, our service wrapper code is simplified&#8230;</p>
<pre class="brush: java;">
public class ServiceWrapper {
    private ThirdPartyService service;

    public ServiceWrapper(ThirdPartyService service) {
        this.service = service;
    }

    public Iterable&lt;MyModel&gt; query(DTOBuilder builder) {
        return service.query(builder.buildDTO());
    }
}
</pre>
<p>.. and we can now write a test for our service wrapper pretty easily:</p>
<pre class="brush: java;">
    public void queryShouldPassDTOFromBuilderToThirdPartyService() {
        DTOBuilder builder = mock(DTOBuilder.class);
        ThirdPartyService service = mock(ThirdPartyService.class);

        ThirdPartyDTO dto = new ThirdPartyDTO();
        Iterable&lt;MyModel&gt; expected = new ArrayList&lt;MyModel&gt;();

        when(builder.buildDTO()).thenReturn(dto);
        when(service.query(dto)).thenReturn(expected);

        ServiceWrapper wrapper = new ServiceWrapper(service);
        Iterable&lt;MyModel&gt; actual = wrapper.query(builder);

        assertEquals(actual, expected);
    }
</pre>
<p>To this point however, we&#8217;ve actually made our controller harder to test. While the code from the controller is clean and reads <em>very</em> well, it&#8217;s no longer easy to test: </p>
<pre class="brush: java;">
    public ModelAndView doSomethingUseful(
            @RequestParam(&quot;name&quot;) String name,
            @RequestParam(&quot;order&quot;) String order)
    {
        DTOBuilder DTOBuilder = new DTOBuilder(MyModel.class)
                .withName(name)
                .orderBy(order);

        Iterable&lt;MyModel&gt; queryResult = serviceWrapper.query(DTOBuilder);

        return new ModelAndView(&quot;myView&quot;, &quot;myModels&quot;, queryResult);
    }
</pre>
<p>A nice feature of <a href="http://www.jetbrains.com/idea/index.html" target="_blank">IntelliJ</a> and an understanding of <a href="http://mockito.org/" target="_blank">mockito</a> can help us out here. The <code>when</code> method in mockito relies on object equality to determine whether an expectation has been met. By overriding equality in our builder class, we can write our controller test like this:</p>
<pre class="brush: java;">
    public void doSomethingUsefulShouldBuildExpressionAndDelegateToService(){
        ServiceWrapper serviceWrapper = mock(ServiceWrapper.class);
        Iterable&lt;MyModel&gt; models = new ArrayList&lt;MyModel&gt;();

        DTOBuilder builder = new DTOBuilder(MyModel.class)
                .withName(&quot;foo&quot;)
                .orderBy(&quot;bar&quot;);

        when(serviceWrapper.query(builder)).thenReturn(models);

        HypotheticalController hypotheticalController = new HypotheticalController(serviceWrapper);
        ModelAndView result = hypotheticalController.doSomethingUseful(&quot;foo&quot;, &quot;bar&quot;);

        assertEquals(result.getModel().get(&quot;myModels&quot;), models);
    }
</pre>
<p>Notice that this test verifies that the correct logic was applied to the DTO without over-relying on mocking and without using <code>verify</code>. It reads well and clearly communicates the intent of the production code.</p>
<p>Where does IntelliJ come into the equation? Writing the equality logic for the builder would be tedious and error prone to do by hand. Luckily, IntelliJ knows how to do it for us. Pressing Command+N brings up the code generation window (that&#8217;s on the mac &#8211; check the IntelliJ documentation for the shortcut on your platform). Choosing &#8220;equals() and hashCode()&#8221; from the resulting menu gives us this:</p>
<pre class="brush: java;">
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    DTOBuilder that = (DTOBuilder) o;

    if (aClass != null ? !aClass.equals(that.aClass) : that.aClass != null) return false;
    if (name != null ? !name.equals(that.name) : that.name != null) return false;
    if (order != null ? !order.equals(that.order) : that.order != null) return false;

    return true;
}

@Override
public int hashCode() {
    int result = aClass != null ? aClass.hashCode() : 0;
    result = 31 * result + (order != null ? order.hashCode() : 0);
    result = 31 * result + (name != null ? name.hashCode() : 0);
    return result;
}
</pre>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=717&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/07/13/a-nifty-equality-trick-with-mockito-or-how-to-verify-without-calling-verify/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Finding High-Impact Areas for Refactoring</title>
		<link>http://www.rallydev.com/engblog/2010/07/06/finding-high-impact-areas-for-refactoring/</link>
		<comments>http://www.rallydev.com/engblog/2010/07/06/finding-high-impact-areas-for-refactoring/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 22:35:06 +0000</pubDate>
		<dc:creator>Rod Hilton</dc:creator>
				<category><![CDATA[Code Reviews]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[Refactoring]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[complexity]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[metrics]]></category>
		<category><![CDATA[quality]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=705</guid>
		<description><![CDATA[At Rally, we&#8217;ve stayed committed for the last 7 years to never telling the business that the product has to halt active development to pay down technical debt.  For us, the &#8220;big rewrite in the sky&#8221; has always been off the table.  Instead, we prefer to incrementally refactor and improve the existing elements [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F07%2F06%2Ffinding-high-impact-areas-for-refactoring%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F07%2F06%2Ffinding-high-impact-areas-for-refactoring%2F" height="61" width="51" /></a></div><p>At Rally, we&#8217;ve stayed committed for the last 7 years to never telling the business that the product has to halt active development to pay down technical debt.  For us, the &#8220;big rewrite in the sky&#8221; has always been off the table.  Instead, we prefer to incrementally refactor and improve the existing elements of the codebase, gradually getting it to where we want it to be without ever halting feature development completely.</p>
<p>This has never been an easy task.  The truth is, the codebase is quite old by engineering standards, many parts of it written before standards emerged in the industry.  Our entire persistence layer, for example, was created by hand well before libraries such as Hibernate existed (or at least, before they became standard practice).  This means that there are many, many areas of the code that we&#8217;d like to attack and improve.</p>
<p>One thing we&#8217;ve started doing is holding lunchtime &#8220;refactotums,&#8221; where we take a look at some code and try to improve it.  Of course, with such a large codebase, and so many areas of the code that have not been touched since they were first written, we&#8217;ve often wondered how to most effectively spend our refactotum time.  This led us to wonder &#8220;what are the areas of the code where a refactoring would have the greatest impact?&#8221;</p>
<p>We started keeping track of our codebase using <a href="http://www.sonarsource.org/">Sonar</a> which was extremely helpful in keeping track of metrics at a class level, but it didn&#8217;t give us quite enough information.  For example, if <code>Class A</code> has a complexity score of 500 and <code>Class B</code> has a complexity score of 200 (higher is worse), you&#8217;d think that spending some time cleaning up <code>Class A</code> would be the most effective thing to do.  But what if <code>Class B</code> is used by 50 times as many other classes as <code>Class A</code>?  Or what if <code>Class B</code>, complicated as it is, has never been modified in the history of the project?  It would be nice to clean this class up, but it might be a better use of time to focus on <code>Class A</code></p>
<p>So our idea was to combine two metrics: the class complexity (based on <a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity">McCabe Cyclomatic Complexity</a>) and the number of times a file has been modified in the codebase.  The idea here was, every time a file has to be modified, it has to be read.  A complex file is harder to read, making the engineer more likely to misunderstand or make a mistake while editing the file.</p>
<p>Luckily, git makes it quite easy to count the number of modifications to files in the codebase:</p>
<p><span id="more-705"></span></p>
<pre class="brush: bash;">
git whatchanged | grep '^:.*alm-webapp/src/main/java/.*\.java$' | cut -c65- | sed -e 's/\//./g' -e 's/.java//' | sort | uniq -c | sort -r
</pre>
<p>Obviously, the grep and cut would have to be modified slightly depending on your codebase structure.  The grep is used to ensure only our code is looked at, excluding any third-party code in the system.  The cut and seds are mostly just about munging the filenames into &#8220;probable&#8221; classnames, since that&#8217;s more useful (and what Sonar stores).</p>
<p>This command yields some high-traffic files, but it needs to be combined with high-value targets as well.  For this, we turned to Sonar.</p>
<p>We were unable to use the Sonar web services as thoroughly as we were hoping, so we wound up writing a query directly against the database where the Sonar data is stored. The query for a given metric (in our case, metric 12, class complexity) looks like this:</p>
<pre class="brush: sql;">
    SELECT projects.kee, project_measures.value, project_measures.metric_id
    FROM snapshots, project_measures, projects
    WHERE islast = 1
      AND project_measures.snapshot_id = snapshots.id
      AND project_measures.metric_id = 12
      AND snapshots.qualifier = 'CLA'
      AND snapshots.project_id = projects.id
</pre>
<p>We wrote a Ruby script that pulled the data from these two sources and correlated them together, deriving an eventual &#8220;weighted rank&#8221; from the two.  We went on to include a few more metrics in the final equation.   Output from the script looks like this:</p>
<pre>
Calculating complexity data from sonar...Done!
Calculating revision data from git...Done!
Calculating weighted rank data...Done!
Current formula:
   ( 0.15 * ( complexity / average_complexity ) )
 + ( 0.40 * ( function_complexity / average_function_complexity ) )
 + ( 0.30 * ( efferent_coupling / average_efferent_coupling ) )
 + ( 0.40 * ( revisions / average_revisions ) )

Rank    Score  Revisions  Complexity  F.Complexity  E.Coupling  Class
----  -------  ---------  ----------  ------------  ----------  -----
   1    14.12         60         507             2          67  com.rallydev.ClassG
   2    13.10         93         301             2          71  com.rallydev.ClassQ
   3    12.53         80         318             2          70  com.rallydev.ClassR
   4    11.25         11          45            45          27  com.rallydev.ClassO
   5     9.65          9          43            43           8  com.rallydev.ClassC
   6     9.48         72         194             2          52  com.rallydev.ClassZ
   7     9.24         60         205             2          56  com.rallydev.ClassN
   8     8.79         42         218             2          59  com.rallydev.ClassK
   9     8.75         31         260             3          53  com.rallydev.ClassF
  10     8.63         54         167             2          59  com.rallydev.ClassT
  11     8.38         65         194             2          39  com.rallydev.ClassO
  12     8.29         44         207             3          49  com.rallydev.ClassY
  13     7.81         40         225             2          41  com.rallydev.ClassP
  14     7.48         18          26            26          23  com.rallydev.ClassK
  15     7.44         60         164             2          34  com.rallydev.ClassA
</pre>
<p>We&#8217;ve found it pretty useful to know where our efforts would be most useful.  If you&#8217;re using git and sonar, feel free to modify this script and try it on your own codebase (note, you will need the mysql client binary in your path for it to work).</p>
<pre class="brush: ruby;">
#!/usr/bin/env ruby
# Authors: Rod Hilton and Ryan Scott
# Site: www.rallydev.com

METRICS = {12=&gt;:complexity, 14=&gt;:function_complexity, 74=&gt;:efferent_coupling}
WEIGHTS = {:revisions=&gt;0.4, :complexity=&gt;0.15, :function_complexity=&gt;0.4, :efferent_coupling=&gt;0.3}

def print_around(title, &amp;block)
  print title + &quot;...&quot;
  STDOUT.flush
  yield block
  print &quot;Done!\n&quot;
end

def add_class_data(class_data, class_name, data_key, data_value)
  class_data[class_name] = {} unless class_data.has_key?(class_name)
  class_data[class_name][data_key] = data_value
end

def add_complexity_data(class_data)
  complexity_query = &lt;&lt;-EOF
    SELECT projects.kee, project_measures.value, project_measures.metric_id
    FROM snapshots, project_measures, projects
    WHERE islast = 1
      AND project_measures.snapshot_id = snapshots.id
      AND project_measures.metric_id in (#{METRICS.keys.join(&quot;,&quot;)})
      AND snapshots.qualifier = 'CLA'
      AND snapshots.project_id = projects.id
    EOF

  print_around &quot;Calculating complexity data from sonar&quot; do
    complexity = `echo &quot;#{complexity_query}&quot; | mysql -h alm-build --user=YOUR_SONAR_USERNAME --password=YOUR_SONAR_PASSWORD --database=YOUR_SONAR_DATABASE`

    complexity.each do |complexity_line|
      match = complexity_line.match(/^(\S*)\s+([\d\.]+)\s+(\d+)$/)
      if(match)
        name = match[1]
        metric = match[2].to_f
        metric_id = match[3].to_i
        metric_name = METRICS[metric_id]
        add_class_data(class_data, name.match(/^.*:([^:]*)$/)[1], metric_name, metric)
      end
    end
  end
end

def add_revision_data(class_data)
  print_around &quot;Calculating revision data from git&quot; do
    file_changes = `git whatchanged | grep '^:.*alm-webapp/src/main/java/.*\.java$' | cut -c65- | sed -e 's/\\//./g' -e 's/.java//' | sort | uniq -c | sort -r`
    # file_changes = `cat modified.txt`

    file_changes.each do |file_change_line|
      match = file_change_line.match(/^\s*(\d+)\s+(.*)$/)
      if(match)
        revisions = match[1].to_i
        name = match[2]
        add_class_data(class_data, name, :revisions, revisions)
      end
    end
  end
end

def average_for_data(class_data, key)
  values = class_data.values.collect{|val| val[key]}
  values.inject{ |sum, el| sum + el }.to_f / values.size
end

def add_weighted_rank_data(class_data)

  print_around &quot;Calculating weighted rank data&quot; do
    averages = {}
    WEIGHTS.each do |key, value|
      average = average_for_data(class_data, key)
      averages[key] = average
    end

    class_data.each do |classname, data|
      data[:weighted_rank] = 0
      WEIGHTS.each do |key, weight|
        chunk = weight * (data[key] / averages[key])
        data[:weighted_rank] = data[:weighted_rank] + chunk
      end
    end
  end
end

def remove_classes_without_all_data(class_data)
  num_keys = class_data.values.map{|val| val.size}.max
  class_data.delete_if{|key, val| val.size &lt; num_keys}
end

# --- Main ---

class_data = {}

add_complexity_data(class_data)
add_revision_data(class_data)

class_data = remove_classes_without_all_data(class_data)

add_weighted_rank_data(class_data)

#Sort by rank, descending
class_data_sorted = class_data.sort{|a,b| b[1][:weighted_rank] &lt;=&gt; a[1][:weighted_rank]}

puts &quot;Current formula: &quot;
pieces = []
WEIGHTS.each do |key, weight|
  pieces &lt;&lt; sprintf( &quot;( %3.2f * ( %s / %s ) )&quot;, weight, key.to_s, &quot;average_&quot;+key.to_s)
end
puts &quot;   &quot; + pieces.join(&quot;\n + &quot;)

puts &quot;\n&quot;
puts &quot;Rank    Score  Revisions  Complexity  F.Complexity  E.Coupling  Class&quot;
puts &quot;----  -------  ---------  ----------  ------------  ----------  -----&quot;
class_data_sorted[0..49].each_with_index do |file, index|
  class_name=file[0]
  class_data=file[1]
  printf(&quot;%4u  %7.2f  %9u  %10u  %12u  %10u  %s\n&quot;, (index+1), class_data[:weighted_rank], class_data[:revisions], class_data[:complexity], class_data[:function_complexity], class_data[:efferent_coupling], class_name)
end
</pre>
<p>Feel free to make any improvements or changes to this script that you wish.  It&#8217;s not the most efficient in the world, but it was thrown together pretty quickly to get a decent idea of our highest-impact refactoring targets.</p>
<p>Suggestions for improving the formula?  Made your own changes to the script?  Feel free to post in the comments below, we welcome the discussion!</p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=705&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/07/06/finding-high-impact-areas-for-refactoring/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sauce OnDemand with Selenium 2.0 and WebDriver</title>
		<link>http://www.rallydev.com/engblog/2010/06/29/sauce-ondemand-with-selenium-2-0-and-webdriver/</link>
		<comments>http://www.rallydev.com/engblog/2010/06/29/sauce-ondemand-with-selenium-2-0-and-webdriver/#comments</comments>
		<pubDate>Tue, 29 Jun 2010 16:53:18 +0000</pubDate>
		<dc:creator>Jeff Smith</dc:creator>
				<category><![CDATA[Automated Testing]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=689</guid>
		<description><![CDATA[We have recently started to write our browser based tests with Selenium 2.0. and the WebDriver api. We like the direct control over the browser versus the JavaScript execution of Selenium 1.0.
I also stumbled across Sauce Labs and its Sauce OnDemand product. I really would like to not maintain a huge grid of different browsers [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F06%2F29%2Fsauce-ondemand-with-selenium-2-0-and-webdriver%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F06%2F29%2Fsauce-ondemand-with-selenium-2-0-and-webdriver%2F" height="61" width="51" /></a></div><p>We have recently started to write our browser based tests with Selenium 2.0. and the <a href="http://selenium.googlecode.com/svn/trunk/docs/api/java/index.html">WebDriver api</a>. We like the direct control over the browser versus the JavaScript execution of Selenium 1.0.</p>
<p>I also stumbled across <a href="http://saucelabs.com">Sauce Labs</a> and its <a href="http://saucelabs.com/products/sauce-ondemand">Sauce OnDemand</a> product. I really would like to not maintain a huge grid of different browsers and operating systems, having them available &#8220;in the cloud&#8221; seems like a much better option. It also aligns better with the companies efforts to be more green. Unfortunately, Sauce OnDemand is a Selenium 1.0 version of Selenium Remote Control. Bummer. I took some time and at got a very basic example working using WebDriver and Sauce OnDemand.</p>
<p>Here is a simple TestNG test that verifies the title of google.</p>
<pre class="brush: java;">
package com.rallydev.acceptance;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.CommandExecutor;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.net.URL;

import static org.testng.Assert.assertEquals;

@Test
public class SauceLabsTest {

    private WebDriver driver;

    @BeforeMethod
    protected void setUp() throws Exception {

        String browserString = new SauceLabsBrowser(&quot;sauceusername&quot;, &quot;sauce-api-key-goes-here&quot;, &quot;Windows 2003&quot;, &quot;firefox&quot;, &quot;3.&quot;, &quot;WebDriver example&quot;).toJson();

        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setBrowserName(browserString);
        CommandExecutor executor = new RallySeleneseCommandExecutor(new URL(&quot;http://saucelabs.com:4444/&quot;), new URL(&quot;http://www.google.com/&quot;), capabilities);
        driver = new RemoteWebDriver(executor, capabilities);

    }

    @AfterMethod
    protected void tearDown() throws Exception {
        driver.quit();

    }

    public void sauceLabsWorks() throws Exception {
        driver.get(&quot;http://www.google.com/&quot;);
        assertEquals(&quot;Google&quot;, driver.getTitle());
    }
}
</pre>
<p>I had to extend the SeleneseCommandExecutor so the JSON browser name that Sauce wants doesn&#8217;t blow up. Here is the code for the what I called the RallySeleneseCommandExecutor:</p>
<pre class="brush: java;">
package com.rallydev.acceptance;

import com.thoughtworks.selenium.HttpCommandProcessor;
import org.openqa.selenium.SeleneseCommandExecutor;
import org.openqa.selenium.remote.Capabilities;

import java.net.URL;

public class RallySeleneseCommandExecutor extends SeleneseCommandExecutor {
    public RallySeleneseCommandExecutor(URL seleniumServer, URL remoteAddress, Capabilities capabilities) {
        super(
                new HttpCommandProcessor(
                        seleniumServer.getHost(),
                        seleniumServer.getPort(),
                        capabilities.getBrowserName(),
                        remoteAddress.toString()
                )
        );
    }
}
</pre>
<p>The only other thing I did to make life easier was to create the SauceLabsBrowser class. All it does it put all the arguments into map and then convert the map to JSON. I can only handle string concatenation for so long <img src='http://www.rallydev.com/engblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<pre class="brush: java;">
package com.rallydev.acceptance;

import net.sf.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

public class SauceLabsBrowser {

    private Map&lt;String, String&gt; sauceParams;

    public SauceLabsBrowser(String username, String accessKey, String os, String browser, String browserVersion, String jobName) {
        this.sauceParams = new HashMap&lt;String, String&gt;();

        this.sauceParams.put(&quot;username&quot;, username);
        this.sauceParams.put(&quot;access-key&quot;, accessKey);
        this.sauceParams.put(&quot;os&quot;, os);
        this.sauceParams.put(&quot;browser&quot;, browser);
        this.sauceParams.put(&quot;browser-version&quot;, browserVersion);
        this.sauceParams.put(&quot;job-name&quot;, jobName);
    }

    public String toJson() {
        JSONObject jsonObject = new net.sf.json.JSONObject();
        jsonObject.putAll(sauceParams);
        return jsonObject.toString();
    }
}
</pre>
<p>There is still a decent amount of work to do. I found there a bunch of WebDriver commands that aren&#8217;t yet implemented in the SeleneseCommandExecutor. I hope to fix that and get a patch send to the Selenium people soon. Hope this at least gets people started.</p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=689&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/06/29/sauce-ondemand-with-selenium-2-0-and-webdriver/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Marketing Application Security</title>
		<link>http://www.rallydev.com/engblog/2010/06/22/marketing-application-security/</link>
		<comments>http://www.rallydev.com/engblog/2010/06/22/marketing-application-security/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 18:09:32 +0000</pubDate>
		<dc:creator>Jeff Smith</dc:creator>
				<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=667</guid>
		<description><![CDATA[Ben Whaley of AppliedTrust and I recently spent a few months putting together a security model for Rally. We analyzed each module of our Java-based stack, detailing and capturing security considerations along the way. We relied on OWASP as a standardized framework for guiding our process. The goal of the project was to conveniently describe [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F06%2F22%2Fmarketing-application-security%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F06%2F22%2Fmarketing-application-security%2F" height="61" width="51" /></a></div><div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;">Ben Whaley of AppliedTrust and I recently spent a few months putting together a security model for Rally. We analyzed each module of our Java-based stack, detailing and capturing security considerations along the way. We relied on OWASP as a standardized framework for guiding our process. The goal of the project was to conveniently describe the Rally approach to security at the application layer. Additionally, this model has proven essential for newer members of our development team.</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;">Many companies, particularly SaaS companies, can benefit by having a published security model. Since we had never seen anything quite like this, and because we believe in the open exchange of information, we decided to share the model with the public. Ben and I presented a talk entitled &#8220;Effectively marketing security as a win for both the business and the customer&#8221; at Front Range OWASP Conference 2010 where we presented the model detailing Rally&#8217;s approach to securing our SaaS product. Feel free to take a look at our slides.</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;">We can now arm our sales team with a straightforward (even pretty) picture that represents exactly how we handle security throughout the application. This should assist us with answering questions in those difficult RFP documents from our more security-conscious customers. Also, I truly believe that having our security approach out there for the whole world to see shows a certain level of confidence. It&#8217;s clear that we&#8217;re not just doing a &#8220;security by obscurity&#8221; approach.</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;">The development team at Rally has been growing during the last year and it can be difficult to train developers, especially from a security standpoint. The security model serves dual purpose from a training perspective. We now have a current architecture diagram that visually depicts security at each layer of the stack.</div>
<div id="_mcePaste" style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;">Our hope is that other companies can use this model as an example. There was no existing reference point when we started this project, so we chose to share ours.  The software development industry generally, and next-generation web platforms specifically, can only benefit from being more security-aware as increasingly sensitive data is made available via the web.</div>
<p>Ben Whaley of <a href="http://appliedtrust.com">AppliedTrust</a> and I recently spent a few months putting together a security model for Rally. We analyzed each module of our Java-based stack, detailing and capturing security considerations along the way. We relied on <a href="http://www.owasp.org/">OWASP</a> as a standardized framework for guiding our process. The goal of the project was to conveniently describe the Rally approach to security at the application layer. Additionally, this model has proven essential for newer members of our development team.</p>
<p>Many companies, particularly SaaS companies, can benefit by having a published security model. Since we had never seen anything quite like this, and because we believe in the open exchange of information, we decided to share the model with the public. Ben and I presented a talk entitled &#8220;Effectively marketing security as a win for both the business and the customer&#8221; at <a href="http://www.owasp.org/index.php/Front_Range_OWASP_Conference_2010">Front Range OWASP Conference 2010</a> where we presented the model detailing <a href="http://www.rallydev.com">Rally</a>&#8217;s approach to securing our SaaS product. Feel free to take a look at our <a href="https://docs.google.com/a/rallydev.com/present/view?id=0Af-vbfka88vFZGRrcjYycXZfMzI5Z3c0OWQ2Y2M&amp;hl=en">slides</a>.</p>
<p>We can now arm our sales team with a straightforward (even pretty) picture that represents exactly how we handle security throughout the application. This should assist us with answering questions in those difficult RFP documents from our more security-conscious customers. Also, I truly believe that having our security approach out there for the whole world to see shows a certain level of confidence. It&#8217;s clear that we&#8217;re not just doing a &#8220;security by obscurity&#8221; approach.</p>
<p>The development team at Rally has been growing during the last year and it can be difficult to train developers, especially from a security standpoint. The security model serves dual purpose from a training perspective. We now have a current architecture diagram that visually depicts security at each layer of the stack.</p>
<p>Our hope is that other companies can use this model as an example. There was no existing reference point when we started this project, so we chose to share ours.  The software development industry generally, and next-generation web platforms specifically, can only benefit from being more security-aware as increasingly sensitive data is made available via the web.</p>
<p><object id="flashObj" width="486" height="412" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,47,0"><param name="movie" value="http://c.brightcove.com/services/viewer/federated_f9/60849901001?isVid=1" /><param name="bgcolor" value="#FFFFFF" /><param name="flashVars" value="@videoPlayer=97828545001&#038;playerID=60849901001&#038;domain=embed&#038;dynamicStreaming=true" /><param name="base" value="http://admin.brightcove.com" /><param name="seamlesstabbing" value="false" /><param name="allowFullScreen" value="true" /><param name="swLiveConnect" value="true" /><param name="allowScriptAccess" value="always" /><embed src="http://c.brightcove.com/services/viewer/federated_f9/60849901001?isVid=1" bgcolor="#FFFFFF" flashVars="@videoPlayer=97828545001&#038;playerID=60849901001&#038;domain=embed&#038;dynamicStreaming=true" base="http://admin.brightcove.com" name="flashObj" width="486" height="412" seamlesstabbing="false" type="application/x-shockwave-flash" allowFullScreen="true" swLiveConnect="true" allowScriptAccess="always" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"></embed></object></p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=667&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/06/22/marketing-application-security/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>A Couple Things That Should Be Really Easy To Do In Selenium But Aren’t</title>
		<link>http://www.rallydev.com/engblog/2010/04/06/a-couple-things-that-should-be-really-easy-to-do-in-selenium-but-arent/</link>
		<comments>http://www.rallydev.com/engblog/2010/04/06/a-couple-things-that-should-be-really-easy-to-do-in-selenium-but-arent/#comments</comments>
		<pubDate>Tue, 06 Apr 2010 16:24:51 +0000</pubDate>
		<dc:creator>Garston Tremblay</dc:creator>
				<category><![CDATA[Automated Testing]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=629</guid>
		<description><![CDATA[Recently we&#8217;ve been expanding our suite of &#8220;Smoke Tests&#8221;.  Smoke Tests is our term for the GUI tests that we&#8217;ve written for the purpose of testing common, happy-path actions in the Rally application.  These tests are written in Java using the Selenium API.
During the process of writing these tests, we&#8217;ve ran into a few situations [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F04%2F06%2Fa-couple-things-that-should-be-really-easy-to-do-in-selenium-but-arent%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F04%2F06%2Fa-couple-things-that-should-be-really-easy-to-do-in-selenium-but-arent%2F" height="61" width="51" /></a></div><p>Recently we&#8217;ve been expanding our suite of &#8220;Smoke Tests&#8221;.  Smoke Tests is our term for the GUI tests that we&#8217;ve written for the purpose of testing common, happy-path actions in the Rally application.  These tests are written in Java using the Selenium API.</p>
<p>During the process of writing these tests, we&#8217;ve ran into a few situations where we needed to do seemingly simple actions through the web browser, but we weren&#8217;t able to immediately come up with solutions simply by looking at the Selenium API.  I&#8217;d like to highlight a few of these below:</p>
<p><strong>1.  Interacting with WebElements in iframes</strong><br />
In one of our Smoke Tests for an editor in the Rally Application, we wanted to enter some text into the the &#8220;Description&#8221; field in the editor.</p>
<p><img class="aligncenter size-full wp-image-635" src="http://www.rallydev.com/engblog/wp-content/uploads/2010/04/Editor.JPG" alt="Editor" width="550"  /></p>
<p>The Description field uses our rich-text editor, and the text box where the Description text can be entered is enclosed inside of an iframe.  The setValue method below shows the code we were trying to use to enter text into this field:</p>
<pre class="brush: jscript;">public class RichTextField
{
private String iframeId;
private WebDriver webDriver;

public RichTextField(String iframeId, WebDriver webDriver)
{
this.iframeId = iframeId;
this.webDriver = webDriver;
}

public void setValue(String value)
{
webDriver.findElement(By.cssSelector(&quot;#&quot; + iframeId + &quot; body&quot;)).sendKeys(value);
}
}</pre>
<p>When we tried to access the body element of the iframe with the call to By.cssSelector(&#8230;), Selenium was unable to find the body element.  This is because it was inside of the iframe.</p>
<p>Fortunately, after some searching we were able to find the switchTo() method on WebDriver.  This did the trick:</p>
<pre class="brush: jscript;">public class RichTextField
{
private String iframeId;
private WebDriver webDriver;

public RichTextField(String iframeId, WebDriver webDriver)
{
this.iframeId = iframeId;
this.webDriver = webDriver;
}

public void setValue(String value)
{
webDriver.switchTo().frame(iframeId).findElement(By.tagName(&quot;body&quot;)).sendKeys(value);
webDriver.switchTo().defaultContent();
}
}</pre>
<p>Notice the webDriver.switchTo().defaultContent() line.  This is necessary so that the WebDriver instance&#8217;s context is switched back to the main page instead of being inside of the iframe, which is the result of the first statement in setValue.</p>
<p><strong>2.  Selecting a Value From an Ext.form.ComboBox</strong><br />
In another one of our Smoke Tests, we wanted to click the &#8220;Create New User&#8221; option from our new auto-suggest text box on the Project-Users grid.  This auto-suggest box is built using ExtJS&#8217; Ext.form.ComboBox object.</p>
<p><img class="aligncenter size-full wp-image-636" src="http://www.rallydev.com/engblog/wp-content/uploads/2010/04/AddProjectUserToolbar.JPG" alt="AddProjectUserToolbar" width="397" height="196" /></p>
<p>Text needs to be entered into the auto-suggest textbox so that the drop-down box containing the &#8220;Create New User&#8221; option will render.  We thought this could be accomplished using the following code in the sendKeys method (comboTextBox is the WebElement corresponding to the auto-suggest textbox):</p>
<pre class="brush: jscript;">public class Combo
{
private WebElement comboTextBox;

public Combo(WebElement comboTextBox)
{
this.comboTextBox = comboTextBox;
}

public void sendKeys(CharSequence text)
{
comboTextBox.sendKeys(text);
}
}</pre>
<p>However, we noticed that unless the Firefox window that was running the test was the focused window, the drop-down box (with the link we wanted to click) would never appear regardless of what was typed into the auto-suggest text box.  Wierd, huh?</p>
<p>After much head-shaking, we looked at the expand method in Ext.form.ComboBox:</p>
<pre class="brush: jscript;">expand : function(){
if(this.isExpanded() || !this.hasFocus){
return;
}
this.list.alignTo(this.wrap, this.listAlign);
this.list.show();
this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
Ext.getDoc().on('mousewheel', this.collapseIf, this);
Ext.getDoc().on('mousedown', this.collapseIf, this);
this.fireEvent('expand', this);
}</pre>
<p>We put some alerts in the there, and noticed that hasFocus was false!  This meant that expand() did nothing, and that&#8217;s why our auto-suggest box was never expanding!  hasFocus is set to true in the onFocus() method of ComboBox, which is called when the focus event is fired (hence the name <img src='http://www.rallydev.com/engblog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ).  So we modified our sendKeys method to attempt to explicitly focus the ComboBox by clicking on it:</p>
<pre class="brush: jscript;">public void sendKeys(CharSequence text)
{
comboTextBox.click();
comboTextBox.sendKeys(text);
}</pre>
<p>Still no luck!  hasFocus was still false.  After some searching, we stumbled on <a href="http://code.google.com/p/selenium/issues/detail?id=157">this</a> article.  This led us to believe that the focus event was never being fired in our Firefox browser.  So we got creative:</p>
<pre class="brush: jscript;">public class Combo
{
private WebElement comboTextBox;
private WebDriver webDriver;

public Combo(WebElement comboTextBox, WebDriver webDriver)
{
this.comboTextBox = comboTextBox;
this.webDriver = webDriver;
}

public void sendKeys(CharSequence text)
{
((JavascriptExecutor) webDriver).executeScript(&quot;Ext.getCmp('project-user-auto-suggest').onFocus()&quot;);
comboTextBox.sendKeys(text);
}
}</pre>
<p>This is really awesome because we were able to use the Ext.getCmp method from the ExtJS framework that we use in our application to get our auto-suggest ComboBox (&#8221;project-user-auto-suggest&#8221; is the id of the component), and then we could make up for the browser&#8217;s inability to fire the focus event by simply calling onFocus() ourselves.  This worked like a charm.  So awesome!</p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=629&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/04/06/a-couple-things-that-should-be-really-easy-to-do-in-selenium-but-arent/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Easiest Time To Live (TTL) Cache Ever</title>
		<link>http://www.rallydev.com/engblog/2010/03/23/easiest-time-to-live-ttl-cache-ever/</link>
		<comments>http://www.rallydev.com/engblog/2010/03/23/easiest-time-to-live-ttl-cache-ever/#comments</comments>
		<pubDate>Tue, 23 Mar 2010 20:56:29 +0000</pubDate>
		<dc:creator>Jeff Smith</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=617</guid>
		<description><![CDATA[The other day I needed to cache some data that should expire. Thanks to Google Collections this was incredibly easy.
The entries in this map will expire in 1 hour. Super easy and pretty clean too. Thanks Google.

ConcurrentMap&#60;String, String&#62; cache = new MapMaker().expiration(1, TimeUnit.HOURS).makeMap();

]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F03%2F23%2Feasiest-time-to-live-ttl-cache-ever%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F03%2F23%2Feasiest-time-to-live-ttl-cache-ever%2F" height="61" width="51" /></a></div><p>The other day I needed to cache some data that should expire. Thanks to <a href="http://code.google.com/p/google-collections/">Google Collections</a> this was incredibly easy.</p>
<p>The entries in this map will expire in 1 hour. Super easy and pretty clean too. Thanks Google.</p>
<pre class="brush: java;">
ConcurrentMap&lt;String, String&gt; cache = new MapMaker().expiration(1, TimeUnit.HOURS).makeMap();
</pre>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=617&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/03/23/easiest-time-to-live-ttl-cache-ever/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rally Hosting a CodeRetreat on March 27th</title>
		<link>http://www.rallydev.com/engblog/2010/03/09/rally-coderetreat/</link>
		<comments>http://www.rallydev.com/engblog/2010/03/09/rally-coderetreat/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 11:00:03 +0000</pubDate>
		<dc:creator>Adam Esterline</dc:creator>
				<category><![CDATA[Engineering Practices]]></category>
		<category><![CDATA[Pair Programming]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[Test-Driven Design]]></category>
		<category><![CDATA[coderetreat]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=608</guid>
		<description><![CDATA[Rob Park and I will be hosting a CodeRetreat at Rally on March 27th.
What is a CodeRetreat (taken from the CodeRetreat Ning site)?

At CodeRetreat, we retreat from the world to advance in our craft. We sharpen our saws, together. We retreat from production and business value to increase our production capacity, our quality, our velocity, [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F03%2F09%2Frally-coderetreat%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F03%2F09%2Frally-coderetreat%2F" height="61" width="51" /></a></div><p><a href="http://twitter.com/robpark">Rob Park</a> and I will be hosting a <a href="http://coderetreat.com">CodeRetreat</a> at Rally on March 27th.</p>
<p><strong>What is a CodeRetreat</strong> (taken from the <a href="http://coderetreat.ning.com/profiles/blogs/how-to-run-a-coderetreat">CodeRetreat Ning site</a>)?</p>
<blockquote><p>
At CodeRetreat, we retreat from the world to advance in our craft. We sharpen our saws, together. We retreat from production and business value to increase our production capacity, our quality, our velocity, our ability to produce business value. We retreat from immersion in deep technology weeds (Oh no! The JBoss ClassLoader is giving me CCE&#8217;s!) to advance in our ability to learn and adopt to any technology well. We retreat from our fears, and embrace new practices, patterns, languages.</p>
<p>We retreat from our local ponds and swim in a larger pool. We connect with other passionate coders who we seldom get to code with. We make new connections and learn new lessons.
</p></blockquote>
<p><strong>Code Retreat Format</strong></p>
<ul>
<li>Problem: <a href="http://en.wikipedia.org/wiki/Conway's_game_of_life">Conway&#8217;s Game of Life</a></li>
<li>Length of Iterations/Sessions: 45 minutes</li>
<li>Duration: 8am to 5pm</li>
<li>Language: Java</li>
</ul>
<p>This will be a fun day for all.    Breakfast and Lunch will be provided.    If you will be attending, please register at <a href="http://coderetreat.ning.com/">http://coderetreat.ning.com/</a> or send me an email at aesterline at rallydev dot com.  </p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=608&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/03/09/rally-coderetreat/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fun with Oracle Sequences</title>
		<link>http://www.rallydev.com/engblog/2010/03/02/fun-with-oracle-sequences/</link>
		<comments>http://www.rallydev.com/engblog/2010/03/02/fun-with-oracle-sequences/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 11:00:06 +0000</pubDate>
		<dc:creator>Adam Esterline</dc:creator>
				<category><![CDATA[Quick Build]]></category>
		<category><![CDATA[Refactoring]]></category>
		<category><![CDATA[Software Development]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=589</guid>
		<description><![CDATA[In our quest for ever-faster build times, we noticed that a large portion of our time running tests was getting the next OID for persisted objects. 
After some investigation, we discovered the bottleneck.   For every persisted object created in the system, a new connection was obtained from our datasource and a query was [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F03%2F02%2Ffun-with-oracle-sequences%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F03%2F02%2Ffun-with-oracle-sequences%2F" height="61" width="51" /></a></div><p>In our quest for ever-faster build times, we noticed that a large portion of our time running tests was getting the next OID for persisted objects. </p>
<p>After some investigation, we discovered the bottleneck.   For every persisted object created in the system, a new connection was obtained from our datasource and a query was issued to retrieve a single OID.   I know, I know&#8230; not very efficient.    We wanted to refactor the code to grab batches of OIDs and cache them in memory.</p>
<p>How do we get groups of OIDs from an Oracle sequence?    We had a couple of choices.</p>
<p><strong>Modify the sequence&#8217;s INCREMENT BY to something larger than one.</strong><br />
We liked this approach because it allowed us to get a batch of OIDs with one query.    But.. This approach has problems for external code that inserts data into our database.    The external code would also have to know about the change in INCREMENT BY.    </p>
<p>Changing all of the external integration code seemed like a big task.    We wanted to find an approach that allowed us to keep the sequence unchanged, but allowed us to get a batch of OIDs with one query.</p>
<p><strong>Query the sequence for a batch of OIDs</strong><br />
How do we do that?     We need a query in Oracle that will generate N number of results.    We tried several queries before we finally settled on one clever query.   (Or at least it was clever to us.)</p>
<pre class="brush: sql;">
SELECT SEQ.nextval FROM (SELECT LEVEL FROM DUAL CONNECT BY LEVEL &lt;= :1)
</pre>
<p>The clever part is the CONNECT BY LEVEL.   This allows us to generate an arbitrary number of rows in our result set.    The sequencer does the rest.   We capture the results from the query and hand them out in memory.    This query gave us the best of both worlds.   We got to get a batch of OIDs with one query without having to change any of our external systems.</p>
<p><strong>Where do we go from here?</strong><br />
In a word, GUIDs.    This was a necessary performance related step on the way to implementing GUIDs.</p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=589&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/03/02/fun-with-oracle-sequences/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>You Have To Buy It Twice Before It’s Cheap</title>
		<link>http://www.rallydev.com/engblog/2010/01/26/you-have-to-buy-it-twice-before-its-cheap/</link>
		<comments>http://www.rallydev.com/engblog/2010/01/26/you-have-to-buy-it-twice-before-its-cheap/#comments</comments>
		<pubDate>Tue, 26 Jan 2010 20:56:40 +0000</pubDate>
		<dc:creator>Rod Hilton</dc:creator>
				<category><![CDATA[Estimating]]></category>
		<category><![CDATA[Incremental Design]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[estimation]]></category>

		<guid isPermaLink="false">http://www.rallydev.com/engblog/?p=577</guid>
		<description><![CDATA[One of the most common sources of tension between product owners and developers is when product owners are surprised at how high an estimate for a story might be.  Usually this tension is easy to resolve by reiterating that the product owners really have no concept of how much something should cost.  However, [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: right; margin-left: 10px;"><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F01%2F26%2Fyou-have-to-buy-it-twice-before-its-cheap%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.rallydev.com%2Fengblog%2F2010%2F01%2F26%2Fyou-have-to-buy-it-twice-before-its-cheap%2F" height="61" width="51" /></a></div><p>One of the most common sources of tension between product owners and developers is when product owners are surprised at how high an estimate for a story might be.  Usually this tension is easy to resolve by reiterating that the product owners really have no concept of how much something should cost.  However, there is one scenario I see over and over again: when a product owner protests the estimate of a story because it seems, to the PO, like it&#8217;s simply re-using an aspect of the system somewhere else.</p>
<p>&#8220;I already paid for that!&#8221; he or she might say, quite correctly.  A simple example recently for us here was our column picker.  Our grids allow you to select which columns you wish to see on any given grid.  Later on, we created the ability to have grids inside of panels on user dashboards.  Our product owner wanted to put the column picker on the settings pane of a grid panel.  To him, this was simply reusing the column picker that had already been created for an earlier story.  He was quite shocked when our estimate for the effort was just as high as it was the first time.</p>
<p>His frustration is completely understandable.  How is this possible?  Why isn&#8217;t it easy to just drop that component into another place and have it work?  After all, if we were good engineers, wouldn&#8217;t we have written that component to be reusable?</p>
<p>Actually, <strong>no</strong>.  It&#8217;s <strong>because</strong> we were good engineers that it wasn&#8217;t written in that way.  This is one of the hardest things for product owners to understand, and I hope to explain it in this post.</p>
<p><strong>Evolutionary Design</strong></p>
<p>Once upon a time, development teams worked in specific phases.  First they would design all of the code they were going to write; boxes would be drawn on whiteboards, sequences captured in documents, and so on.  Once the design was finished, the developers would go off and write the code for it.  Luckily, our industry has realized that this is a problematic way to write software.</p>
<p>It&#8217;s simply not likely that the team will discover all of the quirks of the design during the design phase.  It&#8217;s even more likely that requirements and features will change during the long span of time this process takes, forcing the developers to throw away work and go &#8220;back to the drawing board&#8221; on the design.  Splitting the design phase from the coding phase is a recipe for disaster.</p>
<p>Instead, it&#8217;s generally preferable to practice <a href="http://en.wikipedia.org/wiki/Continuous_design">evolutionary design</a>; allow the design to grow organically as the code is written.  This, combined with testing and refactoring, seems to be the best known way to ensure that the quality of the code remains high as a product grows.</p>
<p>Doing this makes it easier to deal with changes that effect design, and it prevents the team from wasting time going &#8220;back to the drawing board&#8221; in the event a problem is discovered.</p>
<p><strong>YAGNI</strong></p>
<p>One principle to follow as you code is the <a href="http://en.wikipedia.org/wiki/YAGNI">YAGNI</a> principle.  YAGNI means &#8220;You Ain&#8217;t Gonna Need It&#8221; and essentially it means that developers should discourage themselves from adding additional code to support something that is not, at the time, required.  Developers have a strong desire to add code here or there while editing code, and YAGNI helps us remember to resist the urge to add superfluous code or functionality.</p>
<p>Following this principle helps eliminate waste, and helps keep developers focused on adding value that product owners want.  But why?</p>
<p>If I&#8217;m editing <strong><em>Module A</em></strong> to add <strong><em>Functionality X</em></strong>, why not just add <strong><em>Functionality Y</em></strong> while I&#8217;m there?  It may delay the delivering of <strong><em>Functionality X</em></strong> in the short-term, but later when it&#8217;s decided that <strong><em>Functionality Y</em></strong> must be supported as well, it will be dirt cheap, right?</p>
<p>The trick to understanding why YAGNI helps is realizing that there are thousands upon thousands of potential <strong><em>Module A</em></strong>s in a system.  The truth is, <strong><em>Functionality Y</em></strong> may simply never be required.  And even if it is, <strong><em>Functionality Z</em></strong> may not be.  Adding that functionality would be a waste of time and resources if it&#8217;s never used.</p>
<p><strong>YAGNI Applied to Design</strong></p>
<p>The same principle behind YAGNI can be extended to aspects of high-level design.  When we originally designed the Column Picker component, it was not designed to be dropped into any part of the system.  This was intentional: if we had spent the time to make the Column Picker completely generic and reusable, but then had never needed to drop it into another part of the system, the additional effort required to make it super-generic would have been a wasted effort.  This is true for any aspect of the system being designed: we can spend the extra time and effort to make something extremely generic, but if that generality is never needed, that time is waste.  Many times developers will refer to something that&#8217;s overly generic as &#8220;over-engineered.&#8221;</p>
<p>This means that the description of the component can often be misleading.  We never built &#8220;a column picker&#8221; &#8211; we built &#8220;a column picker for a grid page&#8221;.  The column picker was written in a way that assumed it was part of a grid page.  Again, it could have been written more generically, but doing so would have potentially created waste (<em>this does not mean that it&#8217;s alright to design code that violates <a href="http://en.wikipedia.org/wiki/Solid_(Object_Oriented_Design)">principles of good design</a>.   Code can be well-designed but still not fully generic</em>).</p>
<p>So when it came time to put the component in a completely different context than the one for which it was designed, it was still a lot of work: almost as much as was originally required to make the first pass.  Though some time was saved simply because it was a known and well-understood problem for which we would leverage newly-developed knowledge, it still effectively required writing a whole new component: one that was much more generic and utilized what it could of the previous increment.</p>
<p>Essentially, to avoid having developers waste time, components should be &#8220;generic on demand&#8221;.  Once a component needs to be reused, then it should be refactored into something extremely generic, but not before.  The end result is that a product owner will have to pay for a component twice before it&#8217;s cheap.</p>
<p>This may seem counterintuitive to product owners, but it&#8217;s important to understand.  Because the team approaches coding in a way that causes the second time to be nearly as expensive as the first, the team is able to deliver <strong>everything else</strong> quickly.</p>
<p>The first time, it is costly because the developers have to solve unknowns and create something new from scratch.</p>
<p>The second time, it is costly because it has to be molded into something generic and reusable.</p>
<p>The third time, however, is cheap.</p>
<img src="http://www.rallydev.com/engblog/?ak_action=api_record_view&id=577&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://www.rallydev.com/engblog/2010/01/26/you-have-to-buy-it-twice-before-its-cheap/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
