<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" version="2.0">
  <channel>
    <title>No Fluff Just Stuff</title>
    <link>http://www.nofluffjuststuff.com</link>
    <description>No Fluff Just Stuff</description>
    <item>
      <title>Active Resource Spy</title>
      <link>http://www.nofluffjuststuff.com/blog/david_bock/2012/02/active_resource_spy</link>
      <description>&lt;p&gt;I&amp;#39;m doing a lot of work lately using ActiveResource.&amp;#0160; I wanted to get &amp;#39;down to the wire&amp;#39; and see the traffic flowing over http... so a quick google search, and I found an entry I had written in 2008.&amp;#0160; I love it when that happens!&amp;#0160; So I&amp;#39;m reblogging it with a slight change to make it work in Ruby 1.9.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re using rails, create an initializer name active_resource_spy.rb and drop in this content:&lt;/p&gt;
&lt;p&gt;&lt;span style="font-family: courier new,courier;"&gt;class ActiveResource::Connection&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160; # Creates new Net::HTTP instance for communication with&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160; # remote service and resources.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160; def http&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160;&amp;#0160;&amp;#0160; http = Net::HTTP.new(@site.host, @site.port)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160;&amp;#0160;&amp;#0160; http.use_ssl = @site.is_a?(URI::HTTPS)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160;&amp;#0160;&amp;#0160; http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160;&amp;#0160;&amp;#0160; http.read_timeout = @timeout if @timeout&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160;&amp;#0160;&amp;#0160; #Here&amp;#39;s the addition that allows you to see the output&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160;&amp;#0160;&amp;#0160; http.set_debug_output $stderr&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160;&amp;#0160;&amp;#0160; return http&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; &amp;#0160; end&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new,courier;"&gt; end&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;All the data that flows over the http connection will be dumped to &lt;span style="font-family: courier new,courier;"&gt;$stderr &lt;/span&gt;(the terminal you started the app in, unless you redirected it).&lt;/p&gt;
&lt;p&gt;&lt;span style="font-family: courier new,courier;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 09 Feb 2012 10:42:00 CST</pubDate>
      <guid isPermaLink="true">http://blog.codesherpas.com/on_the_path/2012/02/active-resource-spy.html</guid>
      <dc:creator>David Bock</dc:creator>
    </item>
    <item>
      <title>Webinar for Geographically Distributed Agile Teams on Feb 15, Noon Eastern</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/02/webinar_for_geographically_distributed_agile_teams_on_feb_15_noon_eastern</link>
      <description>&lt;p&gt;Please join Shane Hastie and me for a &lt;a href="http://www.anymeeting.com/PIID=EB59DA828649 " target="_blank"&gt;webinar&lt;/a&gt; about our Geographically Distributed Teams workshop on Feb 15, at noon Eastern.&lt;/p&gt;
&lt;p&gt;Want to make your geographically distributed agile projects more effective? Join Johanna Rothman and Shane Hastie for this webinar where they will discuss their workshop, &lt;a href="http://www.jrothman.com/2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;Working Effectively In Geographically Distributed Agile Project Teams&lt;/a&gt;. We&amp;#8217;ll address a few of the common problems of geographically distributed agile teams, and then open it up for your questions.&lt;/p&gt;
&lt;p&gt;Even if you can&amp;#8217;t make the &lt;a href="http://www.anymeeting.com/PIID=EB59DA828649 " target="_blank"&gt;webinar&lt;/a&gt;, please sign up so I can tell you about the recording.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=W21WYZamPIk:MXb3KPbKTxk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=W21WYZamPIk:MXb3KPbKTxk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=W21WYZamPIk:MXb3KPbKTxk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=W21WYZamPIk:MXb3KPbKTxk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=W21WYZamPIk:MXb3KPbKTxk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=W21WYZamPIk:MXb3KPbKTxk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=W21WYZamPIk:MXb3KPbKTxk:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=W21WYZamPIk:MXb3KPbKTxk:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/W21WYZamPIk" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 07 Feb 2012 13:50:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11144</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Geographically Distributed Agile Teams Have Choices for Their Lifecycles</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/02/geographically_distributed_agile_teams_have_choices_for_their_lifecycles</link>
      <description>&lt;p&gt;I hope that by now you see that you have any number of choices for your lifecycle if you are geographically distributed team and you are transitioning to agile. I do recommend a servant leader agile project manager, for coordination and risk management. With people all over the world, it&amp;#8217;s difficult to coordinate the project, which leads to more risk.&lt;/p&gt;
&lt;p&gt;Scrum is often not the best approach to geographically distributed agile. That does not mean the distributed team should not go agile. It just means they should not use Scrum. If the distributed team can all travel to one place so they can get trained by the same Scrum trainer &lt;em&gt;together&lt;/em&gt;, and if they can take the opportunity to talk together to discuss what they need from the Scrum Master then maybe they can use Scrum, especially if they use their retrospectives well. It&amp;#8217;s a lot of responsibility for the team new to agile and new to Scrum. If you&amp;#8217;re a team new to agile and new to Scrum, and you try this, do yourself a favor and use a coach.&lt;/p&gt;
&lt;p&gt;Electronic tools do not always make seeing and managing the risk easier when you first start&amp;#8211;tools can prevent transparency until you understand what you are doing. I like starting with stickies or cards on a wall or board, and as the team members learn how to work, and what they, as a team need, then choosing a tool. I don&amp;#8217;t see how to choose a tool before you know what tool you need. One tip: do not allow your management to select a tool for you. You, as a team, can select a tool for yourself. You might start with digital pictures of your task board on a project wiki before you decide which tool to use.&lt;/p&gt;
&lt;p&gt;Let me recap:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jrothman.com/blog/mpd/2012/01/agile-lifecycles-for-geographically-distributed-teams-part-1.html" target="_blank"&gt;Agile Lifecycles for Geographically Distributed Teams, Part 1&lt;/a&gt; discussed iterations and silo&amp;#8217;d teams.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jrothman.com/blog/mpd/2012/01/agile-lifecycles-for-geographically-distributed-teams-part-2.html" target="_blank"&gt;Agile Lifecycles for Geographically Distributed Teams, Part 2&lt;/a&gt; discussed kanban and silo&amp;#8217;d teams.&lt;/p&gt;
&lt;p&gt;I got all hot under the collar and discussed &lt;a href="http://www.jrothman.com/blog/mpd/2012/02/why-an-agile-project-manager-is-not-a-scrum-master.html" target="_blank"&gt;Why an Agile Project Manager is Not a Scrum Master&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jrothman.com/blog/mpd/2012/02/agile-lifecycles-for-geographically-distributed-teams-part-3.html" target="_blank"&gt;Agile Lifecycles for Geographically Distributed Teams, Part 3&lt;/a&gt; discussed iterations and kanban and silo&amp;#8217;d teams.&lt;/p&gt;
&lt;p&gt;You should also read &lt;a href="http://www.jrothman.com/2008/01/what-lifecycle-selecting-the-right-model-for-your-project/" target="_blank"&gt;What Lifecycle? Selecting the Right Model for Your Project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have several teams all working towards one business goal, you have a program, and that is a different problem. Look for more blog posts on that, later.&lt;/p&gt;
&lt;p&gt;These are precisely the problems no one teaches you in project management certification courses. If you have read &lt;a href="http://www.jrothman.com/books/manage-it-your-guide-to-modern-pragmatic-project-management/" target="_blank"&gt;Manage It! Your Guide to Modern, Pragmatic Project Management&lt;/a&gt;, you know I consider knowledge of different lifecycles necessary for project managers.&lt;/p&gt;
&lt;p&gt;Project managers who try to control their teams better have a darn good reason&amp;#8211;these people are adults. They manage the rest of their lives. Are you trying to tell me they can&amp;#8217;t manage their work? And, these are the problems that can cause geographically distributed projects to fail, splat.&lt;/p&gt;
&lt;p&gt;If you want to learn to work more effectively on your geographically distributed team, please join Shane Hastie and me in a &lt;a href="../../../2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;workshop&lt;/a&gt; April 17-18, 2012. We would love to have you.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Qt6xDWwisfk:d5vdJNkKvfQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Qt6xDWwisfk:d5vdJNkKvfQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Qt6xDWwisfk:d5vdJNkKvfQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Qt6xDWwisfk:d5vdJNkKvfQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Qt6xDWwisfk:d5vdJNkKvfQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Qt6xDWwisfk:d5vdJNkKvfQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Qt6xDWwisfk:d5vdJNkKvfQ:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Qt6xDWwisfk:d5vdJNkKvfQ:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/Qt6xDWwisfk" height="1" width="1"/&gt;</description>
      <pubDate>Mon, 06 Feb 2012 09:56:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11128</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Agile Lifecycles for Geographically Distributed Teams, Part 3</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/02/agile_lifecycles_for_geographically_distributed_teams_part_3</link>
      <description>&lt;h2&gt;Example 3: Using a Project Manager with Iterations and Kanban and Silo&amp;#8217;d Teams&lt;/h2&gt;
