<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hey, it&#039;s OPOWER! &#187; Best Practice</title>
	<atom:link href="http://www.heyitsopower.com/category/best-practice/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.heyitsopower.com</link>
	<description>Energy Efficiency Starts at /usr/home</description>
	<lastBuildDate>Tue, 27 Jul 2010 20:14:37 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Boxed primitives and ==</title>
		<link>http://www.heyitsopower.com/culture/boxed-primitives-and/</link>
		<comments>http://www.heyitsopower.com/culture/boxed-primitives-and/#comments</comments>
		<pubDate>Thu, 27 May 2010 18:25:06 +0000</pubDate>
		<dc:creator>Dave Copeland</dc:creator>
				<category><![CDATA[Best Practice]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Culture]]></category>

		<guid isPermaLink="false">http://www.heyitsopower.com/?p=215</guid>
		<description><![CDATA[One part of programming culture at OPOWER is to program defensively, providing useful information when contracts are violated.   What does this have to do with boxed primitives?  Getting an assertion message of &#8220;Person 1001&#8217;s customer id 109032 didn&#8217;t match the passed-in customer&#8217;s id of 109032&#8243; seemed logically impossible, but was somehow true.
But [...]]]></description>
			<content:encoded><![CDATA[<p>One part of programming culture at OPOWER is to program defensively, providing useful information when contracts are violated.   What does this have to do with boxed primitives?  Getting an assertion message of &#8220;Person 1001&#8217;s customer id 109032 didn&#8217;t match the passed-in customer&#8217;s id of 109032&#8243; seemed logically impossible, but was somehow true.</p>
<p>But first, let&#8217;s talk about defensive coding and how we do it.  For example:</p>
<pre name="code" class="java">/**
 * frobs the bar and baz.
 * @param bar the bar to frob; may not be null
 * @param baz the baz to from; must be positive
 */
public String frob(String bar, int baz) {
  if (bar.length() &gt; baz) {
    return bar.substring(baz);
  }
  else {
    return bar;
  }
}</pre>
<p>If the contract is violated by the caller, we will get a <code>NullPointerException</code> or some other <code>StringIndexOutOfBoundsException</code>.  Not too helpful.  Instead, we use an internal validator helper class to provide useful messages.  This is <strong>crucial</strong> for properly understanding bugs that occur in production; there&#8217;s nothing worse than coming into work to find a <code>NullPointerException</code> in your inbox and no clue what went wrong.</p>
<pre name="code" class="java">/**
 * frobs the bar and baz.
 * @param bar the bar to frob; may not be null
 * @param baz the baz to from; must be positive
 */
public String frob(String bar, int baz) {
  Validate.notNull(bar,"bar must not be null to frob");
  Validate.isTrue(baz &gt; 0,"baz must be positive");
  if (bar.length() &gt; baz) {
    return bar.substring(baz);
  }
  else {
    return bar;
  }
}</pre>
<p><code>Validate.*</code>  methods essentially throw <code>IllegalArgumentException</code> if what they are checking for fails.<br />
We use this pattern extensively, even in constructors of simple data-structure style objects.  This (plus keeping this things immutable) allows users of these objects to safely rely upon the <code>get</code> methods behaving properly. I had need of a class to hold both a <code>Person</code> (representing a website user) and a <code>Customer</code> (representing a utility-company customer).  This class (called, naturally, <code>PersonAndCustomer</code>), takes both objects in its constructor and requires that they are both non-null.  As a further sanity check, I wanted to make sure that the <code>Person</code>&#8217;s <code>customerId</code> matched the id of the <code>Customer</code> that was being passed in (i.e. that they represented the same actual human in the real world):</p>
<pre name="code" class="java">public PersonAndCustomer(Person p, Customer c) {
  Validate.notNull(p,"Person may not be null");
  Validate.notNull(c,"Customer may not be null");
  Validate.isTrue(p.getCustomerId() == c.getId(),
    "Person %d's customer id %d didn't match passed-in customer's id %d",
    p.getId(),
    p.getCustomerId(),
    c.getId());
  ...
}</pre>
<p>Looks pretty inocuous, right?  Well, because these objects are Hibernate-managed, all of our ids are <code>Long</code> and not <code>long</code>.  So, my <code>isTrue</code> validation is really checking that the person&#8217;s <code>customerId</code> <em>object</em> is the <em>same object</em> as the customer&#8217;s <code>id</code> object.  What&#8217;s worse, in several months of development and production deployment, these two objects <em>happened to be the same</em>.</p>
<p>Until this week; we were testing our application for a new client and I got the error posted above (&#8220;Person 1001&#8217;s customer id 109032 didn&#8217;t match the passed-in customer&#8217;s id of 109032&#8243;).  It seems that much like how the JVM will re-use string objects, it also will sometimes re-use boxed objects as well.  But not always.  The fix for this is obvious, but I wanted to make sure I could actually recreate this situation.  So, I created a test that gave both the <code>Person</code> and <code>Customer</code> the same <em>value</em> for the customer id, but as different objects. and verified that the class could still be constructed.  Sure enough, the test failed.  The fix:</p>
<pre name="code" class="java">public PersonAndCustomer(Person p, Customer c) {
  Validate.notNull(p,"Person may not be null");
  Validate.notNull(c,"Customer may not be null");
  Validate.isTrue(p.getCustomerId().equals(c.getId()),
    "Person %d's customer id %d didn't match passed-in customer's id %d",
    p.getId(),
    p.getCustomerId(),
    c.getId());
  ...
}</pre>
<p>What&#8217;s interesting is that if one end of the <code>==</code> is a primitive, the JVM will unbox the other one and the test succeeds:</p>
<pre name="code" class="java">Long l1 = new Long(45L);
Long l2 = new Long(45L);

System.out.println(l1 == l2);      // false
System.out.println(l1.equals(l2)); // true
System.out.println(l1 == 45L);     // true!</pre>
<p>The moral of the story is to always know what you are comparing.  Might even be best to always use <code>.equals()</code> unless the compiler complains.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.heyitsopower.com/culture/boxed-primitives-and/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tie Tuesday &#8211; more fun that it sounds</title>
		<link>http://www.heyitsopower.com/culture/tie-tuesday-more-fun-that-it-sounds/</link>
		<comments>http://www.heyitsopower.com/culture/tie-tuesday-more-fun-that-it-sounds/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 16:56:28 +0000</pubDate>
		<dc:creator>Dave Copeland</dc:creator>
				<category><![CDATA[Best Practice]]></category>
		<category><![CDATA[Culture]]></category>

		<guid isPermaLink="false">http://www.heyitsopower.com/?p=183</guid>
		<description><![CDATA[
The developers have an informal &#8220;Tie Tuesday&#8221; each work.  It&#8217;s exactly what it sounds like.  And, since you asked, yes, it is more fun to wear a tie when you don&#8217;t have to.  Not everyone does it every week, but this week, we had the best participation yet:

]]></description>
			<content:encoded><![CDATA[<p>
The developers have an informal &#8220;Tie Tuesday&#8221; each work.  It&#8217;s exactly what it sounds like.  And, since you asked, yes, it is more fun to wear a tie when you don&#8217;t have to.  Not everyone does it every week, but this week, we had the best participation yet:
</p>
<div id="attachment_182" class="wp-caption alignnone" style="width: 535px"><img src="http://www.heyitsopower.com/wordpress/home/35481/domains/heyitsopower.com/html/wordpress/wp-content/uploads/2010/01/our-opower-mascot-on-tie-tuesday.jpeg" alt="Riley Dawg participates in tie tuesday" title="Our Mascot on Tie Tuesday" width="525" height="700" class="size-full wp-image-182" /><p class="wp-caption-text">Riley Dawg participates in tie tuesday</p></div>
]]></content:encoded>
			<wfw:commentRss>http://www.heyitsopower.com/culture/tie-tuesday-more-fun-that-it-sounds/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>When is a null not a null?</title>
		<link>http://www.heyitsopower.com/code/when-is-a-null-not-a-null/</link>
		<comments>http://www.heyitsopower.com/code/when-is-a-null-not-a-null/#comments</comments>
		<pubDate>Thu, 10 Sep 2009 14:48:15 +0000</pubDate>
		<dc:creator>Tom Vaughan</dc:creator>
				<category><![CDATA[Best Practice]]></category>
		<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://www.heyitsopower.com/?p=74</guid>
		<description><![CDATA[tl/dr: <em>Don't have a non-null object's .toString() display "null". It's confusing and not helpful when looking at object states in IDE debuggers.</em>

I recently converted the attribute of our Person POJO that stored a user's email address from a java.lang.String to a javax.mail.InternetAddress.  Call it not enough testing or lack of imagination, but I introduced a Null Pointer Exception in a back-office web flow where a new user is created for our customer service application.  When a new CSR account is created and the email address is left blank, an NPE gets thrown up.  Embarrassing, but easy to fix, right?]]></description>
			<content:encoded><![CDATA[<p>tl/dr: <em>Don&#8217;t have a non-null object&#8217;s .toString() display &#8220;null&#8221;. It&#8217;s confusing and not helpful when looking at object states in IDE debuggers.</em></p>
<p>I recently converted the attribute of our Person POJO that stored a user&#8217;s email address from a java.lang.String to a javax.mail.InternetAddress.  Call it not enough testing or lack of imagination, but I introduced a Null Pointer Exception in a back-office web flow where a new user is created for our customer service application.  When a new CSR account is created and the email address is left blank, an NPE gets thrown up.  Embarrassing, but easy to fix, right?</p>
<p>Just looking at the stack trace narrows it down immediately:</p>
<pre class="java">2009-09-09 16:56:53,636 WARN  [btpool0-1] [REPORT] [WARN] Handler execution resulted in exception
java.lang.NullPointerException
	at javax.mail.internet.InternetAddress.parse(InternetAddress.java:609)
	at javax.mail.internet.InternetAddress.parse(InternetAddress.java:569)
	at javax.mail.internet.InternetAddress.(InternetAddress.java:105)
	at poscore.db.InternetAddressUserType.deepCopy(InternetAddressUserType.java:93)
	at org.hibernate.type.CustomType.deepCopy(CustomType.java:179)
	at org.hibernate.type.TypeFactory.deepCopy(TypeFactory.java:374)</pre>
<p>Ahh&#8230;yeah&#8230;that custom hibernate type I wrote to translate VARCHARs to InternetAddresses and vice versa.  Ok, what does that deep copy method look like?</p>
<pre class="java">    public Object deepCopy(Object value) throws HibernateException {
        if(value == null) {
            return null;
        }
        InternetAddress original = (InternetAddress)value;
        InternetAddress copy = null;
        try {
            copy = new InternetAddress(original.getAddress());
        }
        catch (AddressException ex) {
            throw new HibernateException("Unable to deep copy email address '" + original.getAddress() + "'");
        }
        return copy;
    }</pre>
<p>It turns out, the error is right here:</p>
<pre class="java">            copy = new InternetAddress(original.getAddress());</pre>
<p>You can&#8217;t pass a null to the InternetAddress constructor.  It was late in the day and I must not have internalized that there&#8217;s no way <em>value </em>or <em>original</em> could be null, because my first instinct was that the NPE was actually the result of calling &#8220;.getAddress()&#8221; on a null <em>original</em> object&#8230;.not that the constructor can&#8217;t take a null.</p>
<p>While I was on that assumption (that it was the <em>original</em> object that was null), I fired up my debugger and got confused by what appeared to be null-checks failing to check nulls. Check out this screen shot of the NPE about to be thrown after what appears to be 2 null checks failing to prevent the NPE:<br />
<img class="aligncenter size-full wp-image-77" title="null_internet_address" src="http://www.heyitsopower.com/wordpress/wp-content/uploads/2009/09/null_internet_address.png" alt="null_internet_address" width="705" height="606" /></p>
<p>The arrows marked (A) and (B) shows what appear to be null objects passing null checks (in the top pane) even though they&#8217;re being reported as null (in the bottom pane).  The green line (C) shows the code falling through to that line just before it throws up the NPE.</p>
<p>Of course, <em>value</em> and <em>original</em> aren&#8217;t null at all&#8230;it&#8217;s just that their .toString() methods report them to be null.  Here&#8217;s the InternetAddress.toString method:</p>
<pre class="java">  276       public String toString() {
  277   	if (encodedPersonal == null &amp;&amp; personal != null)
  278   	    try {
  279   		encodedPersonal = MimeUtility.encodeWord(personal);
  280   	    } catch (UnsupportedEncodingException ex) { }
  281
  282   	if (encodedPersonal != null)
  283   	    return quotePhrase(encodedPersonal) + " &lt;" + address + "&gt;";
  284   	else if (isGroup() || isSimple())
  285   	    return address;
  286   	else
  287   	    return "&lt;" + address + "&gt;";
  288       }</pre>
<p>In my situation, I fell in to the &#8220;else if(isGroup() || isSimple())&#8221; case, which returns <em>address</em>, which is null, so the .toString() for the whole InternetAddress object is &#8220;null.&#8221;</p>
<p>I think the toString() method could benefit from another null-check right at line 284, which would mean an InternetAddress object with a null internal <em>address</em> String would display as &#8220;&lt;null&gt;&#8221;&#8230;that&#8217;s a more immediate clue that the object itself isn&#8217;t null.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.heyitsopower.com/code/when-is-a-null-not-a-null/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Move meetings to move developers</title>
		<link>http://www.heyitsopower.com/development/move-meetings-to-move-developers/</link>
		<comments>http://www.heyitsopower.com/development/move-meetings-to-move-developers/#comments</comments>
		<pubDate>Fri, 28 Aug 2009 15:08:35 +0000</pubDate>
		<dc:creator>Tom Vaughan</dc:creator>
				<category><![CDATA[Best Practice]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[concentration]]></category>
		<category><![CDATA[meetings]]></category>
		<category><![CDATA[procrastination]]></category>

		<guid isPermaLink="false">http://www.heyitsopower.com/?p=55</guid>
		<description><![CDATA[Here's the scenario:

You cruise in to the office at 8:45, get your coffee, check the reddits, and ignore that email from your parents asking if their computer picked up a virus because McAfee won't stop popping up a tooltray icon helpfully informing them that they're totally vulnerable.  Right around 9:15, you get motivated to tackle that 2 point user story for the current iteration.

You head over to the wiki, check out the specs for the requirement, find out what the Trac ticket number is and see if there are any comments with last minute advice or dependencies.  None?  Good. . . let's get started!]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s the scenario:</p>
<p>You cruise in to the office at 8:45, get your coffee, check the reddits, and ignore that email from your parents asking if their computer picked up a virus because McAfee won&#8217;t stop popping up a tooltray icon helpfully informing them that they&#8217;re totally vulnerable.  Right around 9:15, you get motivated to tackle that 2 point user story for the current iteration.</p>
<p>You head over to the wiki, check out the specs for the requirement, find out what the Trac ticket number is and see if there are any comments with last minute advice or dependencies.  None?  Good. . . let&#8217;s get started!</p>
<p>Oh, huh. . . it&#8217;s 9:40.  There&#8217;s that all-hands meeting at 10 o&#8217;clock.  I <em>could</em> get started, but I&#8217;m not really going to get anything done in 20 minutes really&#8230;.and that link to the <a href="http://www.buzzfeed.com/akdobbins/three-keyboard-cat-moon-shirt">keyboard-cat-three-wolf-t-shirt</a> <em>did</em> look interesting.</p>
<p>The 10 o&#8217;clock meeting runs a little long, and then there&#8217;s the lunch train, and suddenly it&#8217;s the afternoon and you haven&#8217;t been in The Zone once.</p>
<p><strong>The Zone</strong></p>
<p>We&#8217;ve all been in The Zone &#8211; that magical place where time appears to stop and you rock out an insane amount of code in what seems like 10 minutes, but when you look up from your keyboard the sun is down and the cleaning crew is giving you a weird look because you didn&#8217;t realize you were trying to sing along to Sigur Ros.</p>
<p>Trying to force yourself into The Zone is like trying to force yourself to see through those <a href="http://cdn-write.demandstudios.com/upload//0000/000/90/4/94.gif">stereograms</a> (hint: it&#8217;s always a Schooner).  And as hard as it is to get <em>in</em> The Zone, it&#8217;s annoying easily to get <em>out</em> of The Zone.</p>
<p>There are a lot of recommendations out there to help create &#8220;zone-friendly&#8221; work places including:</p>
<ul>
<li>separate offices</li>
<li>ambient music</li>
<li>leaving something intentionally broken the night before to give you that excuse to dive in the next morning</li>
<li><a href="http://cdn-write.demandstudios.com/upload//0000/000/90/4/94.gif">shift your tasks to the upper right</a></li>
<li><a href="http://www.paulgraham.com/makersschedule.html">Paul Graham&#8217;s thoughts</a> on the matter</li>
<li>etc. etc.</li>
</ul>
<p>We were wrapping up our all-hands meeting yesterday when the ever-perceptive Dave noted that our weekly meeting schedules were really not all that Zone-Friendly.  With a couple of exceptions, we had standing meetings along the following schedule:<br />
<img src="http://www.heyitsopower.com/wordpress/wp-content/uploads/2009/08/zone_unfriendly.png" alt="Standing Meetings" /></p>
<p>And keep in mind, that&#8217;s just the standing meetings, so it&#8217;s the bare-minimum one would expect.  Now let&#8217;s add some &#8220;procrastination shading&#8221; to that calendar to demonstrate the actual standing meeting impact:<br />
<img src="http://www.heyitsopower.com/wordpress/wp-content/uploads/2009/08/zone_unfriendly_shaded.png" alt="Standing Meetings" /></p>
<p>If we shuffled some meetings around (especially around the meat of the middle of the week), I think we could end up &#8220;finding&#8221; 2 or 3 hours a week more conducive to The Zone:<br />
<img src="http://www.heyitsopower.com/wordpress/wp-content/uploads/2009/08/zone_friendly_shaded.png" alt="Friendly Standing Meetings" /></p>
<p>Of course, this is an idealized example, but it&#8217;s something we&#8217;re thinking about and playing with.</p>
<p>What do you guys out there in development land do?  Any recommendations of particular effectiveness?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.heyitsopower.com/development/move-meetings-to-move-developers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Developer Law</title>
		<link>http://www.heyitsopower.com/development/new-developer-law/</link>
		<comments>http://www.heyitsopower.com/development/new-developer-law/#comments</comments>
		<pubDate>Wed, 26 Aug 2009 14:06:25 +0000</pubDate>
		<dc:creator>Tom Vaughan</dc:creator>
				<category><![CDATA[Best Practice]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.heyitsopower.com/?p=34</guid>
		<description><![CDATA[NEW LAW FOR DEVELOPERS:

If you throw an exception about some connection being refused, you'd better damn well put what connection you were attempting to make in the exception message.

What inspired this law?  Thanks for asking.]]></description>
			<content:encoded><![CDATA[<p>NEW LAW FOR DEVELOPERS:</p>
<p><em>If you throw an exception about some connection being refused, you&#8217;d better damn well put what connection you were attempting to make in the exception message.</em></p>
<p>What inspired this law?  Thanks for asking.</p>
<p>Check out this snippet in the middle of an 80 line stack dump:</p>
<pre>2009-08-25 17:23:42.373::WARN:  Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'coreAutopatcher' defined in class path resource [config/migrationContext.xml]: Invocation of init method failed; nested exception is com.tacitknowledge.util.migration.MigrationException: Error applying patches:
com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source.
at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1319)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:557)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:477)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525)</pre>
<p>&#8220;A ResourcePool could not acquire a resource from its primary factory or source&#8221;.  Awesome.  Sooo helpful.  /wristsemoragegrowl</p>
<p>Also, a WARN?  Seriously?  You&#8217;re a connection pool.  Do you think not connecting to your underlying datasource warrants maybe something as stern as an ERROR?  Or, heaven forbid, a FATAL?</p>
<p>I&#8217;m sure I could have turned up logging somewhere, but I noticed Spring&#8217;s DelegatingDataSource bean standing out from among the stack lines and set a break point on its called method to figure out exactly which of my many connections was unable to be resolved:</p>
<p><img class="aligncenter size-full wp-image-37" title="what_am_i_connecting_to" src="http://www.heyitsopower.com/wordpress/wp-content/uploads/2009/08/what_am_i_connecting_to.png" alt="what_am_i_connecting_to" width="909" height="307" /></p>
<p>There&#8217;s the culprit!  localhost:5455.  You know, whoever the idiot is who runs that localhost server needs to get his act together.  I should call the guys from YouTube and have them do a Maury Povich-style video expose on why he can&#8217;t keep any of his connections stable.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.heyitsopower.com/development/new-developer-law/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Subversion Branching &#8220;Good&#8221; Practice</title>
		<link>http://www.heyitsopower.com/development/subversion-branching-good-practice/</link>
		<comments>http://www.heyitsopower.com/development/subversion-branching-good-practice/#comments</comments>
		<pubDate>Mon, 24 Aug 2009 00:56:52 +0000</pubDate>
		<dc:creator>Jeff Kolesky</dc:creator>
				<category><![CDATA[Best Practice]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[branching]]></category>
		<category><![CDATA[subversion]]></category>

		<guid isPermaLink="false">http://www.heyitsopower.com/?p=18</guid>
		<description><![CDATA[I am relatively new to Subversion — well, two years into using it now, but this is the first project I have used it on.  Subversion, like any tool, has its quirks and works best when you really know how to use it.  When I started, I treated the "trunk/branches/tags" directory structure exactly like a directory structure.  It took me a little playing around until I stumbled on what I would call a best practice.  When I checkout a new project, I do it like so:

svn co http://domain.com/svnroot/project/trunk project

That way, when I go to the project directory, I do not have to then go into the trunk directory to get to my files.]]></description>
			<content:encoded><![CDATA[<p>I am relatively new to Subversion — well, two years into using it now, but this is the first project I have used it on.  Subversion, like any tool, has its quirks and works best when you really know how to use it.  When I started, I treated the &#8220;trunk/branches/tags&#8221; directory structure exactly like a directory structure.  It took me a little playing around until I stumbled on what I would call a best practice.  When I checkout a new project, I do it like so:</p>
<p><code>svn co http://domain.com/svnroot/project/trunk project</code></p>
<p>That way, when I go to the <code>project</code> directory, I do not have to then go into the <code>trunk</code> directory to get to my files.  Saving an extra directory level is not the best benefit though.  When I set up my IDE (yes, I use an IDE), I put the IDE files in that one directory and do not have to recreate them for each branch or tag that I might check out.  I essentially treat the &#8220;trunk/branches/tags&#8221; part of the directory as pure metadata, which makes perfect sense to me.</p>
<p>When I switched over to this system, there was one big drawback.  I could not easily tell where I am just by looking at my prompt.  I had to run <code>svn info | grep URL</code> to see if I was on a branch.  So instead of letting my prompt handicap me, I decided to get smart about my prompt.  Using bash, my prompt used to look like this:</p>
<p><code>jeff:/opt/pose/main/report/src/main/java</code></p>
<p>but now it looks like this (supercharged with Subversion metadata):</p>
<p><code>jeff:/opt/pose/main/report/src/main/java [SVN: /main/report/trunk]</code></p>
<p>I&#8217;m no bash expert, so I dug around on the web and learned a bit about how <code>PROMPT_COMMAND</code> works.  Here is what I have in my <code>.bash_profile</code> now:</p>
<p><code><br />
function spwd ()<br />
{<br />
    stat .svn > /dev/null 2>&#038;1;<br />
    if [ "$?" == "0" ]; then<br />
        SURL=`svn info | grep URL | perl -pe 's/URL: (.*)/\1/'`;<br />
        if [ `echo ${SURL} | grep -E "branches|tags"` ]; then<br />
            SVER=`echo ${SURL} | perl -pe 's{.*/(branches|tags)/(.*)}{\1/\2}' | cut -d/ -f1-2`;<br />
            SPTH=`echo ${SURL} | perl -pe 's{.*svnroot/(.*)/(branches|tags)/.*}{/\1}'`;<br />
            SPWD="${SPTH}/${SVER}";<br />
        else<br />
            SPWD=`echo ${SURL} | perl -pe 's{.*svnroot/(.*)/trunk(.*)}{/\1/trunk}'`;<br />
        fi;<br />
        export PS1="\u:\w [SVN: $SPWD]\n$ ";<br />
    else<br />
        export PS1="\u:\w $ ";<br />
    fi<br />
}<br />
export PROMPT_COMMAND=spwd<br />
</code></p>
<p>It may not be the most efficient way to do it, but it sure works well and lets me know in an instant what trunk, branch or tag I happen to be working on at that very moment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.heyitsopower.com/development/subversion-branching-good-practice/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