&lt;p&gt;Here, the developers were in Cambridge, MA, the product owners were in San Francisco, the testers were in Bangalore, and the project manager was always flying somewhere, because the project manager was shared among several projects. The developers knew about timeboxed iterations, so they used timeboxes. Senior management had made the decision to fire all the local testers and buy cheaper tester time over the developers&amp;#8217; objections and move the testing to Bangalore. The Indian testers were very smart, and unfamiliar with the product, so the developers suggested the testers test feature by feature inside the iteration.&lt;/p&gt;
&lt;p&gt;The project manager suggested they use cumulative flow diagrams and cycle time measurements to make sure the developers were not developing &amp;#8220;too fast&amp;#8221; for the testers. The developers, still smarting over the loss of &amp;#8220;their testers&amp;#8221; were at first, peeved about this. They then realized the truth of this statement, and developed this kanban board.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.jrothman.com/blog/mpd/wp-content/uploads/2012/02/kanban.iteration.lifecycle.example1.jpg"&gt;&lt;img class="alignleft size-medium wp-image-11119" title="kanban.iteration.lifecycle.example1" src="http://www.jrothman.com/blog/mpd/wp-content/uploads/2012/02/kanban.iteration.lifecycle.example1-300x251.jpg" alt="" width="300" height="251" /&gt;&lt;/a&gt;You can see in this board, that four items are waiting to go into system test. Uh oh. The developers are out-producing what the testers can take. This is precisely what a kanban board can show you.&lt;/p&gt;
&lt;p&gt;The testers aren&amp;#8217;t stupid or slow. They are new. They cannot keep up with the developers. It&amp;#8217;s a fact of life, not a mystery of life. The developers have to act in some way to help the testers or the entire project will fail. The reason they are working in timeboxes as well as using kanban is that they have several contractual deliverables, that management, bless their tiny little hearts, committed to. The timebox allows the team or the product owners to meet with their customers and show them their progress. (They were deciding who would meet when I last worked with the team.) The kanban board help make the progress even more transparent.&lt;/p&gt;
&lt;p&gt;Iteration planning: The product owner and the project manager jointly work on the agile feature roadmap, and the product owner owns the roadmap responsibility for it. The product owner owns and generates the backlog. The product owner and the agile project manager present a strawman iteration backlog to the team at the start of the iteration. They have had difficulty finding iteration planning time that allows everyone to be awake and functioning, bless the senior managers&amp;#8217; little hearts.&lt;/p&gt;
&lt;p&gt;Daily commitment: They do a handoff, asking each other what they completed that day and what the impediments are. If you have read &lt;a href="http://www.jrothman.com/books/manage-it-your-guide-to-modern-pragmatic-project-management/" target="_blank"&gt;Manage It!&lt;/a&gt;, you know I modified the three questions to &amp;#8220;What did you complete, what are you planning to complete, what is in your way?&amp;#8221;&lt;/p&gt;
&lt;p&gt;Measurements: cumulative flow, average time to release a feature into the product. They are experimenting with burnup charts and impediment charts. They are still having trouble bringing the testers up to speed fast enough.&lt;/p&gt;
&lt;p&gt;Yes, they do retrospectives at the end of each iteration. Yes, the product owners own the backlogs.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ll summarize in the final part, the next entry.&lt;/p&gt;
&lt;p&gt;(Want to learn to work more effectively on your geographically distributed team? Join Shane Hastie and me in a &lt;a href="../../../2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;workshop&lt;/a&gt; April 17-18, 2012.)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fqjv0A-gJOw:XsnazvO6yPg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fqjv0A-gJOw:XsnazvO6yPg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fqjv0A-gJOw:XsnazvO6yPg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Fqjv0A-gJOw:XsnazvO6yPg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fqjv0A-gJOw:XsnazvO6yPg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Fqjv0A-gJOw:XsnazvO6yPg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fqjv0A-gJOw:XsnazvO6yPg:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fqjv0A-gJOw:XsnazvO6yPg:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/Fqjv0A-gJOw" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 03 Feb 2012 08:37:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11118</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Why an Agile Project Manager is Not a Scrum Master</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/02/why_an_agile_project_manager_is_not_a_scrum_master</link>
      <description>&lt;p&gt;A reader asked why the lifecycle in &lt;a href="http://www.jrothman.com/blog/mpd/2012/01/agile-lifecycles-for-geographically-distributed-teams-part-1.html" target="_blank"&gt;Agile Lifecycles for Geographically Distributed Teams, Part 1&lt;/a&gt; is not Scrum. It&amp;#8217;s not Scrum for these reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The project manager and product owner start the release planning and ask the team if the release planning is ok. The team does not generate the initial draft of release planning itself. In Scrum, the team is supposed to generate all of the planning itself.&lt;/li&gt;
&lt;li&gt;The checkin is different from the Scrum standup and the objectives of the checkin are different. I did suggest to the teams that if you want to create a cross-functional team where the functions are separated, if you ask people how they are working together, you might help them work together. Sometimes those questions work, and sometimes they don&amp;#8217;t. It depends on the team and whether the people want to work together.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I didn&amp;#8217;t mention retrospectives or backlogs in my examples so far, because I took them for granted. Yes, both examples of these teams do perform retrospectives and have product backlogs. They also have agile feature roadmaps, which are on my list to blog about.&lt;/p&gt;
&lt;p&gt;The real difference is the difference between a Scrum Master and an Agile Project Manager. A Scrum Master is not a project manager. A scrum master does not manage risk by him or herself. A project manager will take on the risk management responsibility without asking the team.&lt;/p&gt;
&lt;p&gt;A Scrum Master has only allegiance to the team. A project manager has responsibility to the team &lt;em&gt;and&lt;/em&gt; to the organization. That means that the project manager might feel torn when the organization pressures the project manager to do something stupid. (Although, I just downloaded the Scrum Guide, and the Scrum Master&amp;#8217;s responsibilities have grown considerably since I took my CSM with Jeff way back in 2006.)&lt;/p&gt;
&lt;p&gt;But agile provides transparency when the organization asks the agile project manager to do something stupid, so it&amp;#8217;s easier to retain your integrity as a project manager.&lt;/p&gt;
&lt;p&gt;Want to move a feature higher in the backlog? Change the feature roadmap with the product owner and then change the backlog with the product owner. I expect the agile project manager to collaborate on the feature roadmap and the backlog with the product owner.&lt;/p&gt;
&lt;p&gt;Want to change the velocity of the team to please some crazed manager? Both the Scrum Master or the agile project manager protects the team in these ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explain that velocity is not a productivity metric&lt;/li&gt;
&lt;li&gt;Say No and explain why&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.itjoblog.co.uk/2010/07/agile-management-new-schedule-game.html" target="_blank"&gt;Play the Double Your Velocity &lt;/a&gt;schedule game&lt;/li&gt;
&lt;li&gt;Or choose some other way to remove this management obstacle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Agile makes it easy to protect the team. The question is this: does the Scrum Master have other responsibilities in addition to protecting the team or is the Scrum Master full time? An agile project manager tends to be full time on a geographically distributed team. Even on a geographically distributed team, a Scrum Master is not seen as a full time position. Bless their tiny little hearts, managers don&amp;#8217;t seem to understand that transitioning to agile, especially for silo&amp;#8217;d distributed teams with different cultural norms is non-trivial. They will make room for a project manager, but a Scrum Master? Oh no. Makes me nuts.&lt;/p&gt;
&lt;p&gt;Cut corners on quality? I don&amp;#8217;t see how. The team doesn&amp;#8217;t meet the acceptance criteria on the stories and doesn&amp;#8217;t meet their criteria of done for an iteration, and can&amp;#8217;t show a demo. How does that serve anyone?&lt;/p&gt;
&lt;p&gt;Help a team go faster? This is the one place where a project manager &lt;em&gt;may&lt;/em&gt; have an edge over a Scrum Master, and that&amp;#8217;s only because of education. An agile project manager is a project manager. That means he or she is actively studying project management, which means he or she is studying lean also, looking into work in progress. (I realize many project managers do not actively study project management.) I have high expectations of an agile project manager, and that is to limit WIP, work in progress, to measure cumulative flow. But, Johanna, that&amp;#8217;s a lean project manager. Yes, that&amp;#8217;s correct. Why not use all of the tools available to us at all times? This is not to help a team actually go faster, but to provide feedback to the team about their WIP. If everyone takes a story at the start of the iteration and everyone always works on their own story, it&amp;#8217;s likely the team is at the slowest possible velocity. It&amp;#8217;s worth knowing that, or at least retrospecting about the data. A project manager will gather the data. A Scrum Master, especially one who was not a trained project manager, may not know to gather the data.&lt;/p&gt;
&lt;p&gt;I have nothing against Scrum Masters. Some of my good friends are CSTs (Certified Scrum Trainers). However, they are not all project managers, and have not been project managers, and have not studied the field of project management. Some have been. And, the real issue is this: In a two or three day workshop, they cannot convey to a person who may or may not have been a practicing project manager all of their project knowledge.&lt;/p&gt;
&lt;p&gt;Organizations do not always pick project managers to be Scrum Masters. And, with good reason. Some project managers are command-and-control project managers. I suspect back in my long-ago past, I was. I gave it up long ago because it didn&amp;#8217;t work. Some people never gave up command-and-control project management. Those people are not good project managers for agile projects. They are terrible project managers for geographically distributed projects, where you must work through influence.&lt;/p&gt;
&lt;p&gt;You can have self-managing teams that are geographically distributed. You can have self-directed teams that are geographically distributed. But, they don&amp;#8217;t start that way. They evolve into self-directed and self-managing teams. They start as management-led teams.&lt;/p&gt;
&lt;p&gt;And, especially when they are silo&amp;#8217;d teams, they need the coordination of a project manager, someone who will manage the risk between the silos, and someone who has the organizational backing, and yes, someone who has the allegiance to the organization to say, &amp;#8220;We need to do this project&amp;#8221; to write the project charter.&lt;/p&gt;
&lt;p&gt;In a geographically distributed team, the agile project manager writes the project charter either with the team, or as a strawman for the people to edit and approve. Shane and I recommend that the people get together to write it together. We like it if people get together in person. We know how rarely that happens. (Penny wise, pound foolish.) So we teach people how to write a project charter when they are divided in space.&lt;/p&gt;
&lt;p&gt;Because until there is a project charter, there is no organizing principle for the silo&amp;#8217;d teams. Those developers in France, testers in Belarus, product managers and project manager in San Francisco, they all need something to coalesce around. The charter, which includes the project vision provides that. The iterations provide the project heartbeat.&lt;/p&gt;
&lt;p&gt;So, that&amp;#8217;s why I don&amp;#8217;t think &lt;a href="http://www.jrothman.com/blog/mpd/2012/01/agile-lifecycles-for-geographically-distributed-teams-part-1.html" target="_blank"&gt;Agile Lifecycles for Geographically Distributed Teams, Part 1&lt;/a&gt; is Scrum. It&amp;#8217;s close, but no cigar. I respect Ken and Jeff&amp;#8217;s work too much to call it Scrum when it&amp;#8217;s not.&lt;/p&gt;
&lt;p&gt;Now that I&amp;#8217;m mostly recovered from my cold, I can continue the series about lifecycles.&lt;/p&gt;
&lt;p&gt;(Want to learn to work more effectively on your geographically distributed team? Join Shane Hastie and me in a &lt;a href="../../../2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;workshop&lt;/a&gt; April 17-18, 2012.)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=l5rftu71S1Q:IblcfQgxFlo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=l5rftu71S1Q:IblcfQgxFlo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=l5rftu71S1Q:IblcfQgxFlo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=l5rftu71S1Q:IblcfQgxFlo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=l5rftu71S1Q:IblcfQgxFlo:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=l5rftu71S1Q:IblcfQgxFlo:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=l5rftu71S1Q:IblcfQgxFlo:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=l5rftu71S1Q:IblcfQgxFlo:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/l5rftu71S1Q" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 01 Feb 2012 10:01:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11105</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>LinkedIn Etiquette</title>
      <link>http://www.nofluffjuststuff.com/blog/howard_lewis_ship/2012/01/linkedin_etiquette</link>
      <description>&lt;p&gt;I've used &lt;a href="http://linkedin.com"&gt;LinkedIn&lt;/a&gt; for many years now, long before I joined Facebook ... I liked the concept of never losing contact information with business contacts and technologist. It just seemed like a good idea (though I do sometimes wonder &lt;a href="http://www.youtube.com/watch?v=NcfXij6t4LA"&gt;if LinkedIn has any particular purpose&lt;/a&gt;).
&lt;p&gt;I tend to only connect with people I've met in person, or at least talked to on the phone.
&lt;p&gt;One thing that drives me crazy about LinkedIn is that you aren't forced to customize the message. As far as I'm concerned, the default message is like no message at all, and its a sign that you are just trolling for contacts. Just like you should always write a cover letter for a resume.
&lt;p&gt;So the first rule of LinkedIn is: &lt;strong&gt;always customize the message&lt;/strong&gt;. You chances of a connection go up significantly (at least with me) ... and with the default message, your chances drop down to near zero.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3214990755545065639?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/7DjI04zp4n0" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 27 Jan 2012 10:07:08 CST</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-4110180.post-3214990755545065639</guid>
      <dc:creator>Howard Lewis Ship</dc:creator>
    </item>
    <item>
      <title>Tapestry Advantages</title>
      <link>http://www.nofluffjuststuff.com/blog/howard_lewis_ship/2012/01/tapestry_advantages</link>
      <description>&lt;p&gt;A summary of a &lt;a href="http://tapestry.1045711.n5.nabble.com/Stuts-VS-Tapestry-I-need-some-ammo-td5432282.html"&gt;discussion about the advantages of Tapestry over Struts&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;Exceptional exception reporting&lt;/li&gt;
&lt;li&gt;Significantly less code&lt;/li&gt;
&lt;li&gt;Live class reloading&lt;/li&gt;
&lt;li&gt;Sensible defaults, especially for &lt;a href="http://en.wikipedia.org/wiki/SEO"&gt;SEO&lt;/a&gt;-friendly URLs&lt;/li&gt;
&lt;li&gt;Great community&lt;/li&gt;
&lt;li&gt;Flexibility and customizability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Interestingly, the quality of Tapestry's documentation was mentioned ... favorably! Between the revised &lt;a href="http://tapestry.apache.org"&gt;home page&lt;/a&gt;, and &lt;a href="http://jumpstart.doublenegative.com.au/home.html"&gt;Tapestry JumpStart&lt;/a&gt; (and &lt;a href="http://blog.tapestry5.de/index.php/2011/09/23/publishing-tapestry-5-in-action-book-on-my-own/"&gt;Igor's coming book&lt;/a&gt;), I think we're headed in the right direction in terms of documentation going from a liability to an asset.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3557656971524285189?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/Rw7zKtUFgjI" height="1" width="1"/&gt;</description>
      <pubDate>Thu, 26 Jan 2012 12:26:50 CST</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-4110180.post-3557656971524285189</guid>
      <dc:creator>Howard Lewis Ship</dc:creator>
    </item>
    <item>
      <title>Agile Lifecycles for Geographically Distributed Teams, Part 2</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/agile_lifecycles_for_geographically_distributed_teams_part_2</link>
      <description>&lt;h2&gt;Example 2: Using a Project Manager with Kanban, Silo&amp;#8217;d Teams&lt;/h2&gt;
&lt;p&gt;This is a product development organization with developers in Italy, testers in India, more developers in New York, product owners and project managers in California.&lt;/p&gt;
&lt;p&gt;This organization first tried iterations, but the team could never get to done. The problem was that the stories were too large. Normally I suggest smaller iterations, but one of the developers suggested they move to kanban.&lt;/p&gt;
&lt;p&gt;The New York developers had a problem biting off more than they could chew. So nothing moved across their board. The Italy developers had a board where the work did move across the board. The teams took pictures of their boards every day and shared the work across a project-based wiki. That allowed the New York-based developers to see the work move across the Italy board. And, that encouraged the New Yorkers to call the Italians and ask some questions. That helped the New Yorkers to change the size of their work by working with the product owners.&lt;/p&gt;
&lt;p&gt;Now, why did the New Yorkers have such trouble originally? Because the developers &amp;#8220;knew better&amp;#8221; than the product owners, so they changed the stories into architectural features when they had originally received them. (Now they don&amp;#8217;t. They leave the stories as real stories.)&lt;/p&gt;
&lt;p&gt;Release planning: Management in California plan with agile roadmaps. They have features planned specifically week-by-week for the next 6 weeks, and have more of a quarter-by-quarter approach after that.&lt;/p&gt;
&lt;p&gt;Iteration planning: No iteration planning because they are using kanban.&lt;/p&gt;
&lt;p&gt;Daily commitment: No daily commitment needed because they use kanban. They do have a checkin a few times a week with each other as a technical team to make sure they don&amp;#8217;t create bottlenecks and that they respect the WIP (work in progress) limits.&lt;/p&gt;
&lt;p&gt;At one point, both the New York and Italy developer teams created automated tests so that the testers could catch up and stay caught up with regression tests. They add a story like that every couple of weeks, and they are paying down their automated testing debt.&lt;/p&gt;
&lt;p&gt;The Project manager keeps an eye on the WIP, work in progress. Project manager also shepherds the product owner into keeping the queue of incoming work full and properly ranked. The product owner is notorious for changing the incoming work queue &lt;em&gt;all&lt;/em&gt; the time. Project manager makes sure the team does retrospectives and is a little unclear how to do them in such a distributed team. The project manager is not so sure their retrospectives are working, and has started an obstacle list, to make sure the team has transparency for their obstacles.&lt;/p&gt;
&lt;p&gt;Measurements: cumulative flow, average time to release a feature into the product.&lt;/p&gt;
&lt;p&gt;(Want to learn to work more effectively on your geographically distributed team? Join Shane Hastie and me in a &lt;a href="http://www.jrothman.com/2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;workshop&lt;/a&gt; April 17-18, 2012.)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Tsz7O6IOAko:F9o-L0VxMWs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Tsz7O6IOAko:F9o-L0VxMWs:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Tsz7O6IOAko:F9o-L0VxMWs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Tsz7O6IOAko:F9o-L0VxMWs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Tsz7O6IOAko:F9o-L0VxMWs:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Tsz7O6IOAko:F9o-L0VxMWs:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Tsz7O6IOAko:F9o-L0VxMWs:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Tsz7O6IOAko:F9o-L0VxMWs:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/Tsz7O6IOAko" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 25 Jan 2012 10:06:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11076</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Tapestry 5.4: Focus on JavaScript</title>
      <link>http://www.nofluffjuststuff.com/blog/howard_lewis_ship/2012/01/tapestry_5_4_focus_on_javascript_1</link>
      <description>&lt;div&gt;
  &lt;p&gt;
       Tapestry 5.3.1 is out in the wild ... and if Tapestry is to
       stay relevant, Tapestry 5.4 is going to need to be something
       quite (r)evolutionary.
  &lt;/p&gt;

&lt;blockquote&gt;
  There was some confusion on the Tapestry developer mailing list in
  advance of this blog post; I'd alluded that it was coming, and some
  objected to such pronouncements coming out fully formed, without
  discussion. In reality, this is just a distillation of ideas, a
  starting point, and not a complete, finalized solution. If it's more
  detailed than some discussions of Tapestry's evolution in the past,
  that just means that the mailing list discussion and eventual
  implementation will be that much better informed.
&lt;/blockquote&gt;
  &lt;p&gt;
    In posts and other conversations, I've alluded to my vision for
    Tapestry 5.4. As always, the point of Tapestry is to allow
    developers to &lt;em&gt;code less, deliver more&lt;/em&gt;, and that has been
    the focus of Tapestry on the server side: everything drives that
    point: terseness of code and templates, live class reloading, and
    excellent feedback are critical factors there. Much of what went
    into Tapestry 5.3 strengthened those points ... enhancements to
    Tapestry's meta-programming capabilities, improvements to the IoC
    container, and reducing Tapestry's memory footprint in a number of
    ways. I have one client reporting a 30% reduction in memory
    utilization, and another reporting a 30 - 40% improvement in
    execution speed.
  &lt;/p&gt;

  &lt;p&gt;
    Interestingly, I think that for Tapestry to truly stay relevant,
    it needs to shift much, much, more of the emphasis to the client
    side. For some time, Tapestry has been walking a fine line with
    regards to the critical question of &lt;em&gt;where does the application
    execute?&lt;/em&gt;  Pre-Ajax, that was an easy question: the
    application runs on the server, with at most minor JavaScript
    tricks and validations on the client.  As the use of Ajax has
    matured, and customer expectations for application behavior in the
    browser have expanded, it is no longer acceptable to say that
    Tapestry is page based, with limited Ajax
    enhancements. Increasingly, application flow and business logic
    need to execute in the browser, and the server-side's role is to
    orchestrate and facilitate the client-side application, as well as
    to act as a source and sink of data ultimately stored in a
    database.
  &lt;/p&gt;

  &lt;p&gt;
    As Tapestry's server-side has matured, the client side has not
    kept sufficient pace. Tapestry does include some excellent
    features, such as how it allows the server-side to drive
    client-side JavaScript in a modular and efficient way. However,
    that is increasingly insufficient ... and the tension caused by
    give-and-take between client-side and server-side logic has grown
    with each release.
  &lt;/p&gt;

  &lt;p&gt;
    Nowhere is this more evident than in how Tapestry addresses HTML
    forms. This has always been a tricky issue in Tapestry, because
    the dynamic rendering that can occur needs to be matched by
    dynamic form submission processing. In Tapestry, the approach is
    to serialize into the form instructions that will be used when the
    form is submitted (see the &lt;code&gt;store()&lt;/code&gt; method of the
    &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/FormSupport.html"&gt;FormSupport&lt;/a&gt;
    API). These instructions are used during the processing of the
    form submission request to re-configure the necessary components,
    and direct them to read their query parameters, perform
    validations, and push updated values back into server-side objects
    properties. If you've ever wondered what
    the &lt;code&gt;t:formdata&lt;/code&gt; hidden input field inside every
    Tapestry forms is about ... well, now you know: it's a serialized
    stream of Java objects, GZipped and MIME encoded.

  &lt;/p&gt;
  &lt;p&gt;
    However, relative to many other things in Tapestry, this is a bit
    clumsy and limited. You start to notice this when you see the
    tepid response to questions on the mailing list such as "&lt;em&gt;how
    to do cross-field validation?&lt;/em&gt;" Doing more complicated things,
    such as highly dynamic form layouts, or forms with even marginal
    relationships between fields, can be problematic (though still
    generally possible) ... but it requires a bit too much internal
    knowledge of Tapestry, and the in-browser results feel a bit
    kludgy, a bit clumsy. Tapestry starts to feel like it is getting
    in the way, and that's never acceptible.
  &lt;/p&gt;
  &lt;p&gt;
    Simply put, Tapestry's abstractions on forms and fields is
    both &lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;leaky&lt;/a&gt;
    and insufficient. Tapestry is trying to do too much, and simply
    can't keep up with modern, reasonable demands in terms of
    responsiveness and useability inside the client. We've become used
    to pages rebuilding and reformatting themselves even while we're
    typing.  For Tapestry to understand how to process the form
    submission, it needs a model of what the form looks like on the
    client-side, and it simply doesn't have it. There isn't an
    effective way to do so without significantly restricting what is
    possible on the client side, or requiring much more data to be
    passed in requests, or stored server-side in the session.
  &lt;/p&gt;
  &lt;p&gt;
    The primary issue here is that overall form submission cycle,
    especially combined with Tapestry's need to serialize commands
    into the form (as the hidden &lt;code&gt;t:formdata&lt;/code&gt; field). Once
    you add Ajax to this mix, where new fields and rules are created
    dynamically (on the server side) and installed into the
    client-side DOM ... well, it gets harder and harder to manage.
    Add in a few more complications (such as a mix of transient and
    persistent Hibernate entities, or dynamic creation of sub-entities
    and relationships) into a form, it can be a brain burner getting
    Tapestry to do the right thing when the form is submitted: you
    need to understand exactly how Tapestry processes
    that &lt;code&gt;t:formdata&lt;/code&gt; information, and how to add your own
    callbacks into the callback stream to accomplish just exactly the
    right thing at just exactly the right time. Again, this is not the
    Tapestry way, where things are expected to &lt;em&gt;just work&lt;/em&gt;.
  &lt;/p&gt;
  &lt;p&gt;
    Further, there is some doubt about even the desirability of the
    overall model. In many cases, it makes sense to batch together a
    series of changes to individual properties ... but in many more,
    it is just as desirable for individual changes to filter back to
    the server (and the database) as the user
    navigates. Form-submit-and-re-render is
    a &lt;a href="http://en.wikipedia.org/wiki/IBM_3270"&gt;green screen&lt;/a&gt;
    style of user interaction. Direct interaction is the expectation
    now, and that's something Tapestry should embrace.
  &lt;/p&gt;
  &lt;p&gt;
    What's the solution, then? Well, it's still very much a moving
    target. The goal is to make creating client-side JavaScript
    libraries easier, to make it easier to integrate with libraries such
    as &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; (and its vast library
    of extensions), make things simpler and more efficient on the
    client side, and not sacrifice the features that make Tapestry fun
    and productive in the first place.&lt;/p&gt;

  &lt;h3&gt;Overall Vision&lt;/h3&gt;

  &lt;p&gt;The overall vision breaks down into a number of steps:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Reduce or remove outside dependencies&lt;/li&gt;
    &lt;li&gt;Modularize JavaScript&lt;/li&gt;
    &lt;li&gt;Change page initializations to use modules&lt;/li&gt;
    &lt;li&gt;Embrace client-side controller logic&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;
    Of course, all of these steps depend on the others, so there isn't
    a good order to discuss them.
  &lt;/p&gt;

  &lt;h3&gt;Reducing and removing outside dependencies&lt;/h3&gt;

  &lt;p&gt;
    Tapestry's client-side strength has always been lots of "out of
    the box" functionality: client-side validation, Zones and other
    Ajax-oriented behaviors, and a well-integrated system for
    performing page-level initializations.
  &lt;/p&gt;
  &lt;p&gt;
    However, this strength is also a weakness, since that out of the
    box behavior is too tightly tied to the &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt; and
    &lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; libraries ... reasonable choices in 2006, but
    out-of-step with the industry today. Not just in terms of the
    momentum behind jQuery, but also in terms of very different
    approaches, such
    as &lt;a href="http://www.sencha.com/"&gt;Sencha/ExtJS&lt;/a&gt; and
    others.&lt;/p&gt;

  &lt;p&gt;
    It was a conscious decision in 2006 to not attempt to create an
    abstraction layer before I understood all the abstractions. I've
    had the intermediate time to embrace those abstractions. Now the
    big problem is momentum and backwards compatibility.&lt;/p&gt;

  &lt;p&gt;
    Be removing unnecessary behaviors, such as animations, we can
    reduce Tapestry's client-side needs. Tapestry needs to be able
    to &lt;strong&gt;attach event handlers&lt;/strong&gt; to elements. It needs to
    be able to easily &lt;strong&gt;locate elements&lt;/strong&gt; via unique ids,
    or via &lt;strong&gt;CSS selectors&lt;/strong&gt;. It needs to be able
    to &lt;strong&gt;run Ajax requests&lt;/strong&gt; and handle the responses,
    including &lt;strong&gt;dynamic updates to elements&lt;/strong&gt;.&lt;/p&gt;
  &lt;p&gt;

    &lt;p&gt;All of these things are reasonable to abstract, and by making
    it even easier to execute JavaScript as part of a page render or
    page update
    (&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ajax/AjaxResponseRenderer.html"&gt;something
    already present in Tapestry 5.3&lt;/a&gt;), currently built-in features
    (such as animations) can be delegated to the application, which is
    likely a better choice in any case.&lt;/p&gt;

  &lt;h3&gt;Modularizing JavaScript&lt;/h3&gt;

  &lt;p&gt;
    Tapestry has always been careful about avoiding client-side
    namespace polution.  Through release 5.2, most of Tapestry's
    JavaScript was encapulated in the &lt;code&gt;Tapestry&lt;/code&gt; object. In
    Tapestry 5.3, a second object, &lt;code&gt;T5&lt;/code&gt; was introduced with
    the intention that it gradually replace the
    original &lt;code&gt;Tapestry&lt;/code&gt; object (but this post represents a
    change in direction).&lt;/p&gt;
  &lt;p&gt;
    However, that's not enough. Too often, users have created in-line
    JavaScript, or JavaScript libraries that defined "bare" variables
    and functions (that are ultimately added to the
    browser's &lt;code&gt;window&lt;/code&gt; object). This causes problems,
    including collisions between components (that provide competing
    definitions of objects and functions), or behavior that varies
    depending on whether the JavaScript was added to the page as part
    of a full-page render, or via an Ajax partial page render.&lt;/p&gt;
  &lt;p&gt;
    The right approach is to encourage and embrace some form of
    &lt;a href="http://yuiblog.com/blog/2007/06/12/module-pattern/"&gt;JavaScript
      module architecture&lt;/a&gt;, where there are no explicit global
    variables or functions, and that all JavaScript is evaluated
    inside a function, allowing for private variables and
    functions.&lt;/p&gt;
  &lt;p&gt;
    Currently, I'm thinking in terms of &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt; as
    the way to organize the JavaScript. Tapestry would faciliate
    organizing its own code into modules, as well as
    application-specific (or even page-specific) JavaScript
    modules. This would mean that de-referencing the &lt;code&gt;T5&lt;/code&gt;
    object would no longer occur (outside of some kind of temporary
    compatibility mode).&lt;/p&gt;
  &lt;p&gt;
    For example, clicking a button inside some container element
    might, under 5.3, publish an event using Tapestry's client-side
    publish/subscribe system. In the following example, the click
    events bubble up from the buttons (with the &lt;code&gt;button&lt;/code&gt;
    CSS class name) to a container element, and are then published
    under the topic name &lt;code&gt;button-clicked&lt;/code&gt;.
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1603920.js?file=53pubsubexample.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Consider this an abbreviated example, as it doesn't explain where
    the &lt;code&gt;element&lt;/code&gt; variable is defined or initialized; the
    important part is the interaction with Tapestry's client-side
    library: the reference to the &lt;code&gt;T5.pubsub.publish&lt;/code&gt; function.
  &lt;/p&gt;
  &lt;p&gt;
    Under 5.4, using the RequireJS &lt;code&gt;require&lt;/code&gt; function, this
    might be coded instead as:
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1603930.js?file=54pubsubexample.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Here, the &lt;code&gt;t5/pubsub&lt;/code&gt; module will be loaded by RequireJS and
    passed as a parameter into the function, which is automatically
    executed. So, this supports JavaScript modularization, and
    leverages RequireJS's ability to load modules on-the-fly, 
    as needed.
  &lt;/p&gt;
  &lt;p&gt;
    Notice the difference between the two examples; in the first
    example, coding as a module was &lt;em&gt;optional&lt;/em&gt; (but
    recommended), since the necessary &lt;code&gt;publish()&lt;/code&gt; function
    was accessible either way. In the 5.4 example, coding using
    JavaScript modules is virtually &lt;em&gt;required&lt;/em&gt;: the anonymous
    function passed to &lt;code&gt;require()&lt;/code&gt; is effectively a module,
    but its only through the use of &lt;code&gt;require()&lt;/code&gt; (or
    RequireJS's &lt;code&gt;define()&lt;/code&gt;) that the &lt;code&gt;publish()&lt;/code&gt;
    function can be accessed. &lt;/p&gt;
  &lt;p&gt;
    This is both the carrot and the stick; the carrot is how easy it
    is to declare dependencies and have them passed in to your
    function-as-a-module. The stick is that (eventually)
    the &lt;em&gt;only&lt;/em&gt; way to access those dependencies is by providing
    a module and declaring dependencies.
  &lt;/p&gt;
  &lt;h3&gt;Change page initializations to use modules&lt;/h3&gt;

  &lt;p&gt;
    Tapestry has a reasonably sophisticated system for allowing
    components to describe their JavaScript requirements as they
    render, in the form of
    the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html"&gt;JavaScriptSupport&lt;/a&gt;
    environmental (an environmental is a kind of
    per-thread/per-request service object).  Methods on
    JavaScriptSupport allow a component to request that a JavaScript
    library be imported in the page (though this is most commonly
    accomplished using
    the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Import.html"&gt;Import&lt;/a&gt;
    annotation), and to request the &lt;em&gt;initialization functions&lt;/em&gt;
    get executed.&lt;/p&gt;
  &lt;p&gt;
    Part of Tapestry's Ajax support is that in an Ajax request, the
    JavaScriptSupport methods can still be invoked, but a completely
    different implementation is responsible for integrating those
    requests into the overall reply (which in an Ajax request is a JSON object, rather
    than a simple stream of HTML).
  &lt;/p&gt;
  &lt;p&gt;
    Here's an example component from the TapX library:&lt;/p&gt;

&lt;script src="https://gist.github.com/1673763.js?file=Expando.java"&gt;&lt;/script&gt;

  &lt;p&gt;
    The @Import annotation directs that a stack (a set of related
    JavaScript libraries, defined elsewhere) be imported into the
    page; alternately, the component could import any number of
    specific JavaScript files, located either in the web application
    context folder, or on the classpath.&lt;/p&gt;
  &lt;p&gt;
    Inside the &lt;code&gt;afterRender()&lt;/code&gt; method, the code constructs
    a &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/json/JSONObject.html"&gt;JSONObject&lt;/a&gt;
    of data needed on the client side to perform the operation. The
    call to &lt;code&gt;addInitializerCall&lt;/code&gt; references a function by
    name: this function must be added to
    the &lt;code&gt;T5.Initializers&lt;/code&gt; namespace object.  Notice the
    naming: &lt;code&gt;tapxExpando&lt;/code&gt;: a prefix to identify the
    library, and to prevent collisions with any other application or
    library that also added its own functions to
    the &lt;code&gt;T5.initializers&lt;/code&gt; object.
  &lt;/p&gt;
  &lt;p&gt;
    The JavaScript library includes the function that will be invoked:
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1673810.js?file=tapx-core.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Under 5.4, this would largely be the same except:
  &lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;There will be a specific Java package for each library (or the
      application) to store library modules.&lt;/li&gt;
    &lt;li&gt;The JavaScriptSupport environmental will have new methods to
      reference a function, inside a module, to invoke.&lt;/li&gt;
      &lt;li&gt;Stacks will consist not just of individual libraries, but
 also modules, following the naming and packaging
 convention.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Embrace client-side controller logic&lt;/h3&gt;

&lt;p&gt;
  The changes discussed so far only smooth out a few rough edges;
  they still position Tapestry code, running on the server, as driving
  the entire show. 
 &lt;/p&gt;

&lt;p&gt;
  As alluded to earlier; for any sophisticated user interface, the
  challenge is to coordinate the client-side user interface (in terms
  of form fields, DOM elements, and query parameters) with the
  server-side components; this is encoded into the
  hidden &lt;code&gt;t:formdata&lt;/code&gt; field. However, it is my opinion that
  for any dynamic form, Tapestry is or near the end of the road for
  this approach.&lt;/p&gt;
&lt;p&gt;Instead, it's time to embrace client-logic, written in JavaScript,
  in the browser. Specifically, break away from HTML forms, and
  embrace a more dynamic structure, one where "submitting" a form
  always works through an Ajax update ... and what is sent is not a
  simple set of query parameters and values, but a JSON representation
  of what was updated, changed, or created.
&lt;/p&gt;
&lt;p&gt;
  My specific vision is to
  integrate &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone.js&lt;/a&gt;
  (or something quite similar), to move this logic solidly to the
  client side. This is a fundamental change: one where the client-side
  is free to change and reconfigure the UI in any way it likes, and is
  ultimately responsible for packaging up the completed data and
  sending it to the server.
&lt;/p&gt;
&lt;p&gt;
  When you are used to
  the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/BeanEditForm.html"&gt;BeanEditForm&lt;/a&gt;
  component, this might feel like a step backwards, as you end up
  responsible for writing a bit more code (in JavaScript) to
  implement the user interface, input validations, and relationships
  between fields. However, as fun as BeanEditForm is, the declarative
  approach to validation on the client and the server has proven to be
  limited and limiting, especially in the face of cross-field
  relationships. We could attempt to extend the declarative nature,
  introducing rules or even scripting languages to establish the
  relationships ... or we could move in a situation that puts the
  developer back in the driver's seat.&lt;/p&gt;
&lt;p&gt;
  Further, there are some that will be concerned that this is a
  violation of
  the &lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY
  pricipal&lt;/a&gt;; however I subscribe to different philosophy that
  client-side and server-side validation are fundamentally different
  in any case; this is discussed in an excellent
  &lt;a href="http://blog.ianbicking.org/2011/03/30/js-on-server-and-client-is-not-a-big-deal/"&gt;blog
  post by Ian Bickling&lt;/a&gt;.

&lt;p&gt;
  Certainly there will be components and services to assist with this
  process, in term of extracting data into JSON format, and converting
  JSON data into a set of updates to the server-side objects. There's
  also a number of security concerns that necessitate careful
  validation of what comes up from the client in the Ajax request.
  Further, there will be new bundled libraries to make it easier to
  build these dynamic user interfaces.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;
  In this vision of Tapestry's future, the server-side framework
  starts to shift from the focus of all behavior to
  the &lt;em&gt;facilitator&lt;/em&gt;: it paints the broad stokes on the server,
  but the key interactions end up working exclusively on the
  client. &lt;/p&gt;
&lt;p&gt;
  I'm sure this view will be controversial: after all, on the surface,
  what the community really wants is just "jQuery instead of
  Prototype". However, all of the factors described in the above
  sections are, I feel, critical to keeping Tapestry relevant by
  embracing the client-side in the way that the client-side
  demands.&lt;/p&gt;

&lt;p&gt;
  I think this change in focus is a big deal; I think it is also
  necessary for Tapestry to stay relevant in the medium to long
  term. I've heard from many individual developers (not necessarily
  Tapestry users) that what they really want is "just jQuery and a
  restful API"; I think Tapestry can be that restful API, but by
  leveraging many of Tapestry's other strengths, it can be a lot more.
  Building something right on the metal feels empowering ... until you
  hit all the infrastructure that Tapestry provides, including
  best-of-class exception reporting, on-the-fly JavaScript aggregation
  and minimization, and (of course) live class reloading during
  development.
java&lt;/p&gt;  
  &lt;p&gt;
    I'm eager to bring Tapestry to the forfront of web application
  development ... and to deliver it fast!  Monitor the Tapestry
  developer mailing list to see how this all plays out.&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/4110180-7447306489952674714?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/mlpJ4i8ShSA" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 25 Jan 2012 09:46:00 CST</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-4110180.post-7447306489952674714</guid>
      <dc:creator>Howard Lewis Ship</dc:creator>
    </item>
    <item>
      <title>Tapestry 5.4: Focus on JavaScript</title>
      <link>http://www.nofluffjuststuff.com/blog/howard_lewis_ship/2012/01/tapestry_5_4_focus_on_javascript</link>
      <description>&lt;div&gt;
  &lt;p&gt;
       Tapestry 5.3.1 is out in the wild ... and if Tapestry is to
       stay relevant, Tapestry 5.4 is going to need to be something
       quite (r)evolutionary.
  &lt;/p&gt;

&lt;blockquote&gt;
  There was some confusion on the Tapestry developer mailing list in
  advance of this blog post; I'd alluded that it was coming, and some
  objected to such pronouncements coming out fully formed, without
  discussion. In reality, this is just a distillation of ideas, a
  starting point, and not a complete, finalized solution. If it's more
  detailed than some discussions of Tapestry's evolution in the past,
  that just means that the mailing list discussion and eventual
  implementation will be that much better informed.
&lt;/blockquote&gt;
  &lt;p&gt;
    In posts and other conversations, I've alluded to my vision for
    Tapestry 5.4. As always, the point of Tapestry is to allow
    developers to &lt;em&gt;code less, deliver more&lt;/em&gt;, and that has been
    the focus of Tapestry on the server side: everything drives that
    point: terseness of code and templates, live r2eloading, and
    excellent feedback are critical factors there. Much of what went
    into Tapestry 5.3 strengthened those points ... enhancements to
    Tapestry's meta-programming capabilities, improvements to the IoC
    container, and reducing Tapestry's memory footprint in a number of
    ways. I have one client reporting a 30% reduction in memory
    utilization, and another reporting a 30 - 40% improvement in
    execution speed.
  &lt;/p&gt;

  &lt;p&gt;
    Interestingly, I think that for Tapestry to truly stay relevant,
    it needs to shift much, much, more of the emphasis to the client
    side. For some time, Tapestry has been walking a fine line with
    regards to the critical question of &lt;em&gt;where does the application
    execute?&lt;/em&gt;  Pre-Ajax, that was an easy question: the
    application runs on the server, with at most minor JavaScript
    tricks and validations on the client.  As the use of Ajax has
    matured, and customer expectations for application behavior in the
    browser have expanded, it is no longer acceptable to say that
    Tapestry is page based, with limited Ajax
    enhancements. Increasingly, application flow and business logic
    need to execute in the browser, and the server-side's role is to
    orchestrate and facilitate the client-side application, as well as
    to act as a source and sink of data ultimately stored in a
    database.
  &lt;/p&gt;

  &lt;p&gt;
    As Tapestry's server-side has matured, the client side has not
    kept sufficient pace. Tapestry does include some excellent
    features, such as how it allows the server-side to drive
    client-side JavaScript in a modular and efficient way. However,
    that is increasingly insufficient ... and the tension caused by
    give-and-take between client-side and server-side logic has grown
    with each release.
  &lt;/p&gt;

  &lt;p&gt;
    Nowhere is this more evident than in how Tapestry addresses HTML
    forms. This has always been a tricky issue in Tapestry, because
    the dynamic rendering that can occur needs to be matched by
    dynamic form submission processing. In Tapestry, the approach is
    to serialize into the form instructions that will be used when the
    form is submitted (see the &lt;code&gt;store()&lt;/code&gt; method of the
    &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/FormSupport.html"&gt;FormSupport&lt;/a&gt;
    API). These instructions are used during the processing of the
    form submission request to re-configure the necessary components,
    and direct them to read their query parameters, perform
    validations, and push updated values back into server-side objects
    properties. If you've ever wondered what
    the &lt;code&gt;t:formdata&lt;/code&gt; hidden input field inside every
    Tapestry forms is about ... well, now you know: it's a serialized
    stream of Java objects, GZipped and MIME encoded.

  &lt;/p&gt;
  &lt;p&gt;
    However, relative to many other things in Tapestry, this is a bit
    clumsy and limited. You start to notice this when you see the
    tepid response to questions on the mailing list such as "&lt;em&gt;how
    to do cross-field validation?&lt;/em&gt;" Doing more complicated things,
    such as highly dynamic form layouts, or forms with even marginal
    relationships between fields, can be problematic (though still
    generally possible) ... but it requires a bit too much internal
    knowledge of Tapestry, and the in-browser results feel a bit
    kludgy, a bit clumsy. Tapestry starts to feel like it is getting
    in the way, and that's never acceptible.
  &lt;/p&gt;
  &lt;p&gt;
    Simply put, Tapestry's abstractions on forms and fields is
    both &lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;leaky&lt;/a&gt;
    and insufficient. Tapestry is trying to do too much, and simply
    can't keep up with modern, reasonable demands in terms of
    responsiveness and useability inside the client. We've become used
    to pages rebuilding and reformatting themselves even while we're
    typing.  For Tapestry to understand how to process the form
    submission, it needs a model of what the form looks like on the
    client-side, and it simply doesn't have it. There isn't an
    effective way to do so without significantly restricting what is
    possible on the client side, or requiring much more data to be
    passed in requests, or stored server-side in the session.
  &lt;/p&gt;
  &lt;p&gt;
    The primary issue here is that overall form submission cycle,
    especially combined with Tapestry's need to serialize commands
    into the form (as the hidden &lt;code&gt;t:formdata&lt;/code&gt; field). Once
    you add Ajax to this mix, where new fields and rules are created
    dynamically (on the server side) and installed into the
    client-side DOM ... well, it gets harder and harder to manage.
    Add in a few more complications (such as a mix of transient and
    persistent Hibernate entities, or dynamic creation of sub-entities
    and relationships) into a form, it can be a brain burner getting
    Tapestry to do the right thing when the form is submitted: you
    need to understand exactly how Tapestry processes
    that &lt;code&gt;t:formdata&lt;/code&gt; information, and how to add your own
    callbacks into the callback stream to accomplish just exactly the
    right thing at just exactly the right time. Again, this is not the
    Tapestry way, where things are expected to &lt;em&gt;just work&lt;/em&gt;.
  &lt;/p&gt;
  &lt;p&gt;
    Further, there is some doubt about even the desirability of the
    overall model. In many cases, it makes sense to batch together a
    series of changes to individual properties ... but in many more,
    it is just as desirable for individual changes to filter back to
    the server (and the database) as the user
    navigates. Form-submit-and-re-render is
    a &lt;a href="http://en.wikipedia.org/wiki/IBM_3270"&gt;green screen&lt;/a&gt;
    style of user interaction. Direct interaction is the expectation
    now, and that's something Tapestry should embrace.
  &lt;/p&gt;
  &lt;p&gt;
    What's the solution, then? Well, it's still very much a moving
    target. The goal is to make creating client-side JavaScript
    libraries easier, to make it easier to integrate with libraries such
    as &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; (and its vast library
    of extensions), make things simpler and more efficient on the
    client side, and not sacrifice the features that make Tapestry fun
    and productive in the first place.&lt;/p&gt;

  &lt;h3&gt;Overall Vision&lt;/h3&gt;

  &lt;p&gt;The overall vision breaks down into a number of steps:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Reduce or remove outside dependencies&lt;/li&gt;
    &lt;li&gt;Modularize JavaScript&lt;/li&gt;
    &lt;li&gt;Change page initializations to use modules&lt;/li&gt;
    &lt;li&gt;Embrace client-side controller logic&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;
    Of course, all of these steps depend on the others, so there isn't
    a good order to discuss them.
  &lt;/p&gt;

  &lt;h3&gt;Reducing and removing outside dependencies&lt;/h3&gt;

  &lt;p&gt;
    Tapestry's client-side strength has always been lots of "out of
    the box" functionality: client-side validation, Zones and other
    Ajax-oriented behaviors, and a well-integrated system for
    performing page-level initializations.
  &lt;/p&gt;
  &lt;p&gt;
    However, this strength is also a weakness, since that out of the
    box behavior is too tightly tied to the &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt; and
    &lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; libraries ... reasonable choices in 2006, but
    out-of-step with the industry today. Not just in terms of the
    momentum behind jQuery, but also in terms of very different
    approaches, such
    as &lt;a href="http://www.sencha.com/"&gt;Sencha/ExtJS&lt;/a&gt; and
    others.&lt;/p&gt;

  &lt;p&gt;
    It was a conscious decision in 2006 to not attempt to create an
    abstraction layer before I understood all the abstractions. I've
    had the intermediate time to embrace those abstractions. Now the
    big problem is momentum and backwards compatibility.&lt;/p&gt;

  &lt;p&gt;
    Be removing unnecessary behaviors, such as animations, we can
    reduce Tapestry's client-side needs. Tapestry needs to be able
    to &lt;strong&gt;attach event handlers&lt;/strong&gt; to elements. It needs to
    be able to easily &lt;strong&gt;locate elements&lt;/strong&gt; via unique ids,
    or via &lt;strong&gt;CSS selectors&lt;/strong&gt;. It needs to be able
    to &lt;strong&gt;run Ajax requests&lt;/strong&gt; and handle the responses,
    including &lt;strong&gt;dynamic updates to elements&lt;/strong&gt;.&lt;/p&gt;
  &lt;p&gt;

    &lt;p&gt;All of these things are reasonable to abstract, and by making
    it even easier to execute JavaScript as part of a page render or
    page update
    (&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ajax/AjaxResponseRenderer.html"&gt;something
    already present in Tapestry 5.3&lt;/a&gt;), currently built-in features
    (such as animations) can be delegated to the application, which is
    likely a better choice in any case.&lt;/p&gt;

  &lt;h3&gt;Modularizing JavaScript&lt;/h3&gt;

  &lt;p&gt;
    Tapestry has always been careful about avoiding client-side
    namespace polution.  Through release 5.2, most of Tapestry's
    JavaScript was encapulated in the &lt;code&gt;Tapestry&lt;/code&gt; object. In
    Tapestry 5.3, a second object, &lt;code&gt;T5&lt;/code&gt; was introduced with
    the intention that it gradually replace the
    original &lt;code&gt;Tapestry&lt;/code&gt; object (but this post represents a
    change in direction).&lt;/p&gt;
  &lt;p&gt;
    However, that's not enough. Too often, users have created in-line
    JavaScript, or JavaScript libraries that defined "bare" variables
    and functions (that are ultimately added to the
    browser's &lt;code&gt;window&lt;/code&gt; object). This causes problems,
    including collisions between components (that provide competing
    definitions of objects and functions), or behavior that varies
    depending on whether the JavaScript was added to the page as part
    of a full-page render, or via an Ajax partial page render.&lt;/p&gt;
  &lt;p&gt;
    The right approach is to encourage and embrace some form of
    &lt;a href="http://yuiblog.com/blog/2007/06/12/module-pattern/"&gt;JavaScript
      module architecture&lt;/a&gt;, where there are no explicit global
    variables or functions, and that all JavaScript is evaluated
    inside a function, allowing for private variables and
    functions.&lt;/p&gt;
  &lt;p&gt;
    Currently, I'm thinking in terms of &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt; as
    the way to organize the JavaScript. Tapestry would faciliate
    organizing its own code into modules, as well as
    application-specific (or even page-specific) JavaScript
    modules. This would mean that de-referencing the &lt;code&gt;T5&lt;/code&gt;
    object would no longer occur (outside of some kind of temporary
    compatibility mode).&lt;/p&gt;
  &lt;p&gt;
    For example, clicking a button inside some container element
    might, under 5.3, publish an event using Tapestry's client-side
    publish/subscribe system. In the following example, the click
    events bubble up from the buttons (with the &lt;code&gt;button&lt;/code&gt;
    CSS class name) to a container element, and are then published
    under the topic name &lt;code&gt;button-clicked&lt;/code&gt;.
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1603920.js?file=53pubsubexample.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Consider this an abbreviated example, as it doesn't explain where
    the &lt;code&gt;element&lt;/code&gt; variable is defined or initialized; the
    important part is the interaction with Tapestry's client-side
    library: the reference to the &lt;code&gt;T5.pubsub.publish&lt;/code&gt; function.
  &lt;/p&gt;
  &lt;p&gt;
    Under 5.4, using the RequireJS &lt;code&gt;require&lt;/code&gt; function, this
    might be coded instead as:
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1603930.js?file=54pubsubexample.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Here, the &lt;code&gt;t5/pubsub&lt;/code&gt; module will be loaded by RequireJS and
    passed as a parameter into the function, which is automatically
    executed. So, this supports JavaScript modularization, and
    leverages RequireJS's ability to load modules on-the-fly, 
    as needed.
  &lt;/p&gt;
  &lt;p&gt;
    Notice the difference between the two examples; in the first
    example, coding as a module was &lt;em&gt;optional&lt;/em&gt; (but
    recommended), since the necessary &lt;code&gt;publish()&lt;/code&gt; function
    was accessible either way. In the 5.4 example, coding using
    JavaScript modules is virtually &lt;em&gt;required&lt;/em&gt;: the anonymous
    function passed to &lt;code&gt;require()&lt;/code&gt; is effectively a module,
    but its only through the use of &lt;code&gt;require()&lt;/code&gt; (or
    RequireJS's &lt;code&gt;define()&lt;/code&gt;) that the &lt;code&gt;publish()&lt;/code&gt;
    function can be accessed. &lt;/p&gt;
  &lt;p&gt;
    This is both the carrot and the stick; the carrot is how easy it
    is to declare dependencies and have them passed in to your
    function-as-a-module. The stick is that (eventually)
    the &lt;em&gt;only&lt;/em&gt; way to access those dependencies is by providing
    a module and declaring dependencies.
  &lt;/p&gt;
  &lt;h3&gt;Change page initializations to use modules&lt;/h3&gt;

  &lt;p&gt;
    Tapestry has a reasonably sophisticated system for allowing
    components to describe their JavaScript requirements as they
    render, in the form of
    the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html"&gt;JavaScriptSupport&lt;/a&gt;
    environmental (an environmental is a kind of
    per-thread/per-request service object).  Methods on
    JavaScriptSupport allow a component to request that a JavaScript
    library be imported in the page (though this is most commonly
    accomplished using
    the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Import.html"&gt;Import&lt;/a&gt;
    annotation), and to request the &lt;em&gt;initialization functions&lt;/em&gt;
    get executed.&lt;/p&gt;
  &lt;p&gt;
    Part of Tapestry's Ajax support is that in an Ajax request, the
    JavaScriptSupport methods can still be invoked, but a completely
    different implementation is responsible for integrating those
    requests into the overall reply (which in an Ajax request is a JSON object, rather
    than a simple stream of HTML).
  &lt;/p&gt;
  &lt;p&gt;
    Here's an example component from the TapX library:&lt;/p&gt;

&lt;script src="https://gist.github.com/1673763.js?file=Expando.java"&gt;&lt;/script&gt;

  &lt;p&gt;
    The @Import annotation directs that a stack (a set of related
    JavaScript libraries, defined elsewhere) be imported into the
    page; alternately, the component could import any number of
    specific JavaScript files, located either in the web application
    context folder, or on the classpath.&lt;/p&gt;
  &lt;p&gt;
    Inside the &lt;code&gt;afterRender()&lt;/code&gt; method, the code constructs
    a &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/json/JSONObject.html"&gt;JSONObject&lt;/a&gt;
    of data needed on the client side to perform the operation. The
    call to &lt;code&gt;addInitializerCall&lt;/code&gt; references a function by
    name: this function must be added to
    the &lt;code&gt;T5.Initializers&lt;/code&gt; namespace object.  Notice the
    naming: &lt;code&gt;tapxExpando&lt;/code&gt;: a prefix to identify the
    library, and to prevent collisions with any other application or
    library that also added its own functions to
    the &lt;code&gt;T5.initializers&lt;/code&gt; object.
  &lt;/p&gt;
  &lt;p&gt;
    The JavaScript library includes the function that will be invoked:
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1673810.js?file=tapx-core.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Under 5.4, this would largely be the same except:
  &lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;There will be a specific Java package for each library (or the
      application) to store library modules.&lt;/li&gt;
    &lt;li&gt;The JavaScriptSupport environmental will have new methods to
      reference a function, inside a module, to invoke.&lt;/li&gt;
      &lt;li&gt;Stacks will consist not just of individual libraries, but
 also modules, following the naming and packaging
 convention.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Embrace client-side controller logic&lt;/h3&gt;

&lt;p&gt;
  The changes discussed so far only smooth out a few rough edges;
  they still position Tapestry code, running on the server, as driving
  the entire show. 
 &lt;/p&gt;

&lt;p&gt;
  As alluded to earlier; for any sophisticated user interface, the
  challenge is to coordinate the client-side user interface (in terms
  of form fields, DOM elements, and query parameters) with the
  server-side components; this is encoded into the
  hidden &lt;code&gt;t:formdata&lt;/code&gt; field. However, it is my opinion that
  for any dynamic form, Tapestry is or near the end of the road for
  this approach.&lt;/p&gt;
&lt;p&gt;Instead, it's time to embrace client-logic, written in JavaScript,
  in the browser. Specifically, break away from HTML forms, and
  embrace a more dynamic structure, one where "submitting" a form
  always works through an Ajax update ... and what is sent is not a
  simple set of query parameters and values, but a JSON representation
  of what was updated, changed, or created.
&lt;/p&gt;
&lt;p&gt;
  My specific vision is to
  integrate &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone.js&lt;/a&gt;
  (or something quite similar), to move this logic solidly to the
  client side. This is a fundamental change: one where the client-side
  is free to change and reconfigure the UI in any way it likes, and is
  ultimately responsible for packaging up the completed data and
  sending it to the server.
&lt;/p&gt;
&lt;p&gt;
  When you are used to
  the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/BeanEditForm.html"&gt;BeanEditForm&lt;/a&gt;
  component, this might feel like a step backwards, as you end up
  responsible for writing a bit more code (in JavaScript) to
  implement the user interface, input validations, and relationships
  between fields. However, as fun as BeanEditForm is, the declarative
  approach to validation on the client and the server has proven to be
  limited and limiting, especially in the face of cross-field
  relationships. We could attempt to extend the declarative nature,
  introducing rules or even scripting languages to establish the
  relationships ... or we could move in a situation that puts the
  developer back in the driver's seat.&lt;/p&gt;
&lt;p&gt;
  Further, there are some that will be concerned that this is a
  violation of
  the &lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY
  pricipal&lt;/a&gt;; however I subscribe to different philosophy that
  client-side and server-side validation are fundamentally different
  in any case; this is discussed in an excellent
  &lt;a href="http://blog.ianbicking.org/2011/03/30/js-on-server-and-client-is-not-a-big-deal/"&gt;blog
  post by Ian Bickling&lt;/a&gt;.

&lt;p&gt;
  Certainly there will be components and services to assist with this
  process, in term of extracting data into JSON format, and converting
  JSON data into a set of updates to the server-side objects. There's
  also a number of security concerns that necessitate careful
  validation of what comes up from the client in the Ajax request.
  Further, there will be new bundled libraries to make it easier to
  build these dynamic user interfaces.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;
  In this vision of Tapestry's future, the server-side framework
  starts to shift from the focus of all behavior to
  the &lt;em&gt;facilitator&lt;/em&gt;: it paints the broad stokes on the server,
  but the key interactions end up working exclusively on the
  client. &lt;/p&gt;
&lt;p&gt;
  I'm sure this view will be controversial: after all, on the surface,
  what the community really wants is just "jQuery instead of
  Prototype". However, all of the factors described in the above
  sections are, I feel, critical to keeping Tapestry relevant by
  embracing the client-side in the way that the client-side
  demands.&lt;/p&gt;

&lt;p&gt;
  I think this change in focus is a big deal; I think it is also
  necessary for Tapestry to stay relevant in the medium to long
  term. I've heard from many individual developers (not necessarily
  Tapestry users) that what they really want is "just jQuery and a
  restful API"; I think Tapestry can be that restful API, but by
  leveraging many of Tapestry's other strengths, it can be a lot more.
  Building something right on the metal feels empowering ... until you
  hit all the infrastructure that Tapestry provides, including
  best-of-class exception reporting, on-the-fly JavaScript aggregation
  and minimization, and (of course) live class reloading during
  development.
java&lt;/p&gt;  
  &lt;p&gt;
    I'm eager to bring Tapestry to the forfront of web application
  development ... and to deliver it fast!  Monitor the Tapestry
  developer mailing list to see how this all plays out.&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/4110180-7447306489952674714?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/mlpJ4i8ShSA" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 24 Jan 2012 19:24:33 CST</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-4110180.post-7447306489952674714</guid>
      <dc:creator>Howard Lewis Ship</dc:creator>
    </item>
    <item>
      <title>Review: Gradle Class with Luke Daley</title>
      <link>http://www.nofluffjuststuff.com/blog/howard_lewis_ship/2012/01/review_gradle_class_with_luke_daley</link>
      <description>&lt;p&gt;Last week, Luke Daly arrived in Portland to teach a three day &lt;a href="http://gradle.org/"&gt;Gradle&lt;/a&gt; class; the folks at Gradleware were nice enough let me audit the class (so it only cost me a couple of thousand dollars of lost billing revenue to attend).  My goals for the class was to gain a deeper understanding of how Gradle works, so that I could write more efficient builds, diagnose problems, and write my own plugins. The class scored very high on all of those counts!&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Much of the first day was spent on basics, including a very useful introduction to the Groovy programming language (which is the basis of the &lt;a href="http://gradle.org/docs/current/dsl/index.html"&gt;Gradle DSL&lt;/a&gt;). Even though I have used Groovy pretty extensively for testing purposes over the last couple of years, there were features I've glossed over in the past that turned out to be very useful.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The class is taught largely bottom up: from the basics of declaring tasks, then actions on tasks, and gradually working up towards defining tasks inputs and outputs. Compiling Java came pretty late, which seems curious since that's the primary job of Gradle, but this makes sense from a bottom-up approach as &lt;a href="http://gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.plugins.JavaPluginConvention:sourceSets(groovy.lang.Closure)"&gt;SourceSets&lt;/a&gt; can then be described correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The class alternates between lecture and discussion, and short focused labs. At the end of the third day, we worked on tuning up our own builds, passing the video projector cable around ... we had a good time making the Tapestry build more efficient and readable, as well as adding new features to it.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;I'm feeling very good about my Gradle skills after attending the course; I've already been able to make further improvements to the Tapestry build subsequently, and I have plans for more involving things going forward. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The return on investment for this class is a bit tricky; Gradle simply does so much straight out of the box, and even without a strong understanding of its structure, you can &lt;em&gt;kind of&lt;/em&gt; get it to do what you want just by guesswork and Googling. If you've been using Gradle for a while, I give this a cautious recommendation: getting back the three days of invested time may take a while to pay off. On the other hand, if you are currently dependent on Ant or Maven ... &lt;a href="http://gradle.org/training"&gt;sign up for the next class&lt;/a&gt; and get yourself switched over, today!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-4121218058413204699?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/stulP-4bay8" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 24 Jan 2012 12:32:18 CST</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-4110180.post-4121218058413204699</guid>
      <dc:creator>Howard Lewis Ship</dc:creator>
    </item>
    <item>
      <title>Agile Lifecycles for Geographically Distributed Teams, Part 1</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/agile_lifecycles_for_geographically_distributed_teams_part_1</link>
      <description>&lt;p&gt;I&amp;#8217;ve been working with geographically distributed and dispersed teams for the past couple of years. Some of them on quite large programs, some of them reasonably small. What they all have in common is that they all want to transition to agile.&lt;/p&gt;
&lt;p&gt;Most of them start this way: someone takes a Scrum class, gets all excited. This is good. Then reality hits. Scrum is meant for collocated geographically cross-functional teams. Uh oh.&lt;/p&gt;
&lt;p&gt;Almost all of these teams are separated by function: the developers are in one place, the testers are in another, the business analysts are in a third place, the project managers are in a fourth places, and if there are product owners (or what passes for product owners) they are often in a fifth location. It&amp;#8217;s not uncommon for every single &lt;em&gt;function&lt;/em&gt; of the team to be separate from every other member of the team. So, the teams don&amp;#8217;t fit the Scrum criteria. Uh oh.&lt;/p&gt;
&lt;p&gt;Since Scrum has so much brand recognition, these people think if they can&amp;#8217;t do Scrum, they can&amp;#8217;t do Agile. Nope, not so. What they need to do is start from the &lt;a href="http://www.agilemanifesto.org/principles.html" target="_blank"&gt;values and principles&lt;/a&gt; of the Agile Manifesto, and go from there. They create their own lifecycle, and their very own brand of Agile.&lt;/p&gt;
&lt;p&gt;When I worked with one client, that client thought they could extend their iteration. Nope, if anything, that means you keep the iterations even shorter, because you need &lt;em&gt;more&lt;/em&gt; frequent feedback when no one is in the same place. Well, there were words. And more words. But, if you start from the values, you see that short iterations are the way to go if you want to be agile. Otherwise, you get staged delivery, which is a lovely lifecycle, but not agile.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m blogging a series of examples. Please don&amp;#8217;t ask me why the people ended up in these locations. I have no idea. All I know is that&amp;#8217;s where the people are.&lt;/p&gt;
&lt;h2&gt;Example 1: Using a Project Manager With Iterations, Silo&amp;#8217;d Teams&lt;/h2&gt;
&lt;p&gt;One IT organization has teams with developers in the Ukraine, testers in India, product managers and project managers in the UK, and enterprise architecture and corporate management in the eastern US.&lt;/p&gt;
&lt;p&gt;This organization moved to two-week iterations. The developers were 3.5 hours ahead of the testers, which was not terrible. This organization had these problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The product managers had to learn to be product owners and write stories that were small enough to finish inside one iteration.&lt;/li&gt;
&lt;li&gt;The enterprise architects had to stop dictating the architecture without features to hang off the architecture.&lt;/li&gt;
&lt;li&gt;The developers and testers had to learn to implement by feature so the architects could help the team see the evolving architecture.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This organization had a ton of command-and-control to start. The project managers needed to facilitate the teams, not control them. The architects needed to help the teams see how to organize the product, not to tell the developers what to do. The testers needed to not be order-takers, as in taking orders from the developers.&lt;/p&gt;
&lt;p&gt;You might ask why the organization wanted to move to agile. Senior management wanted agile because the releases got longer and longer and longer, and could not accommodate change. Agile was a complete cultural shift. The two-week iterations, along with an agile roadmap of features helped a lot.&lt;/p&gt;
&lt;p&gt;The pilot project team consisted of the developers, testers, a product manager, and a project manager. The team rejected the enterprise architect as a member of the team because the architect refused to write code.&lt;/p&gt;
&lt;p&gt;Release planning: The project manager and the product manager do an initial cut at release planning as a strawman and presented it to the team. &amp;#8220;Can you do this? What do you think?&amp;#8221;&lt;/p&gt;
&lt;p&gt;Iteration planning: The team does iteration planning together, making sure every story is either small, medium, or large, where a large story can be done by the entire team in fewer than three days. The team makes sure they get every started story to done at the end of the iteration.&lt;/p&gt;
&lt;p&gt;Daily commitment: The team does a daily checkin, not a standup. They timebox the checkin to 15 minutes. They ask these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What did you complete and with whom yesterday? (reinforces the idea that people work together)&lt;/li&gt;
&lt;li&gt;What are you working on and with whom today?&lt;/li&gt;
&lt;li&gt;What are your impediments?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The project manager who acts as a servant leader, not a command/controller manages the impediments.&lt;/p&gt;
&lt;p&gt;The pilot project has two experienced agile people: the project manager and a developer. Both act as servant leaders.&lt;/p&gt;
&lt;p&gt;Measurements: burnup charts, impediment charts&lt;/p&gt;
&lt;p&gt;The pilot team has been together for six months now, and is successful. This is not Scrum. It&amp;#8217;s not Kanban. It&amp;#8217;s agile and it&amp;#8217;s working. They are ready to start another project team, working by attraction.&lt;/p&gt;
&lt;p&gt;(Want to learn to work more effectively on your geographically distributed team? Join Shane Hastie and me in a &lt;a href="http://www.jrothman.com/2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;workshop&lt;/a&gt; April 17-18, 2012.)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0BNXuAHvurA:-H5g8BLnnu8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0BNXuAHvurA:-H5g8BLnnu8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0BNXuAHvurA:-H5g8BLnnu8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=0BNXuAHvurA:-H5g8BLnnu8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0BNXuAHvurA:-H5g8BLnnu8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=0BNXuAHvurA:-H5g8BLnnu8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0BNXuAHvurA:-H5g8BLnnu8:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0BNXuAHvurA:-H5g8BLnnu8:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/0BNXuAHvurA" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 24 Jan 2012 07:09:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11069</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Drum Roll: Public Workshop April 17-18, 2012</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/drum_roll_public_workshop_april_17_18_2012</link>
      <description>&lt;p&gt;I&amp;#8217;m so pleased to announce that Shane Hastie and I are leading a workshop on &lt;a href="http://www.jrothman.com/2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;Working Effectively In Geographically Distributed Agile Project Teams&lt;/a&gt;, April 17-18, 2012 in Pleasanton, CA. Yes, that is Elisabeth Hendrickson&amp;#8217;s &lt;a href="http://www.agilistry.com/" target="_blank"&gt;Agilistry Studio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Shane and I first delivered this workshop last year in Australia, when I was there for &lt;a href="http://www.softed.com/" target="_blank"&gt;Software Education&lt;/a&gt;&amp;#8216;s SDC. We had a great time, and so did many of the participants. We have since evolved the workshop, to address the needs of the participants who did &lt;em&gt;not&lt;/em&gt; have a great time, and to make sure we covered the topics we need to cover.&lt;/p&gt;
&lt;p&gt;This is an experiential workshop. You will learn by doing and debriefing. If you&amp;#8217;ve taken my one-day versions over the past year, you&amp;#8217;ve had a taste of what we do in the two-day. You will learn even more from both of us. Remember, we developed this as a geographically distributed pair.&lt;/p&gt;
&lt;p&gt;One of the benefits of signing up for the workshop is the informal consulting you can obtain, not just from us, but from the other people there. You&amp;#8217;ll hear what other people are doing, what&amp;#8217;s working and what&amp;#8217;s not working. If you want, you can hear from Shane and me about what&amp;#8217;s working and not working at our clients as they transition to agile and explore more agile approaches.&lt;/p&gt;
&lt;p&gt;Do check out the &lt;a href="http://www.jrothman.com/2012/01/working-effectively-in-geographically-distributed-agile-project-teams/" target="_blank"&gt;syllabus&lt;/a&gt;. And, if you&amp;#8217;re ready to sign up, please &lt;a href="http://www.jrothman.com/store/" target="_blank"&gt;register&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=F1lWgw6M6_Q:k9YJmrC1XnM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=F1lWgw6M6_Q:k9YJmrC1XnM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=F1lWgw6M6_Q:k9YJmrC1XnM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=F1lWgw6M6_Q:k9YJmrC1XnM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=F1lWgw6M6_Q:k9YJmrC1XnM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=F1lWgw6M6_Q:k9YJmrC1XnM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=F1lWgw6M6_Q:k9YJmrC1XnM:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=F1lWgw6M6_Q:k9YJmrC1XnM:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/F1lWgw6M6_Q" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 20 Jan 2012 08:04:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11089</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Pragmatic Manager and InfoQ Video Posted</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/pragmatic_manager_and_infoq_video_posted</link>
      <description>&lt;p&gt;I have posted last week&amp;#8217;s Pragmatic Manager, &lt;a href="http://www.jrothman.com/2012/01/are-you-being-guilted-into-doing-more/" target="_blank"&gt;Are You Being Guilted Into Doing More?&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At Agile 2011, I had a great video conversation with Shane Hastie about &lt;a href="http://www.infoq.com/interviews/rothman-agile-portfolio-management" target="_blank"&gt;agile project portfolio management&lt;/a&gt;. The chair is big, I&amp;#8217;m not so short. The chair is big, I&amp;#8217;m not so short. How many times do you think I have to say that to make it true? The chair is big, I&amp;#8217;m not so short. That ought to do it.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=7hbnb6ByU8k:bsp9O4m3KiI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=7hbnb6ByU8k:bsp9O4m3KiI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=7hbnb6ByU8k:bsp9O4m3KiI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=7hbnb6ByU8k:bsp9O4m3KiI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=7hbnb6ByU8k:bsp9O4m3KiI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=7hbnb6ByU8k:bsp9O4m3KiI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=7hbnb6ByU8k:bsp9O4m3KiI:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=7hbnb6ByU8k:bsp9O4m3KiI:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/7hbnb6ByU8k" height="1" width="1"/&gt;</description>
      <pubDate>Thu, 19 Jan 2012 06:51:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11064</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Hackergarten in PDX - Friday January 20th</title>
      <link>http://www.nofluffjuststuff.com/blog/howard_lewis_ship/2012/01/hackergarten_in_pdx__friday_january_20th</link>
      <description>&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://hackergarten.net/hackergarten_b_and_w_small.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="150" width="392" src="http://hackergarten.net/hackergarten_b_and_w_small.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;Merlyn Albery-Speyer is organizing a &lt;a href="http://hackergarten.net/"&gt;Hackergarten&lt;/a&gt; while &lt;a href="http://ldaley.com/"&gt;Luke Daley&lt;/a&gt; (creator of &lt;a href="http://www.gebish.org/"&gt;Geb&lt;/a&gt;, and &lt;a href="http://gradle.org/"&gt;Gradle&lt;/a&gt; committer) is in town to run an &lt;a href="http://gradleware.com/training"&gt;in-depth Gradle training&lt;/a&gt;.  I'll be there, working on Tapestry, or Gradle, or a video game, or something.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Please see &lt;a href="http://www.curiousattemptbunny.com/2012/01/hackergarten-in-pdx-friday-january-20th.html"&gt;Meryln's blog&lt;/a&gt; to RSVP.  I look forward to meeting and coding with more PDX peeps!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-8652247449868717675?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/_TBpJwaKHvM" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 13 Jan 2012 10:06:36 CST</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-4110180.post-8652247449868717675</guid>
      <dc:creator>Howard Lewis Ship</dc:creator>
    </item>
    <item>
      <title>Elvis carried away by spaceships</title>
      <link>http://www.nofluffjuststuff.com/blog/kenneth_kousen/2012/01/elvis_carried_away_by_spaceships</link>
      <description>&lt;p&gt;I love teaching Groovy to existing Java developers, because they have such a hard time holding back Tears Of Joy when they see how much easier life can be. Today, though, I did a quick demo that resulted in a line of Groovy that was so amusing I had to post it here.&lt;/p&gt;
&lt;p&gt;Consider a trivial POGO (Plain Old Groovy Object) called &lt;code&gt;Course&lt;/code&gt;:&lt;br /&gt;
&lt;pre class="brush: plain;"&gt;
class Course
    String name
    int days
    String toString() { &amp;quot;($name,$days)&amp;quot; }
}
&lt;/pre&gt;&lt;br /&gt;
The goal was to take a collection of courses and sort it by the number of days. That&amp;#8217;s really easy in Groovy:&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
def courses = [
    new Course(name:'Groovy',days:4),
    new Course(name:'Grails',days:3),
    new Course(name:'Spring',days:4),
    new Course(name:'Hibernate',days:3)
]

assert courses.toString() == '[(Groovy,4), (Grails,3), (Spring,4), (Hibernate,3)]'

courses.sort { it.days }

assert courses*.days == [3, 3, 4, 4]
&lt;/pre&gt;&lt;br /&gt;
The &lt;code&gt;sort&lt;/code&gt; method in the &lt;code&gt;java.util.Collection&lt;/code&gt; class is part of the Groovy JDK, meaning it&amp;#8217;s one of the methods Groovy adds to the standard Java libraries. It takes a closure of either one or two arguments. In this case, I&amp;#8217;m using the one-argument closure, which is used to select a property on which to base the sort. By specifying &lt;code&gt;it.days&lt;/code&gt; in the closure, I&amp;#8217;m telling the &lt;code&gt;sort&lt;/code&gt; method to sort the courses based on their &lt;code&gt;days&lt;/code&gt; property. Then I verify that the sort worked by checking that the courses are the right order, using the spread-dot operator to just look at the number of days.&lt;/p&gt;
&lt;p&gt;In class the question that always comes up is, can I sort by days and then by name? In other words, if two courses have the same number of days, can I then sort by the &lt;code&gt;name&lt;/code&gt; property?&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s what the two-argument closure on the &lt;code&gt;sort&lt;/code&gt; method is for. The two arguments are references to any pair of courses, and the closure should return a negative number, zero, or positive number according to whether the first course is less than, equal to, or greater than the second.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s where things get amusing. The sort I want is:&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
courses.sort { a,b -&amp;gt;
    a.days &amp;lt;=&amp;gt; b.days ?: a.name &amp;lt;=&amp;gt; b.name
}

assert courses.toString() == '[(Grails,3), (Hibernate,3), (Groovy,4), (Spring,4)]'
&lt;/pre&gt;&lt;br /&gt;
The body of the closure on &lt;code&gt;sort&lt;/code&gt; uses the spaceship operator &amp;lt;=&amp;gt;, which returns -1, 0, or 1 depending on whether the left side is less than, equal to, or greater than the right side. I use spaceship to compare the &lt;code&gt;days&lt;/code&gt; properties. Then I add the Elvis operator ?: which means if the days comparison is not zero, use it, but otherwise use the following comparison, which uses another spaceship to compare by name.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s only after writing the code in class that one of the students pointed out that I had Elvis in between two spaceships, leading to the following observations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The spaceships are there to return Elvis to his home planet&lt;/li&gt;
&lt;li&gt;It takes two &lt;a href="http://www.smallartworks.ca/Gallery/J2/jupiter2.JPG"&gt;spaceships&lt;/a&gt;, working in tandem, to carry Elvis away, in much the same way &lt;a href="http://www.youtube.com/watch?v=rzcLQRXW6B0"&gt;two swallows can carry a coconut in tandem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Therefore, the Elvis being carried away must be the &lt;a href="http://www.antisteez.com/wp-content/uploads/2009/10/fat-elvis.jpg"&gt;fat Elvis&lt;/a&gt; from the 70s, rather than the &lt;a href="http://hairstylesarea.com/hair-pic/HLIC/11fc6966ee56903bf40fee5c8f462800.jpg"&gt;thin, cool Elvis&lt;/a&gt; from the 50s
&lt;/ol&gt;
&lt;p&gt;Either way, after the sort is finished, Elvis has left the building.&lt;/p&gt;
&lt;p&gt;Thank you, thank you very much.&lt;/p&gt;
&lt;br /&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kousenit.wordpress.com/335/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kousenit.wordpress.com/335/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kousenit.wordpress.com/335/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kousenit.wordpress.com/335/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/kousenit.wordpress.com/335/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/kousenit.wordpress.com/335/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/kousenit.wordpress.com/335/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/kousenit.wordpress.com/335/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kousenit.wordpress.com/335/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kousenit.wordpress.com/335/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kousenit.wordpress.com/335/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kousenit.wordpress.com/335/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kousenit.wordpress.com/335/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kousenit.wordpress.com/335/" /&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kousenit.wordpress.com&amp;amp;blog=186706&amp;amp;post=335&amp;amp;subd=kousenit&amp;amp;ref=&amp;amp;feed=1" width="1" height="1" /&gt;</description>
      <pubDate>Fri, 13 Jan 2012 00:25:00 CST</pubDate>
      <guid isPermaLink="true">http://kousenit.wordpress.com/?p=335</guid>
      <dc:creator>Kenneth Kousen</dc:creator>
    </item>
    <item>
      <title>Who’s Playing Agile Schedule Games Posted</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/who_s_playing_agile_schedule_games_posted</link>
      <description>&lt;p&gt;My new Gantthead column is up, &lt;a href="http://www.gantthead.com/content/articles/269499.cfm" target="_blank"&gt;Who&amp;#8217;s Playing Agile Schedule Games?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you liked the schedule games from the more traditional projects, you&amp;#8217;ll love the agile schedule games. Please comment over there.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Gbvbee4idFE:SEKWjq6GO70:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Gbvbee4idFE:SEKWjq6GO70:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Gbvbee4idFE:SEKWjq6GO70:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Gbvbee4idFE:SEKWjq6GO70:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Gbvbee4idFE:SEKWjq6GO70:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Gbvbee4idFE:SEKWjq6GO70:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Gbvbee4idFE:SEKWjq6GO70:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Gbvbee4idFE:SEKWjq6GO70:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/Gbvbee4idFE" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 11 Jan 2012 06:18:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11060</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Pragmatic Manager Posted: Are Your Shoulds Driving Your Decisions</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/pragmatic_manager_posted_are_your_shoulds_driving_your_decisions</link>
      <description>&lt;p&gt;I posted my most recent Pragmatic Manager: &lt;a href="http://www.jrothman.com/2012/01/are-your-shoulds-driving-your-decisions/" target="_blank"&gt;Are Your &amp;#8220;Shoulds&amp;#8221; Driving Your Decisions?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Yes, in case you couldn&amp;#8217;t tell, I am doing a series on project portfolio management, so that you do take a look at my &lt;a href="http://www.jrothman.com/services/peer-project-portfolio-coaching/" target="_blank"&gt;Peer Project Portfolio Coaching&lt;/a&gt;. Several people took advantage of the early bird pricing. We&amp;#8217;re in the not-quite-early-bird pricing now. And, if you sign up with a buddy, you can still get early bird pricing for the two of you. It&amp;#8217;s a steal.&lt;/p&gt;
&lt;p&gt;If you are struggling with too much to do, sign up. If you are overwhelmed with your workload, sign up. If you are trying to do it all, sign up. You cannot succeed. You are making yourself crazy.&lt;/p&gt;
&lt;p&gt;We will use agile and lean approaches and help you overcome the guilt, the sunk cost syndrome, and the &amp;#8220;you&amp;#8217;re not playing with the team&amp;#8221; nonsense that other people will pull on you. You&amp;#8217;ll get the support you need from your peers.&lt;/p&gt;
&lt;p&gt;Join us. You won&amp;#8217;t be sorry.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fdsaf7Tfy9g:KH1rcEuDBhs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fdsaf7Tfy9g:KH1rcEuDBhs:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fdsaf7Tfy9g:KH1rcEuDBhs:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Fdsaf7Tfy9g:KH1rcEuDBhs:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fdsaf7Tfy9g:KH1rcEuDBhs:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=Fdsaf7Tfy9g:KH1rcEuDBhs:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fdsaf7Tfy9g:KH1rcEuDBhs:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=Fdsaf7Tfy9g:KH1rcEuDBhs:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/Fdsaf7Tfy9g" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 10 Jan 2012 09:08:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11052</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Management Myth, Myth of 100% Utilitization Posted</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/management_myth_myth_of_100_utilitization_posted</link>
      <description>&lt;p&gt;I have an article posted at Techwell, &lt;a href="http://manage.techwell.com/articles/weekly/management-myth-1-myth-100-utilization" target="_blank"&gt;Management Myth #1: The Myth of 100% Utilization&lt;/a&gt;. This myth has always been a problem. It&amp;#8217;s even more of a problem now as more organizations transition to agile.&lt;/p&gt;
&lt;p&gt;People need time to think. They need time to adapt to their current circumstances. They need time to create their teams. Some of the time, people are thinking about this iteration&amp;#8217;s stories. Some of the time, people are thinking about the product roadmap. Some of the time, people are thinking about the &lt;em&gt;next&lt;/em&gt; iteration&amp;#8217;s stories.&lt;/p&gt;
&lt;p&gt;Thinking is work. Not all work occurs when fingers touch a keyboard. Not all work occurs when mouths are open and moving. Even I have learned to sometimes think with my mouth shut, although that is an infrequent occurrence.&lt;/p&gt;
&lt;p&gt;The more &amp;#8220;utilized&amp;#8221; a person is, the less a person can think. And that means a person is not going to innovate. A person cannot provide the best they can for their organization, which is not why you hired that person.&lt;/p&gt;
&lt;p&gt;If you are one of those people who is working with a manager who still thinks in terms of 100% utilization, consider my &lt;a href="http://www.jrothman.com/services/peer-project-portfolio-coaching/" target="_blank"&gt;peer project portfolio coaching&lt;/a&gt;. If you are a manager, who is considering another way, consider &lt;a href="http://www.jrothman.com/books/manage-your-project-portfolio-increase-your-capacity-and-finish-more-projects/" target="_blank"&gt;Manage Your Project Portfolio&lt;/a&gt; and consider my &lt;a href="http://www.jrothman.com/services/peer-project-portfolio-coaching/" target="_blank"&gt;peer project portfolio coaching&lt;/a&gt; with your leadership team.&lt;/p&gt;
&lt;p&gt;In any case, please read the column, and leave comments on the column over at TechWell.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0pk4UUISiVg:ZlHDDD94rlY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0pk4UUISiVg:ZlHDDD94rlY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0pk4UUISiVg:ZlHDDD94rlY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=0pk4UUISiVg:ZlHDDD94rlY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0pk4UUISiVg:ZlHDDD94rlY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=0pk4UUISiVg:ZlHDDD94rlY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0pk4UUISiVg:ZlHDDD94rlY:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=0pk4UUISiVg:ZlHDDD94rlY:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/0pk4UUISiVg" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 03 Jan 2012 10:46:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11041</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Groovy StubFor magic</title>
      <link>http://www.nofluffjuststuff.com/blog/kenneth_kousen/2012/01/groovy_stubfor_magic</link>
      <description>&lt;p&gt;I finished revising the testing chapter in &lt;a href="http://manning.com/kousen"&gt;Making Java Groovy&lt;/a&gt; (the MEAP should be updated this week), but before I leave it entirely, I want to mention a Groovy capability that is both cool and easy to use. Cool isn&amp;#8217;t the right word, actually. I have to say that even after years of working with Groovy, what I&amp;#8217;m about to describe still feels like magic.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the issue: I have a class that uses one of Google&amp;#8217;s web services, and I want to test my class even when I&amp;#8217;m not online. That means I need to mock the dependency, which isn&amp;#8217;t all that hard. The problem is that there&amp;#8217;s no explicit way to get my mock object into my own service. Yet, with Groovy&amp;#8217;s &lt;code&gt;MockFor&lt;/code&gt; and &lt;code&gt;StubFor&lt;/code&gt; classes, I can mock a dependency, even when it&amp;#8217;s instantiated as a local variable inside my class.&lt;/p&gt;
&lt;p&gt;Let me show you the code. I&amp;#8217;ll start with a simple POGO called &lt;code&gt;Stadium&lt;/code&gt;:&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
class Stadium {
    String street
    String city
    String state
    double latitude
    double longitude
    
    String toString() {
        &amp;quot;($street,$city,$state,$latitude,$longitude)&amp;quot;
    }
}
&lt;/pre&gt;&lt;br /&gt;
I use this in my &lt;a href="http://www.kousenit.com/groovybaseball"&gt;Groovy Baseball&lt;/a&gt; application, which accesses MLB box scores online and displays the daily results on a Google Map. The &lt;code&gt;Stadium&lt;/code&gt; class holds location data for an individual baseball stadium. When I use it, I set the street, city, and state and have the service compute latitude and longitude for me.&lt;/p&gt;
&lt;p&gt;My &lt;code&gt;Geocoder&lt;/code&gt; class is based on &lt;a href="http://code.google.com/apis/maps/documentation/geocoding/"&gt;Google&amp;#8217;s restful geocoding service&lt;/a&gt;.&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
class Geocoder {
    String base = 'http://maps.google.com/maps/api/geocode/xml?'
   
    void fillInLatLng(Stadium stadium) {
        String urlEncodedAddress =
                [stadium.street, stadium.city, stadium.state].collect {
                    URLEncoder.encode(it,'UTF-8')
                }.join(',+')
        String url = base + [sensor:false, address:urlEncodedAddress].collect { it }.join('&amp;amp;')
        def response = new XmlSlurper().parse(url)
        String latitude = response.result.geometry.location.lat[0] ?: &amp;quot;0.0&amp;quot;
        String longitude = response.result.geometry.location.lng[0] ?: &amp;quot;0.0&amp;quot;
        stadium.latitude = latitude.toDouble()
        stadium.longitude = longitude.toDouble()
    }
}
&lt;/pre&gt;&lt;br /&gt;
First I take the stadium&amp;#8217;s street, city, and state and add them to a list. Then the &lt;code&gt;collect&lt;/code&gt; method is used to apply a closure to each element of the list, returning the transformed list. The closure runs each value through Java&amp;#8217;s &lt;code&gt;URLEncoder&lt;/code&gt;. In looking at &lt;a href="http://code.google.com/apis/maps/documentation/geocoding/"&gt;the Google geocoder example&lt;/a&gt;, I see that they separate the encoded street from the city and the city from the state using &amp;#8220;&lt;code&gt;,+&lt;/code&gt;&amp;#8220;, so I do the same using the &lt;code&gt;join&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;The Google geocoding service requires a parameter called &lt;code&gt;sensor&lt;/code&gt;, which is true if the request is coming from a GPS-enabled device and false otherwise. In the Groovy map, I set its value to false, and set the value of the &lt;code&gt;address&lt;/code&gt; parameter to the string from the previous line. The &lt;code&gt;collect&lt;/code&gt; method on the map converts each entry to &amp;#8220;&lt;code&gt;key=value&lt;/code&gt;&amp;#8220;, so joining with an ampersand and appending to the base value gives me the complete URL for the stadium.&lt;/p&gt;
&lt;p&gt;Most restful web services try to provide their data in a format requested by the user. The content negotiation is usually done through an &amp;#8220;Accept&amp;#8221; header in the HTTP request, but in this case Google does something different. They support only XML and JSON output data, and let the user select which one they want through separate URLs. The base URL in the service above ends in &lt;code&gt;xml&lt;/code&gt;. Google lists the JSON version as preferred, but that&amp;#8217;s no doubt because they expect the requests to come through their own JavaScript API. I&amp;#8217;m making the request using Groovy, and the &lt;code&gt;XmlSlurper&lt;/code&gt; class makes parsing the result trivial.&lt;/p&gt;
&lt;p&gt;(Since Groovy 1.8, the &lt;code&gt;JsonSlurper&lt;/code&gt; class also makes parsing JSON trivial, and I have a version that does that, too, but the difference really isn&amp;#8217;t significant here.)&lt;/p&gt;
&lt;p&gt;The resulting block of XML that is returned by the service is fairly elaborate, but as you can see from my code, the data is nested through the elements &lt;code&gt;GeocodeResponse/result/geometry/location/lat&lt;/code&gt; and &lt;code&gt;GeocodeResponse/result/geometry/location/lng&lt;/code&gt;. After invoking the &lt;code&gt;parse&lt;/code&gt; method (which returns the root element, &lt;code&gt;GeocodeResponse&lt;/code&gt;), I just walk the tree to get the values I want.&lt;/p&gt;
&lt;p&gt;I do have to protect myself somewhat. The Google service isn&amp;#8217;t nearly as deterministic as I would have expected. Sometimes I get multiple locations when I search, so I added the zero index to make sure I always get the first one. Also, some time during the last year Google decided to start throttling their service. I have a script that uses the Geocoder for all 30 MLB stadiums, and unfortunately it runs too fast (!) for the Google limits. I know this because I start getting back null results if I don&amp;#8217;t artificially introduce a delay. That&amp;#8217;s why I put in the Elvis operator with the value 0.0 if I don&amp;#8217;t get a good answer.&lt;/p&gt;
&lt;p&gt;So much for the service; now I need a test. If I know I&amp;#8217;m going to be online, I can write a simple integration test as follows:&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
import static org.junit.Assert.*;
import org.junit.Test;

class GeocoderIntegrationTest {
    Geocoder geocoder = new Geocoder()
    
    @Test
    public void testFillInLatLng() {
        Stadium google = new Stadium(street:'1600 Ampitheatre Parkway',
            city:'Mountain View',state:'CA')
        geocoder.fillInLatLng(google)
        assertEquals(37.422, google.latitude, 0.01)
        assertEquals(-122.083, google.longitude, 0.01)
    }
}
&lt;/pre&gt;&lt;br /&gt;
I&amp;#8217;m using Google headquarters, because that&amp;#8217;s the example in the documentation. I invoke my Geocoder&amp;#8217;s &lt;code&gt;fillInLatLng&lt;/code&gt; method and check the results within a hundredth of a degree.&lt;/p&gt;
&lt;p&gt;(The fact that the Google geocoder returns answers to seven decimal places is evidence that their developers have a sense of humor.)&lt;/p&gt;
&lt;p&gt;Now, finally, after all that introduction, I reach the subject of this post. What happens if I&amp;#8217;m not online? More to the point, how do I test the business logic in the &lt;code&gt;fillInLatLng&lt;/code&gt; method without requiring access to Google?&lt;/p&gt;
&lt;p&gt;What I need is a mock object, or, more precisely, a stub.&lt;/p&gt;
&lt;p&gt;(A stub stands in for the external dependency, called a collaborator. A mock does the same, but also validates that the caller accesses the collaborator&amp;#8217;s methods the right number of times in the right order. A stub helps test the caller, and a mock tests the interaction of the caller with the collaborator, sometimes called the protocol. Insert obligatory link to Martin Fowler&amp;#8217;s &amp;#8220;&lt;a href="http://martinfowler.com/articles/mocksArentStubs.html"&gt;Mocks Aren&amp;#8217;t Stubs&lt;/a&gt;&amp;#8221; post here.)&lt;/p&gt;
&lt;p&gt;In my &lt;code&gt;Geocoder&lt;/code&gt; class, the Google service is accessed through the &lt;code&gt;parse&lt;/code&gt; method of the &lt;code&gt;XmlSlurper&lt;/code&gt;. Even worse (from a testing point of view), the slurper is instantiated as a local variable inside my &lt;code&gt;fillInLatLng&lt;/code&gt; method. There&amp;#8217;s no way to isolate the dependency and set it from outside, either as an argument to the method, a setter in the class, a constructor argument, or whatever.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s where Groovy&amp;#8217;s &lt;code&gt;StubFor&lt;/code&gt; (or &lt;code&gt;MockFor&lt;/code&gt;) class comes in. Let me show it in action. First, I&amp;#8217;ll set up the answer I want.&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
String xml = '''
    &amp;lt;root&amp;gt;&amp;lt;result&amp;gt;&amp;lt;geometry&amp;gt;
        &amp;lt;location&amp;gt;
            &amp;lt;lat&amp;gt;37.422&amp;lt;/lat&amp;gt;
            &amp;lt;lng&amp;gt;-122.083&amp;lt;/lng&amp;gt;
        &amp;lt;/location&amp;gt;
    &amp;lt;/geometry&amp;gt;&amp;lt;/result&amp;gt;&amp;lt;/root&amp;gt;'''
    
def correctRoot = new XmlSlurper().parseText(xml)
&lt;/pre&gt;&lt;br /&gt;
The xml variable has the right answer in it, nested appropriately. I use the &lt;code&gt;XmlSlurper&lt;/code&gt; to parse the text and return the root of the tree I want.&lt;/p&gt;
&lt;p&gt;Now I need a &lt;code&gt;Stadium&lt;/code&gt; to update. I deliberately put in the wrong street, city, and state to make sure that I only get the right latitude and longitude if I insert the stub correctly.&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
Stadium wrongStadium = new Stadium(
    street:'1313 Mockingbird Lane',
    city:'Mockingbird Heights',state:'CA')
&lt;/pre&gt;&lt;br /&gt;
(Yes, that&amp;#8217;s a pretty obscure reference. For details, see &lt;a href="http://en.wikipedia.org/wiki/Munsters"&gt;here&lt;/a&gt;. And yes, I&amp;#8217;m old. If you&amp;#8217;re in the right age group, though, you now have the show&amp;#8217;s theme song going through your head. Sorry.)&lt;/p&gt;
&lt;p&gt;The next step is to create the stub and set the expectations.&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
def stub = new StubFor(XmlSlurper)
stub.demand.parse { correctRoot }
&lt;/pre&gt;&lt;br /&gt;
The &lt;code&gt;StubFor&lt;/code&gt; constructor takes a class as an argument and builds a stub around it. Then I use the &lt;code&gt;demand&lt;/code&gt; property to say that when the &lt;code&gt;parse&lt;/code&gt; method is called, my previously computed root is returned rather than actually going over the web and parsing anything.&lt;/p&gt;
&lt;p&gt;To put the stub into play, invoke its &lt;code&gt;use&lt;/code&gt; method, which takes a closure:&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
stub.use {
    geocoder.fillInLatLng(wrongStadium)
}
&lt;/pre&gt;&lt;br /&gt;
That&amp;#8217;s all there is to it. By the magic of Groovy metaprogramming, when I invoke the &lt;code&gt;parse&lt;/code&gt; method of the &lt;code&gt;XmlSlurper&lt;/code&gt; &amp;#8212; &lt;em&gt;even when it&amp;#8217;s instantiated as a local variable&lt;/em&gt; &amp;#8212; inside the &lt;code&gt;use&lt;/code&gt; block the stub steps in and returns the expected value.&lt;/p&gt;
&lt;p&gt;For completeness, here&amp;#8217;s the complete test class:&lt;br /&gt;
&lt;pre class="brush: groovy;"&gt;
import static org.junit.Assert.*
import groovy.mock.interceptor.StubFor
import org.junit.Test

class GeocoderUnitTest {
    Geocoder geocoder = new Geocoder()
    
    @Test
    public void testFillInLatLng() {
        Stadium wrongStadium = new Stadium(
            street:'1313 Mockingbird Lane',
            city:'Mockingbird Heights',state:'CA')
        
        String xml = '''
        &amp;lt;root&amp;gt;&amp;lt;result&amp;gt;&amp;lt;geometry&amp;gt;
            &amp;lt;location&amp;gt;
                &amp;lt;lat&amp;gt;37.422&amp;lt;/lat&amp;gt;
                &amp;lt;lng&amp;gt;-122.083&amp;lt;/lng&amp;gt;
            &amp;lt;/location&amp;gt;
        &amp;lt;/geometry&amp;gt;&amp;lt;/result&amp;gt;&amp;lt;/root&amp;gt;'''
        
        def correctRoot = new XmlSlurper().parseText(xml)
        
        def stub = new StubFor(XmlSlurper)
        stub.demand.parse { correctRoot }
        
        stub.use {
            geocoder.fillInLatLng(wrongStadium)
        }
        assertEquals(37.422, wrongStadium.latitude, 0.01)
        assertEquals(-122.083, wrongStadium.longitude, 0.01)
    }
}
&lt;/pre&gt;&lt;br /&gt;
Since I&amp;#8217;m only calling one method and only calling that method once, a stub is perfectly fine in this case. I could invoke the &lt;code&gt;verify&lt;/code&gt; method if I wanted to (&lt;code&gt;MockFor&lt;/code&gt; invokes &lt;code&gt;verify&lt;/code&gt; automatically but &lt;code&gt;StubFor&lt;/code&gt; doesn&amp;#8217;t), but it doesn&amp;#8217;t really add anything here.&lt;/p&gt;
&lt;p&gt;There is one limitation to this technique, which only comes up when you&amp;#8217;re doing the sort of Groovy/Java integration that is the subject of my book. You can only use &lt;code&gt;StubFor&lt;/code&gt; or &lt;code&gt;MockFor&lt;/code&gt; on a class written in Groovy.&lt;/p&gt;
&lt;p&gt;All this code is available in the book&amp;#8217;s Github repository at &lt;a href="http://github.com/kousen/Making-Java-Groovy"&gt;http://github.com/kousen/Making-Java-Groovy&lt;/a&gt;. This particular example is part of Chapter 5: Testing Java and Groovy.&lt;/p&gt;
&lt;p&gt;As a final note, I should say that I dug into all of this, worked out all the details, and tried it in several examples. Then I finally did what I should have done all along, which was look it up in &lt;a href="http://manning.com/koenig2"&gt;Groovy In Action&lt;/a&gt;. Of course, there it was laid out in detail over about five pages. Someday that will stop happening to me. &lt;img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /&gt; &lt;/p&gt;
&lt;br /&gt;  &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kousenit.wordpress.com/331/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kousenit.wordpress.com/331/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kousenit.wordpress.com/331/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kousenit.wordpress.com/331/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/kousenit.wordpress.com/331/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/kousenit.wordpress.com/331/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/kousenit.wordpress.com/331/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/kousenit.wordpress.com/331/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kousenit.wordpress.com/331/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kousenit.wordpress.com/331/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kousenit.wordpress.com/331/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kousenit.wordpress.com/331/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kousenit.wordpress.com/331/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kousenit.wordpress.com/331/" /&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kousenit.wordpress.com&amp;amp;blog=186706&amp;amp;post=331&amp;amp;subd=kousenit&amp;amp;ref=&amp;amp;feed=1" width="1" height="1" /&gt;</description>
      <pubDate>Mon, 02 Jan 2012 14:17:00 CST</pubDate>
      <guid isPermaLink="true">http://kousenit.wordpress.com/?p=331</guid>
      <dc:creator>Kenneth Kousen</dc:creator>
    </item>
    <item>
      <title>Seam in Action Translations</title>
      <link>http://www.nofluffjuststuff.com/blog/dan_allen/2012/01/seam_in_action_translations</link>
      <description>&lt;p&gt;A long overdue post, I'm still excited to share that &lt;a href="http://mojavelinux.com/seaminaction"&gt;Seam in Action&lt;/a&gt; was translated (in 2010) into Korean and Simplified Chinese--two languages I can't even pretend to understand. It's pretty strange to see words that you spend countless hours revising look complete foreign to you. But exciting at the same time!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://plus.google.com/u/0/photos/114112334290393746697/albums/5693116498840747553" style="border: 0;"&gt;&lt;img src="/assets/pics/seam-in-action-translations-collage.png" style="width: 500px; height: 357px" alt="Seam in Action translations collage"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These books arrived in a box that clearly looked like it came out of a shipping container that was aboard an ocean carrier. My goal is that sometime soon (2012 perhaps?) I'll get to travel to where these texts originated :) (but hopefully on an airplane).&lt;/p&gt;

&lt;p&gt;Btw, I have a whole stack of these books, so if you are in the DC area (or the US for that matter) and you want one of these books, just let me know.&lt;/p&gt;</description>
      <pubDate>Mon, 02 Jan 2012 14:00:00 CST</pubDate>
      <guid isPermaLink="true">122@http://www.mojavelinux.com/</guid>
      <dc:creator>Dan Allen</dc:creator>
    </item>
    <item>
      <title>Announcing Peer Project Portfolio Coaching</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2012/01/announcing_peer_project_portfolio_coaching</link>
      <description>&lt;p&gt;If you missed my most recent Pragmatic Manager newsletter, &lt;a href="http://www.jrothman.com/2012/01/focus-on-one-thing-at-a-time/" target="_blank"&gt;Focus on One Thing at a Time,&lt;/a&gt; it&amp;#8217;s posted. In it, I ranted about the delays of multitasking and introduced a new service: &lt;a href="http://www.jrothman.com/services/peer-project-portfolio-coaching/" target="_blank"&gt;Peer Project Portfolio Coaching&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I keep seeing people trying to make the transition to agile, still multitasking and not able to say No to all those projects&amp;#8211;at all levels of the organization. Partly, it&amp;#8217;s because they don&amp;#8217;t have the tools, which is why we&amp;#8217;re talking about the project portfolio. Partly, it&amp;#8217;s because they don&amp;#8217;t have the language, which is why we&amp;#8217;re talking about how to say No. And, partly, because they have rules about &lt;em&gt;should&lt;/em&gt; they even say No. Or, they have guilt about saying No. Or, any number of other reasons.&lt;/p&gt;
&lt;p&gt;If you are one of these people who knows you can&amp;#8217;t quite do it all, and are not sure how to organize or say No, sign up now and take advantage of the early bird pricing.&lt;/p&gt;
&lt;p&gt;If you are a leadership team who has trouble with the sunk-cost problem (&amp;#8220;we already spent so much money, surely we&amp;#8217;ll see some return soon&amp;#8221;), talk to me, and we&amp;#8217;ll decide if group coaching is right or if you need your own private coaching.&lt;/p&gt;
&lt;p&gt;Please join us.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=g0GKj1yZg1M:1KfOG_HnOFw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=g0GKj1yZg1M:1KfOG_HnOFw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=g0GKj1yZg1M:1KfOG_HnOFw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=g0GKj1yZg1M:1KfOG_HnOFw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=g0GKj1yZg1M:1KfOG_HnOFw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=g0GKj1yZg1M:1KfOG_HnOFw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=g0GKj1yZg1M:1KfOG_HnOFw:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=g0GKj1yZg1M:1KfOG_HnOFw:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/g0GKj1yZg1M" height="1" width="1"/&gt;</description>
      <pubDate>Mon, 02 Jan 2012 07:56:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11037</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>Adding "Ajax Throbbers" to Zone updates</title>
      <link>http://www.nofluffjuststuff.com/blog/howard_lewis_ship/2011/12/adding_ajax_throbbers_to_zone_updates</link>
      <description>&lt;p&gt;A common desire in Tapestry is for Zone updates to automatically include a throbber (or "spinner") displayed while the Ajax update is in process.  This is, unfortunately, a space where the built-in Tapestry 5.3 Zone functionality is a bit lacking.  Fortunately, it's not too hard to hard it in after the fact.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;This solution involves a JavaScript library, two CSS stylesheet files (one is IE specific), plus the "throbber" image. Typically, you'll bind all of these things together in your application's Layout component.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;First, the JavaScript. We need to intercept links and forms that update a Zone. When such a request starts, we add a &amp;lt;div&amp;gt; to the top of the Zone's client-side element. When the update from the server arrives, the entire content of the Zone's element will be replaced (so we don't have to worry about clearing the &amp;lt;div&amp;gt; explicitly).&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537029.js?file=zone-overlay.js"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;When a form is submitted with Ajax, to update a Zone, Tapestry fires a client-side event on the Form; the &lt;code&gt; Tapestry.FORM_PROCESS_SUBMIT_EVENT&lt;/code&gt; constant provides the event name. The primary handler for this event is the code that actually performs the XmlHTTPRequest and sets up a handlers for the response; the above code adds a second handler that adds the Ajax overlay.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Likewise, when a link is used to update a Zone, there's a second client-side event; again, the primary handler for the event does the actual Ajax work, but the same logic allows the Zone to be decorated with the overlay.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The overlay consists of a &amp;lt;div&amp;gt; that will visually mark the entire zone's content and consume any mouse clicks during the Ajax update.  The CSS associated with the zone-ajax-overlay CSS class sets up a translucent background color and the spinning Ajax throbber.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Next up is the CSS:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537062.js?file=zone-throbber.css"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;This little bit of CSS is doing quite a bit. Firstly, if the Ajax request is very quick, then there will be an annoying flicker; to combat this, we've set up a &lt;a href="https://developer.mozilla.org/en/CSS/CSS_animations"&gt;simple CSS animation&lt;/a&gt; to delay the animation momentarily, long enough that fast requests will just see the new content pop into place. There's probably a bit of room here to tweak the exact timing.  &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Alas, in the current world, we need to do a bit of work to support both Firefox (the -moz prefix) and WebKit (Safari, Chrome, the -webkit prefix).  This is really calling out for a &lt;a href="http://sass-lang.com/"&gt;SASSy solution&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;You'll also see an animated image for the throbber. I used &lt;a href="http://www.ajaxload.info/"&gt;ajaxload.info&lt;/a&gt; to create one.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;But what about Internet Explorer?  It doesn't understand the animation logic, and it does CSS opacity differently from the others. Fortunately, we can segregate those differences in a separate CSS file.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537101.js?file=zone-throbber-ie.css"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Lastly, we put all this together inside the application's Layout component:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537114.js?file=Layout.java"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The &lt;code&gt;@Import&lt;/code&gt; annotation does the easy imports of the main CSS and JavaScript.&lt;br /&gt;
Tapestry 5.3 supports IE conditional stylesheets ... but this requires just a bit of code as the &lt;code&gt;@Import&lt;/code&gt; annotation doesn't support adding a condition, as this is a fairly rare requirement.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Instead, the IE-specific CSS is injected into the page as an Asset object; this can be combined with StylesheetOptions to form a StylesheetLink, which can be imported into the page.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;With this in place, every page will include both CSS stylesheets (one as an IE-only conditional comment) and the necessary client-side logic ... and every Zone update will get this uniform treatment.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;There's some limitations here; in Tapestry it's possible for the server-side to push updates into multiple Zones. The client-side doesn't even know that's happening until it gets the reply, so there's no universal way to add overlays to multiple zones when the request is initiated.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Secondly, in rare cases, a Zone update may &lt;em&gt;only&lt;/em&gt; update other Zones, and leave the initiating Zone's content unchanged. In that case, you may find that the Zone's throbber is still in place after the response is handled!  I'll leave it as an exercise to the reader on how to deal with that.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-8082440333108958553?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/cYkGpxaTh0g" height="1" width="1"/&gt;</description>
      <pubDate>Thu, 29 Dec 2011 19:40:09 CST</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-4110180.post-8082440333108958553</guid>
      <dc:creator>Howard Lewis Ship</dc:creator>
    </item>
    <item>
      <title>Pragmatic Manager Posted &amp; Update Site</title>
      <link>http://www.nofluffjuststuff.com/blog/johanna_rothman/2011/12/pragmatic_manager_posted__update_site</link>
      <description>&lt;p&gt;I have finally posted my most recent email newsletter, &lt;a href="http://www.jrothman.com/2011/12/pragmatic-manager-vol-8-8-three-myths-and-three-tips/" target="_blank"&gt;Three Myths and Three Tips&lt;/a&gt;. It took a while because I was converting my site to WordPress and I did not want to maintain the site in two places.&lt;/p&gt;
&lt;p&gt;I have finally transitioned fully to WordPress. What a relief. All my old articles are finally posted. I have tagged back to somewhere in 2006. I&amp;#8217;ll keep tagging as I get around to it.&lt;/p&gt;
&lt;p&gt;What do you think? I&amp;#8217;d love your feedback.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=75xDE5ODEXQ:POEIHPr_5ac:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=75xDE5ODEXQ:POEIHPr_5ac:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=75xDE5ODEXQ:POEIHPr_5ac:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=75xDE5ODEXQ:POEIHPr_5ac:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=75xDE5ODEXQ:POEIHPr_5ac:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?i=75xDE5ODEXQ:POEIHPr_5ac:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=75xDE5ODEXQ:POEIHPr_5ac:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?a=75xDE5ODEXQ:POEIHPr_5ac:cGdyc7Q-1BI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/ManagingProductDevelopment?d=cGdyc7Q-1BI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/75xDE5ODEXQ" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 28 Dec 2011 11:28:00 CST</pubDate>
      <guid isPermaLink="true">http://www.jrothman.com/blog/mpd/?p=11033</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
  </channel>
</rss>


