<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>bradt.ca</title>
	
	<link>http://bradt.ca</link>
	<description>Independent web developer, web designer, and entrepreneur living in Halifax, Canada.</description>
	<lastBuildDate>Fri, 24 May 2013 02:36:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/bradtca/posts" /><feedburner:info uri="bradtca/posts" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Stop Avoiding Regular Expressions Damn It</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/I_VcQHA0JSg/</link>
		<comments>http://bradt.ca/blog/stop-avoiding-regular-expressions-damn-it/#comments</comments>
		<pubDate>Sun, 12 May 2013 12:42:43 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[web-development]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=15821</guid>
		<description><![CDATA[<p>If you develop in Perl, PHP, Python, Ruby, Javascript (or pretty much any other language with its roots in Unix) and don&#8217;t know regular expressions, you are missing a critical piece. And if you&#8217;re intentionally avoiding regular expressions, it&#8217;s like you&#8217;ve torn pages out of YOUR manual, but everyone else&#8217;s manual is complete. Regular expressions [...]</p><p>The post <a href="http://bradt.ca/blog/stop-avoiding-regular-expressions-damn-it/">Stop Avoiding Regular Expressions Damn&nbsp;It</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>If you develop in Perl, PHP, Python, Ruby, Javascript (or pretty much any other language with its roots in Unix) and don&#8217;t know regular expressions, you are missing a critical piece. And if you&#8217;re intentionally avoiding regular expressions, it&#8217;s like you&#8217;ve torn pages out of YOUR manual, but everyone else&#8217;s manual is complete.<span id="more-15821"></span></p>
<p><a title="Wait, forgot to escape a space.  Wheeeeee[taptaptap]eeeeee." href="https://xkcd.com/208/"><img class="size-full wp-image-15822 alignnone" alt="XKCD: Regular Expressions" src="http://uploads.bradt.ca/content/uploads/2013/05/regular_expressions.png" width="600" height="607" /></a></p>
<p>Regular expressions are powerful.</p>
<p>I&#8217;m sure you&#8217;ve heard this claim before. But you&#8217;ve also heard lots of negativity against regular expressions on Stack Overflow and elsewhere. There are two main reasons for this:</p>
<ol>
<li><span style="line-height: 18px;">Inappropriate use of regular expressions</span></li>
<li>Lack of understanding and fear of regular expressions</li>
</ol>
<p>Probably the most common example of an inappropriate use of a single regular expression is to <a href="http://stackoverflow.com/a/201378/112832">validate an email address</a>. It turns out that most of us have been disallowing valid email addresses with regular expressions all along and that <a href="http://www.linuxjournal.com/article/9585">some code</a> is required to properly validate an email address. And even then it&#8217;s <a href="http://davidcel.is/blog/2012/09/06/stop-validating-email-addresses-with-regex/">probably wrong</a>.</p>
<p>Using a regular expression when a simple string function could be used is another example of inappropriate use. For example, say you want to validate an email address by just checking for the @ symbol (which is what I do now &#8212; <a href="http://davidcel.is/blog/2012/09/06/stop-validating-email-addresses-with-regex/">see this</a>). Using a regular expression <code>preg_match( '/@/', $email )</code> is overkill. There are no expressions there. We&#8217;re just searching for a character. It&#8217;s better practice to use <code>strpos( $email, '@' )</code> instead.</p>
<p>I say &#8220;better practice&#8221; rather than &#8220;better performance&#8221; because in this example where we&#8217;re operating on a tiny email string. The performance is insignificant. In this case, it doesn&#8217;t really matter which we use. However, if we were searching a very large string instead of an email address and doing it several times within a loop, there could be a significant improvement in performance. Maybe we&#8217;re searching several strings in HTML page sources for example.</p>
<p>If you&#8217;re just matching a string constant, don&#8217;t use regular expressions.</p>
<p>Unfortunately some developers have misunderstood this to mean that regular expressions are terrible on performance and that they should go out of their way to avoid regular expressions. Some have even adopted it as a reason not to learn them. This is dead wrong.</p>
<p>Let&#8217;s add a couple of little requirements to our example. Let&#8217;s say we want to check if the @ symbol is the first character in the string and if it is, replace it with a # symbol. Maybe we&#8217;re converting tweet replies to hash tags for some reason. Using string manipulations might look something like this:</p>
<pre>if ( 0 === strpos( $string, '@' ) ) {
    $string = '#' . substr( $string, 1 );
}</pre>
<p>But this same thing can be achieved with a regular expression more concisely:</p>
<pre>$string = preg_replace( '/^@/', '#', $string );</pre>
<p>If you haven&#8217;t learned regular expressions, you probably feel that this is less readable, but realize that this is more readable to those who have a grip on regular expressions.</p>
<p>If we add another requirement to our example: that the last character also has to be an @, and only then do we replace both @ with a #. Then our string manipulation code gets more complex and less readable:</p>
<pre>$length = strlen( $string );
if ( 0 === strpos( $string, '@' ) &amp;&amp; $length - 1 === strrpos( $string, '@' ) ) {
    $string = '#' . substr( $string, 1, $length - 1 ) . '#';
}</pre>
<p>Our regular expression changes only slightly:</p>
<pre>$string = preg_replace( '/^@(.*)@$/', '#$1#', $string );</pre>
<p>These are trivial examples, but hopefully they illustrate how the code can take a turn for the worst if you insist on sticking with string manipulation instead of taking up the challenge of writing a regular expression.</p>
<p>So, the next time you find yourself going out of your way to avoid using a regular expression, just Google for the solution, learn how it was done, tweak it, play with it, break it, struggle with it, and repeat. This is the best way to learn.</p>
<p>The post <a href="http://bradt.ca/blog/stop-avoiding-regular-expressions-damn-it/">Stop Avoiding Regular Expressions Damn&nbsp;It</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/stop-avoiding-regular-expressions-damn-it/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/stop-avoiding-regular-expressions-damn-it/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=stop-avoiding-regular-expressions-damn-it</feedburner:origLink></item>
		<item>
		<title>Why Firefox DevTools?</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/gpeMjhndUrY/</link>
		<comments>http://bradt.ca/blog/why-firefox-devtools/#comments</comments>
		<pubDate>Tue, 19 Mar 2013 13:46:25 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Settings]]></category>
		<category><![CDATA[User Experience]]></category>
		<category><![CDATA[web browser]]></category>
		<category><![CDATA[web-development]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=15532</guid>
		<description><![CDATA[<p>Alex Limi (from Mozilla) published an excellent post yesterday assessing the current settings control panel in Firefox. Many of the options are unused by 99% of users and some even break the browsing experience. Alex argues that if these settings are not for the vast majority of users, they don&#8217;t belong in the control panel. [...]</p><p>The post <a href="http://bradt.ca/blog/why-firefox-devtools/">Why Firefox&nbsp;DevTools?</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>Alex Limi (from Mozilla) published <a title="Checkboxes that kill your product" href="http://limi.net/checkboxes-that-kill">an excellent post</a> yesterday assessing the current settings control panel in Firefox. Many of the options are unused by 99% of users and some even break the browsing experience.</p>
<p>Alex argues that if these settings are not for the vast majority of users, they don&#8217;t belong in the control panel.</p>
<blockquote><p>The people that need to do these things should use add-ons, or at the very least an <code>about:config</code> tweak.</p></blockquote>
<p>I completely agree.</p>
<p>And so I found it ironic that Paul Rouget (also from Mozilla) had also posted yesterday about <a href="http://paulrouget.com/e/devtoolsnext/">the future of Firefox DevTools</a>.</p>
<p><span id="more-15532"></span>DevTools is a set of features built into Firefox for web developers. As a developer myself, I absolutely love the work that Paul and his teammates are doing. But I have to challenge the idea of including these features in Firefox core rather than making them available as add-ons.</p>
<p>Surely the vast majority of Firefox users are not developers. So, why include this in Firefox?</p>
<p>Also, as much as I love the effort they are putting into DevTools, much of it seems like a repeated effort. Firebug has been around for a long time and is battle-tested. Why rebuild its features into Firefox core? Why not put the effort toward improving Firebug instead?</p>
<p>In his post yesterday, Paul talks about building a new network panel and timeline for DevTools like the one in Firebug. What advantage will the new panel have over Firebug?</p>
<p>I&#8217;m genuinely curious about the answers to these questions. I&#8217;m not trying to call out Paul or his teammates here or declare &#8220;you&#8217;re doing it wrong.&#8221; That&#8217;s not helpful.</p>
<p>I love Mozilla and the work that they do. I&#8217;ve had the pleasure of working with several talented folks who are now part of the Mozilla family. I deeply respect the work they do and I wouldn&#8217;t be writing this if I didn&#8217;t care.</p>
<p>I&#8217;d love to hear from Paul and/or his teammates, get some insight into their decision making, and start a discussion. I want Firefox to be the best goddamn browser, always.</p>
<p>&nbsp;</p>
<p>The post <a href="http://bradt.ca/blog/why-firefox-devtools/">Why Firefox&nbsp;DevTools?</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/why-firefox-devtools/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/why-firefox-devtools/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=why-firefox-devtools</feedburner:origLink></item>
		<item>
		<title>The Best Time to Buy is Now</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/2tJnVJdTdhI/</link>
		<comments>http://bradt.ca/blog/the-best-time-to-buy-is-now/#comments</comments>
		<pubDate>Mon, 04 Feb 2013 16:49:36 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[Entrepreneurship]]></category>
		<category><![CDATA[Promotions]]></category>
		<category><![CDATA[Sales]]></category>
		<category><![CDATA[startup]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=15353</guid>
		<description><![CDATA[<p>My first WP Daily guest post was just published. Here&#8217;s a taste: Last year, I bought a sofa for my office from Costco. It went on sale two weeks later for $500 off. I called Costco and they refunded the $500. No questions asked. The thing is, I knew there was a good chance the sofa would [...]</p><p>The post <a href="http://bradt.ca/blog/the-best-time-to-buy-is-now/">The Best Time to Buy is&nbsp;Now</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>My first WP Daily guest post was just published. Here&#8217;s a taste:</p>
<blockquote><p>Last year, I bought a sofa for my office from Costco. It went on sale two weeks later for $500 off. I called Costco and they refunded the $500. No questions asked.</p>
<p>The thing is, I knew there was a good chance the sofa would go on sale in the near future. I also knew that Costco would refund the difference. So, I didn’t hesitate to purchase. I didn’t wait for the sofa to go on sale.</p>
<p>When a customer is considering purchasing your product, you don’t want them thinking that waiting for a sale is a good idea.</p>
<p><em>Now</em> should always feel like the best time to buy.</p></blockquote>
<p><a href="http://wpdaily.co/best-time-buy/">http://wpdaily.co/best-time-buy/</a></p>
<p>The post <a href="http://bradt.ca/blog/the-best-time-to-buy-is-now/">The Best Time to Buy is&nbsp;Now</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/the-best-time-to-buy-is-now/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/the-best-time-to-buy-is-now/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=the-best-time-to-buy-is-now</feedburner:origLink></item>
		<item>
		<title>Extending WP_Query</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/WWVzBgTn_Gc/</link>
		<comments>http://bradt.ca/blog/extending-wp_query/#comments</comments>
		<pubDate>Sun, 16 Dec 2012 15:46:01 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[web-development]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=15037</guid>
		<description><![CDATA[<p>I&#8217;ve had the pleasure of contributing a couple of WordPress (WP) snippets to Elliot Richmond’s WP Snippets Til Christmas advent calendar this holiday season. Today&#8217;s snippet happens to be one of my contributions and I&#8217;d like to expand on it a bit more here. Let&#8217;s say you have a custom post type book and a [...]</p><p>The post <a href="http://bradt.ca/blog/extending-wp_query/">Extending&nbsp;WP_Query</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve had the pleasure of contributing a couple of WordPress (WP) snippets to <a href="https://twitter.com/elliottrichmond/">Elliot Richmond</a>’s <a href="http://advent.squareonemd.co.uk/">WP Snippets Til Christmas</a> advent calendar this holiday season. <a href="http://advent.squareonemd.co.uk/neatly-wrapping-custom-post-type-queries-in-a-custom-class-that-extends-wp_query/" title="Neatly Wrapping Custom Post Type Queries in a Custom Class that Extends WP_Query">Today&#8217;s snippet</a> happens to be one of my contributions and I&#8217;d like to expand on it a bit more here.<br />
<span id="more-15037"></span><br />
Let&#8217;s say you have a custom post type <code>book</code> and a custom taxonomy <code>book_category</code>. You want to list all the book categories in alphabetical order and list the books under each one, like this:</p>
<blockquote><p><strong>Business</strong></p>
<ul>
<li>Blue Ocean Strategy</li>
<li>The Innovator&#8217;s Dilemma</li>
</ul>
<p><strong>Fiction</strong></p>
<ul>
<li>Dracula</li>
<li>Frankenstein</li>
<li>The Hobbit</li>
<li>Winter of the World</li>
</ul>
<p><strong>History</strong></p>
<ul>
<li>With the Old Breed</li>
</ul>
</blockquote>
<p>Again, notice that both categories and book titles are sorted alphabetically. Unfortunately, there is no easy way to do this using a single call to <code>WP_Query</code>. There is no option to sort by taxonomy. Instead, most developers would get all the categories, then loop through them, calling <code>WP_Query</code> for each one. That would be ok for the tiny bit of data we have above, but let&#8217;s pretend we are outputting a printable catalog of books and we have hundreds of categories and thousands of books. We would definitely want to do this more efficiently, with just one call to <code>WP_Query</code>. So, we need to make use of the filters in <code>WP_Query</code>. In the past, I would have done something like this:</p>
<pre>function my_posts_fields( $sql ) {
	global $wpdb;
	return $sql . ", $wpdb-&gt;terms.name AS 'book_category'";
}

function my_posts_join( $sql ) {
	global $wpdb;
	return $sql . "
		INNER JOIN $wpdb-&gt;term_relationships ON ($wpdb-&gt;posts.ID = $wpdb-&gt;term_relationships.object_id) 
		INNER JOIN $wpdb-&gt;term_taxonomy ON ($wpdb-&gt;term_relationships.term_taxonomy_id = $wpdb-&gt;term_taxonomy.term_taxonomy_id) 
		INNER JOIN $wpdb-&gt;terms ON ($wpdb-&gt;terms.term_id = $wpdb-&gt;term_taxonomy.term_id) 
	";
}

function my_posts_where( $sql ) {
	global $wpdb;
	return $sql . " AND $wpdb-&gt;term_taxonomy.taxonomy = 'book_category'";
}

function my_posts_orderby( $sql ) {
	global $wpdb;
	return "$wpdb-&gt;terms.name ASC, $wpdb-&gt;posts.post_title ASC";
}

add_filter( 'posts_fields', 'my_posts_fields' );
add_filter( 'posts_join', 'my_posts_join' );
add_filter( 'posts_where', 'my_posts_where' );
add_filter( 'posts_orderby', 'my_posts_orderby' );

$r = new WP_Query( array(
	'post_type' =&gt; 'book',
	'posts_per_page' =&gt; -1,
	// Optimize query for no paging
	'no_found_rows' =&gt; true,
	'update_post_term_cache' =&gt; false,
	'update_post_meta_cache' =&gt; false
) );

// Make sure these filters don't affect any other queries
remove_filter( 'posts_fields', 'my_posts_fields' );
remove_filter( 'posts_join', 'my_posts_join' );
remove_filter( 'posts_where', 'my_posts_where' );
remove_filter( 'posts_orderby', 'my_posts_orderby' );</pre>
<p>Look at all those single purpose functions in the global scope. Gross. We can tidy this up by wrapping it in a class that extends <code>WP_Query</code>.</p>
<pre>class My_Book_Query extends WP_Query {

	function __construct( $args = array() ) {
		// Force these args
		$args = array_merge( $args, array(
			'post_type' =&gt; 'book',
			'posts_per_page' =&gt; -1,  // Turn off paging
			'no_found_rows' =&gt; true, // Optimize query for no paging
			'update_post_term_cache' =&gt; false,
			'update_post_meta_cache' =&gt; false
		) );

		add_filter( 'posts_fields', array( $this, 'posts_fields' ) );
		add_filter( 'posts_join', array( $this, 'posts_join' ) );
		add_filter( 'posts_where', array( $this, 'posts_where' ) );
		add_filter( 'posts_orderby', array( $this, 'posts_orderby' ) );

		parent::__construct( $args );

		// Make sure these filters don't affect any other queries
		remove_filter( 'posts_fields', array( $this, 'posts_fields' ) );
		remove_filter( 'posts_join', array( $this, 'posts_join' ) );
		remove_filter( 'posts_where', array( $this, 'posts_where' ) );
		remove_filter( 'posts_orderby', array( $this, 'posts_orderby' ) );
	}

	function posts_fields( $sql ) {
		global $wpdb;
		return $sql . ", $wpdb-&gt;terms.name AS 'book_category'";
	}

	function posts_join( $sql ) {
		global $wpdb;
		return $sql . "
			INNER JOIN $wpdb-&gt;term_relationships ON ($wpdb-&gt;posts.ID = $wpdb-&gt;term_relationships.object_id) 
			INNER JOIN $wpdb-&gt;term_taxonomy ON ($wpdb-&gt;term_relationships.term_taxonomy_id = $wpdb-&gt;term_taxonomy.term_taxonomy_id) 
			INNER JOIN $wpdb-&gt;terms ON ($wpdb-&gt;terms.term_id = $wpdb-&gt;term_taxonomy.term_id) 
		";
	}

	function posts_where( $sql ) {
		global $wpdb;
		return $sql . " AND $wpdb-&gt;term_taxonomy.taxonomy = 'book_category'";
	}

	function posts_orderby( $sql ) {
		global $wpdb;
		return "$wpdb-&gt;terms.name ASC, $wpdb-&gt;posts.post_title ASC";
	}

}</pre>
<p>Now all your custom query code is neatly contained in this class file that you can include when necessary. And because we&#8217;re extending <code>WP_Query</code>, you can use the usual functions in your loops:</p>
<pre>
$query = new My_Book_Query();

if ( $query->have_posts() ) :

	while ( $query->have_posts() ) :

		$query->the_post();
		...

	endwhile;

	wp_reset_postdata();

endif;
</pre>
<p>The post <a href="http://bradt.ca/blog/extending-wp_query/">Extending&nbsp;WP_Query</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/extending-wp_query/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/extending-wp_query/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=extending-wp_query</feedburner:origLink></item>
		<item>
		<title>Commercial WordPress Plugins</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/u8nxOcP8YS4/</link>
		<comments>http://bradt.ca/blog/commercial-wordpress-plugins/#comments</comments>
		<pubDate>Mon, 19 Nov 2012 12:00:29 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[web-development]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=14936</guid>
		<description><![CDATA[<p>As promised, I&#8217;m responding to Matt&#8217;s comments at PressNomics on selling WordPress (WP) plugins and laying out my philosophy and beliefs on the subject. Commercial plugins help or hurt WP? I&#8217;m summarizing, but during his interview, Matt essentially said that designs have more value in being rare, charging for them makes them rarer, so it [...]</p><p>The post <a href="http://bradt.ca/blog/commercial-wordpress-plugins/">Commercial WordPress&nbsp;Plugins</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p><a href="http://bradt.ca/blog/pressnomics-conference-recap/">As promised</a>, I&#8217;m responding to <a href="http://ma.tt">Matt&#8217;s</a> comments at PressNomics on selling WordPress (WP) plugins and laying out my philosophy and beliefs on the subject.</p>
<h3>Commercial plugins help or hurt WP?</h3>
<p>I&#8217;m summarizing, but during his interview, Matt essentially said that designs have more value in being rare, charging for them makes them rarer, so it makes sense. On the flip side, plugins have more value being ubiquitous, charging for them makes them less so, so it doesn&#8217;t make sense.<br />
<span id="more-14936"></span><br />
I agree that plugins have more value being ubiquitous. We should strive to make plugins (or any code for that matter) more available and accessible. I mean, I was able to learn Perl and PHP in my teens by hacking on freely available open source code. I want the next generation to have that same opportunity. But I think there&#8217;s very little risk of losing that. When I was hunting for Perl scripts at cgi-resources.com, there were upsells to paid versions with additional features and support. And that was over a decade ago. The business model of giving away a free package but offering a commercial package with more features and support is ancient in web years; yet I&#8217;m pretty sure that free open source is as healthy as it has ever been. I think a very strong argument can be made that business helps the proliferation of free open source software more than it hurts.</p>
<p>In an ideal world, all code would be free open source and all developers would still be able to pay their bills. But that&#8217;s a utopian dream. In this world, there needs to be a balance between free and commercial open source. Give away as much as possible while still being able to pay the bills.</p>
<h3>Charging for SaaS is better than charging for plugins</h3>
<p>Matt explained that he believes it&#8217;s better to offer a free plugin and charge for a service (SaaS) that the plugin connects to (like Akismet &#038; VaultPress) rather than charge for the plugin itself. I disagree. Giving away a plugin that doesn&#8217;t work without a paid SaaS subscription isn&#8217;t exactly a great contribution to the free open source community. In fact, I&#8217;d argue that the free plugin + commercial plugin model is better. I mean, in the SaaS model the bulk of the code is private. You can release little bits and pieces but not the whole thing. If you released everything, it would be too easy for competitors to setup shop.</p>
<p>With the free plugin + commercial plugin model, when you buy an open source plugin, all the code is available to be reviewed, learned from, and in the case of GPL, you&#8217;re free to use the code in your own GPL projects. Plus, you have access to a working, free plugin.</p>
<h3>Bad idea to build a business on a feature that could be rolled into WP core</h3>
<p>When people&#8217;s livelihoods depend on selling a certain plugin that could be incorporated into WP core, Matt explained that it becomes a bit of a dilemma for the core team. He gave the specific example that if WP core decided to incorporate some kind of form features it might hurt <a href="http://www.gravityforms.com/">Gravity Forms</a>.</p>
<p>This extends to business in general. It&#8217;s a bad idea to build a business on a feature that could be built-into the platform. I think it&#8217;s generally good advice, but as with most business advice, I don&#8217;t think it&#8217;s a hard and fast rule. If you recognize the risk of the platform incorporating the feature, but the investment is minimal, it might still be worth it.</p>
<p>A non-WP example of this is <a href="http://www.s3stat.com/web-stats/about-us.ashx">S3Stat</a>, which was built by Expat Software to satisfy a need for their clients, then with a little development offered as a service for $5/month and set it on autopilot. That was 2007. Now, five years later, Amazon Web Services still has not integrated this feature into their platform, and the founder of S3Stat has been <a href="http://news.ycombinator.com/item?id=1925214">living off the income for the past three years</a>, all the while bootstrapping other ventures.</p>
<p>As for Gravity Forms, I&#8217;ll let the founder, <a href="http://perezbox.com/2012/11/wordpress-commercialization-to-do-or-not-to-do/#comment-830">Carl Hancock speak to that</a>.</p>
<p>I suggest heading over to the <a href="http://perezbox.com/2012/11/wordpress-commercialization-to-do-or-not-to-do/#comments">comments of Tony Perez&#8217;s post on this subject</a> to read opinions from plenty of others including responses from Matt.</p>
<p>The post <a href="http://bradt.ca/blog/commercial-wordpress-plugins/">Commercial WordPress&nbsp;Plugins</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/commercial-wordpress-plugins/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/commercial-wordpress-plugins/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=commercial-wordpress-plugins</feedburner:origLink></item>
		<item>
		<title>PressNomics Conference Recap</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/2KrgjvEjKSI/</link>
		<comments>http://bradt.ca/blog/pressnomics-conference-recap/#comments</comments>
		<pubDate>Thu, 15 Nov 2012 17:33:10 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[conference]]></category>
		<category><![CDATA[pressnomics]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=14915</guid>
		<description><![CDATA[<p>PressNomics was the best conference I&#8217;ve ever attended. Held last week in Phoenix, it was the inaugural conference for those in the business of WordPress (WP). Previous to this, Web Directions North had been my favourite conference. So why did PressNomics take the top spot? I&#8217;m passionate about building web-based product businesses. Most conferences I&#8217;ve [...]</p><p>The post <a href="http://bradt.ca/blog/pressnomics-conference-recap/">PressNomics Conference&nbsp;Recap</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p><a href="http://pressnomics.com">PressNomics</a> was the best conference I&#8217;ve ever attended.</p>
<p>Held last week in Phoenix, it was the inaugural conference for those in the business of WordPress (WP). Previous to this, <a href="http://bradt.ca/blog/web-directions-north-wrapup/">Web Directions North</a> had been my favourite conference. So why did PressNomics take the top spot?</p>
<p>I&#8217;m passionate about building web-based product businesses. Most conferences I&#8217;ve attended have focused on web development, talking mostly about the technology and a bit about client services business. Few attendees were interested in talking about products. They were either self-employed, happy doing client services, or employed at an agency. PressNomics was largely about products.<br />
<span id="more-14915"></span><br />
Every night, I had dinner with designers and developers who were building products for WP. I heard stories about scaling customer support, costs involved, and setting prices. I heard fascinating plans about upcoming projects. I was given feedback on my work, suggestions for what to work on next and strategies. I asked questions and was blown away by the feedback.</p>
<p>Due to the nor&#8217;easter storm, I had to reschedule flights and missed the day one sessions. I would really have liked to catch <a href="http://ithemes.com">Cory Miller</a> and <a href="http://alexking.org">Alex King</a>&#8216;s talks in particular, but upon landing in Phoenix, I felt lucky I had made it at all. I managed to arrive just in time for the happy hour drinks and was super pumped to meet a ton of awesome people I&#8217;ve been emailing with for the past year, especially <a href="http://adii.me">Adii</a>, who&#8217;s been advising me on WP App Store. I really enjoyed the day two sessions. I was particularly floored by the stories of humble beginnings from <a href="http://envato.com">Envato</a>&#8216;s CEO Collis Ta&#8217;eed and the gutsy decisions made by Chris Drake to get <a href="http://firehost.com">FireHost</a> where it is today.</p>
<p>And then there was the on-stage interview with <a href="http://ma.tt">Matt Mullenweg</a> which went exactly as I expected it would. I had had a couple of back and forth emails with Matt about WP App Store months ago and felt I had a pretty good understanding of his position. I also had an in-depth email exchange with Otto about .org and why the WP App Store plugin isn&#8217;t allowed. I have a good understanding of the philosophy there as well. So when Matt was asked about his disapproval of WP App Store, I wasn&#8217;t surprised by the response and I didn&#8217;t see the point of challenging. As Tony Perez from <a href="http://sucuri.net/">Securi</a> says in <a href="http://perezbox.com/2012/11/wordpress-commercialization-to-do-or-not-to-do/">his post</a>:</p>
<blockquote><p>It is one of those things that is engrained in how an individual thinks and in their own beliefs. It’s not really one of those subjects that you can engage in, in the hopes that you will sway opinion.</p></blockquote>
<p>I also wasn&#8217;t surprised that Matt&#8217;s beliefs conflicted with the majority of people in the room. I started writing out my beliefs and philosophy concerning selling plugins in this post, but it was running too long. I&#8217;ll put together a separate post in the next few days. Spoiler alert, my philosophy and beliefs aren&#8217;t in line with Matt&#8217;s. In the meantime, there&#8217;s an excellent discussion going on in <a href="http://perezbox.com/2012/11/wordpress-commercialization-to-do-or-not-to-do/#comments">the comments of Tony&#8217;s post</a> about this stuff. Check it out.</p>
<p>I can hardly imagine how much work Sally, Josh, and the <a href="http://page.ly">Page.ly</a> crew put into this event. It was so awesome, that any issues don&#8217;t even register. Fuck the Wifi. Who cares who got VIP and who didn&#8217;t. As far as I&#8217;m concerned, it was flawless. Kudos to you folks. You should be very proud.</p>
<p>The post <a href="http://bradt.ca/blog/pressnomics-conference-recap/">PressNomics Conference&nbsp;Recap</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/pressnomics-conference-recap/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/pressnomics-conference-recap/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=pressnomics-conference-recap</feedburner:origLink></item>
		<item>
		<title>Custom Table for WordPress Custom Fields</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/56ma8h3ovMM/</link>
		<comments>http://bradt.ca/blog/custom-table-for-wordpress-custom-fields/#comments</comments>
		<pubDate>Thu, 01 Nov 2012 13:22:50 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[web-development]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=14815</guid>
		<description><![CDATA[<p>I just read Tom McFarlin&#8217;s thoughts on building web apps with WP and my comment started to run long, so here I am, writing a blog post. I agree with Tom, WP can make a great foundation for an app. I chose to use WP when I started building WP&#160;App&#160;Store and it gave me a [...]</p><p>The post <a href="http://bradt.ca/blog/custom-table-for-wordpress-custom-fields/">Custom Table for WordPress Custom&nbsp;Fields</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>I just read Tom McFarlin&#8217;s <a href="http://tommcfarlin.com/web-applications-with-wordpress/">thoughts on building web apps with <abbr title="WordPress">WP</abbr></a> and my comment started to run long, so here I am, writing a blog post.</p>
<p>I agree with Tom, WP can make a great foundation for an app. I chose to use WP when I started building <a href="http://wpappstore.com">WP&nbsp;App&nbsp;Store</a> and it gave me a huge head start when compared to starting with an app framework. Having the dashboard UI built and ready to customize is probably the piece that saved the most time. As Tom mentions, there&#8217;s user management, a clean API, and lots more. All of which are battle-tested by thousands of developers and millions of web sites. As I went deeper into building WP App Store though, I did learn some lessons about what works and what doesn&#8217;t when using WP for an app. In this post, I&#8217;ll describe one that I&#8217;ve been grappling with lately.<br />
<span id="more-14815"></span><br />
As WP App Store has become more complex, so has our data and our queries. Some of our queries are now looking pretty ugly, having a lot of joins. Here&#8217;s one that gets a report of a publisher&#8217;s sales commissions by querying the <code>order</code> post type and its custom field data:</p>
<pre class="language-sql">SELECT pm2.meta_value as product_title, COUNT(*) as unit_count, SUM( pm1.meta_value ) as commission_total
FROM $wpdb-&gt;posts
	INNER JOIN $wpdb-&gt;postmeta AS pm1 ON pm1.post_id = $wpdb-&gt;posts.ID AND pm1.meta_key = '_commission_amount'
	INNER JOIN $wpdb-&gt;postmeta AS pm2 ON pm2.post_id = $wpdb-&gt;posts.ID AND pm2.meta_key = '_product_title'
	LEFT OUTER JOIN $wpdb-&gt;postmeta AS pm3 ON pm3.post_id = $wpdb-&gt;posts.ID AND pm3.meta_key = '_is_test'
	INNER JOIN $wpdb-&gt;postmeta AS pm4 ON pm4.post_id = $wpdb-&gt;posts.ID AND pm4.meta_key = '_publisher_id'
    INNER JOIN $wpdb-&gt;postmeta AS pm5 ON pm5.post_id = $wpdb-&gt;posts.ID AND pm5.meta_key = '_product_type'
    LEFT OUTER JOIN $wpdb-&gt;postmeta AS pm6 ON pm6.post_id = $wpdb-&gt;posts.ID AND pm6.meta_key = '_is_refunded'
WHERE pm5.meta_value = '%s'
    AND pm4.meta_value = '%s'
    AND (pm3.meta_value IS NULL OR pm3.meta_value = '' OR pm3.meta_value = '0')
    AND (pm6.meta_value IS NULL OR pm6.meta_value = '' OR pm6.meta_value = '0')
    AND $wpdb-&gt;posts.post_type = 'order'
    AND ($wpdb-&gt;posts.post_status = 'publish')
GROUP BY pm2.meta_value</pre>
<p>Why a custom query? Why not use the <code>WP_Query</code> object for this? I believe <code>WP_Query</code> is intended for querying posts, but in this case we&#8217;re generating a report, leveraging MySQL&#8217;s awesome ability to count the number of sales, add up the commissions, and return one neat row with these numbers for each product title. This is the kind of stuff MySQL is great at doing. </p>
<p>Unfortunately though, custom fields were not designed for this. They were originally designed to be returned from the database and displayed, not queried. Performance is terrible when queries have to join the <code>postmeta</code> table many times for each custom field key. Plus, as <a href="http://shoptalkshow.com/episodes/033-with-alex-king/">Alex King pointed out in a recent episode of Shop Talk Show</a> we&#8217;re querying a MySQL column of type <code>longtext</code> which is slow. And to top it off, the <code>meta_value</code> column isn&#8217;t indexed.</p>
<p>If we look at the custom fields in the query above, you could convert some of the custom fields into taxonomies because they would be repeated across several orders, but there is some data that is unique to a single instance of a post as well. Also, if we converted all the custom fields to taxonomies, we&#8217;d end up querying for many taxonomies in this one query, which would again result in many joins. Instead, I&#8217;ve been considering creating a custom database table for each post type with columns for each of the custom fields.</p>
<p>So, for the data in the example above, a custom table to hold the custom field data might look like the following:</p>
<pre class="language-sql">CREATE TABLE IF NOT EXISTS `wp_pm_order` (
  `order_id` bigint(20) NOT NULL,
  `publisher_id` bigint(20) NOT NULL,
  `product_title` varchar(255) NOT NULL,
  `product_type` varchar(255) NOT NULL,
  `commission_amount` float NOT NULL,
  `is_test` tinyint(4) NOT NULL,
  `is_refunded` tinyint(4) NOT NULL
  PRIMARY KEY (`order_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;</pre>
<p>Then the query above would look like this:</p>
<pre class="language-sql">SELECT pm.product_title, COUNT(*) as unit_count, SUM( pm.commission_amount ) as commission_total
FROM $wpdb-&gt;posts
    INNER JOIN $wpdb-&gt;pm_order AS pm ON pm.post_id = $wpdb-&gt;posts.ID
WHERE pm.product_type = '%s'
    AND pm.publisher_id = '%s'
    AND (pm.is_test IS NULL OR pm.is_test = '' OR pm.is_test = '0')
    AND (pm.is_refunded IS NULL OR pm.is_refunded = '' OR pm.is_refunded = '0')
    AND $wpdb-&gt;posts.post_type = 'order'
    AND ($wpdb-&gt;posts.post_status = 'publish')
GROUP BY pm.product_title</pre>
<p>We&#8217;ve gone from 6 joins down to 1 and the columns we&#8217;re now querying are of types representative of the data they contain. A major improvement to the query performance, but also much easier to read and maintain in the future.</p>
<p>What do you think of using custom database tables to store custom field data for an app built with WP? Maybe there&#8217;s a better way?</p>
<p>Thanks to <a href="http://tommcfarlin.com">Tom</a> for reviewing and providing feedback on this post before I published it.</p>
<p>The post <a href="http://bradt.ca/blog/custom-table-for-wordpress-custom-fields/">Custom Table for WordPress Custom&nbsp;Fields</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/custom-table-for-wordpress-custom-fields/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/custom-table-for-wordpress-custom-fields/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=custom-table-for-wordpress-custom-fields</feedburner:origLink></item>
		<item>
		<title>WordPress Best Practices: Global functions vs. classes</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/ZX7ZAxTJzNg/</link>
		<comments>http://bradt.ca/blog/wordpress-best-practices-global-functions-vs-classes/#comments</comments>
		<pubDate>Fri, 25 May 2012 14:21:46 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[Coding Standards]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[web-development]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=10198</guid>
		<description><![CDATA[<p>A couple of months ago I started a thread on the wp-hackers mailing list. I wanted to know whether the WP&#8217;s core team intended to use PHP classes or sets of global functions with global variables going forward. I prize consistency above all else in my coding and wanted to follow their lead. Here&#8217;s what I posted: [...]</p><p>The post <a href="http://bradt.ca/blog/wordpress-best-practices-global-functions-vs-classes/">WordPress Best Practices: Global functions vs.&nbsp;classes</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>A couple of months ago <a href="http://lists.automattic.com/pipermail/wp-hackers/2012-March/042574.html">I started a thread</a> on the <a href="http://lists.automattic.com/pipermail/wp-hackers/">wp-hackers mailing list</a>. I wanted to know whether the WP&#8217;s core team intended to use PHP classes or sets of global functions with global variables going forward. I prize consistency above all else in my coding and wanted to follow their lead. Here&#8217;s what I posted:</p>
<blockquote><p>It seems there&#8217;s two competing views on whether a set of functions or a class should be used in core. For example, the relatively recent transients API is a set of functions, but could have just as easily been a class. Is there some reasoning behind the decision to use a set of functions instead of a class? Or is it coders choice?</p>
<p>To be clear, I&#8217;m not for or against using a set of functions instead of a class. And the transients API is great the way it is. If there&#8217;s no difference in performance, I personally don&#8217;t care either way. However, I am for consistency and don&#8217;t see a reason why we should use both techniques.</p>
<p>And what about static class variables rather than global variables? WP stopped supporting PHP4 last year, so is there any reason not to use static class variables for new development?</p></blockquote>
<p><span id="more-10198"></span>The discussion that ensued was excellent. For me, the crux of the conversation was delivered by <a href="http://twitter.com/nacin">@nacin</a>, with a <a href="http://lists.automattic.com/pipermail/wp-hackers/2012-March/042586.html">detailed post</a> about the core team&#8217;s work with classes and globals. I recommend reading the whole post, but here are few gems:</p>
<blockquote><p>Don&#8217;t be surprised if WP_Post happens in 3.5 or 3.6.</p></blockquote>
<blockquote><p>Someone mentioned PHP 5.3 namespaces, and that makes me laugh. In two years, PHP 5.3 adoption (in terms of WordPress sites) has gone from 4% to 10%. Not holding my breath.</p></blockquote>
<blockquote><p>Rewriting code for the sake of rewriting code is not a good use of our time.</p></blockquote>
<blockquote><p>That also does not mean we&#8217;re going to use classes everywhere. There are plenty of situations where procedural code is going to be better.</p></blockquote>
<blockquote><p>I am at the point where I will fight hard to prevent any more globals be added to core; we&#8217;ve been rather good about this for at least a year now.</p></blockquote>
<blockquote><p>That said, static class variables (as suggested) aren&#8217;t exactly any better. They&#8217;re not.</p></blockquote>
<blockquote><p>Global state (a global variable, a static method, a static property, what have you) is not testable.</p></blockquote>
<p>The last point about global state not being testable really hit home for me. I read through all of the referenced articles and it really opened my eyes to how code needs to be structured to be testable. I&#8217;ve been aiming to get into test-driven development and this was a big step in that direction.</p>
<p>Also, it&#8217;s clear to me now that I should avoid using the global state and look at implementing a class before a set of global functions, but not be afraid to use global functions if they work better for the situation.</p>
<p>The post <a href="http://bradt.ca/blog/wordpress-best-practices-global-functions-vs-classes/">WordPress Best Practices: Global functions vs.&nbsp;classes</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/wordpress-best-practices-global-functions-vs-classes/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/wordpress-best-practices-global-functions-vs-classes/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=wordpress-best-practices-global-functions-vs-classes</feedburner:origLink></item>
		<item>
		<title>Dandelion: Git Deployment over FTP</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/XFuNPXY0F8g/</link>
		<comments>http://bradt.ca/blog/dandelion-git-deployment-over-ftp/#comments</comments>
		<pubDate>Fri, 04 May 2012 18:10:59 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[web-development]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=10178</guid>
		<description><![CDATA[<p>Dandelion is an awesome deployment tool I adopted recently. When you run dandelion deploy from the command line, it automatically determines what has changed in your Git repository since the last deploy and adds, updates, or deletes just those files over FTP, SFTP, or Amazon S3. Kudos to Scott Nelson for his awesome work on this. Packaged as [...]</p><p>The post <a href="http://bradt.ca/blog/dandelion-git-deployment-over-ftp/">Dandelion: Git Deployment over&nbsp;FTP</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p><a href="https://github.com/scttnlsn/dandelion">Dandelion</a> is an awesome deployment tool I adopted recently. When you run <code>dandelion deploy</code> from the command line, it automatically determines what has changed in your Git repository since the last deploy and adds, updates, or deletes just those files over FTP, SFTP, or Amazon S3. Kudos to <a href="http://www.scttnlsn.com/">Scott Nelson</a> for his awesome work on this.</p>
<p>Packaged as a Ruby Gem, Dandelion is super simple to install: <code>gem install dandelion</code>. To setup, you create a tiny YAML config file in the same directory as your Git repository with the server connection details. Then you can run  <code>dandelion deploy</code>. Pure awesome.</p>
<p><span id="more-10178"></span></p>
<p>Why not use <a href="https://github.com/capistrano/capistrano">Capistrano</a>? I&#8217;m definitely not a Capistrano expert, but I don&#8217;t believe it&#8217;s as easy to set it up to deploy over FTP, SFTP, or Amazon S3. From what I understand, it&#8217;s primarily designed for use over SSH. Unfortunately not all web hosts offer SSH access and those that do sometimes require time to set it up. As a result, I&#8217;d been using the <a href="http://panic.com/transmit/">Transmit</a>&#8216;s Sync feature for years. It&#8217;s slow (needs to check each file for changes) and a break from my command line workflow. Dandelion keeps my workflow on the command line and is much quicker to upload changes. A nice little bump to <a href="http://gettingreal.37signals.com/ch10_Optimize_for_Happiness.php">developer happiness</a>.</p>
<p>I&#8217;ve been deploying <a href="http://wpappstore.com/">WP App Store</a> to <a href="http://wpengine.com/">WP Engine</a> with Dandelion for a couple weeks now and it has been fantastic. As I bring on additional developers and deployment becomes a bit more complex, I&#8217;ll likely move to <a href="http://www.deployhq.com/">deployhq.com</a>.</p>
<p>The post <a href="http://bradt.ca/blog/dandelion-git-deployment-over-ftp/">Dandelion: Git Deployment over&nbsp;FTP</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/dandelion-git-deployment-over-ftp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/dandelion-git-deployment-over-ftp/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=dandelion-git-deployment-over-ftp</feedburner:origLink></item>
		<item>
		<title>Founder Inner Conflict: Engineer vs. Entrepreneur</title>
		<link>http://feedproxy.google.com/~r/bradtca/posts/~3/X8ijRPmV3NU/</link>
		<comments>http://bradt.ca/blog/founder-inner-conflict-engineer-vs-entrepreneur/#comments</comments>
		<pubDate>Wed, 22 Feb 2012 14:27:36 +0000</pubDate>
		<dc:creator>bradt</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Business]]></category>
		<category><![CDATA[psychology]]></category>
		<category><![CDATA[startup]]></category>
		<category><![CDATA[web-development]]></category>

		<guid isPermaLink="false">http://bradt.ca/?p=10036</guid>
		<description><![CDATA[<p>When working on a project, I constantly find myself at odds with&#8230;myself. It&#8217;s a battle between the engineer in me wanting to write ideal code and the entrepreneur in me who just wants it working. The engineer wants ideal code, while the entrepreneur is fine with it being hacked together as long as it works [...]</p><p>The post <a href="http://bradt.ca/blog/founder-inner-conflict-engineer-vs-entrepreneur/">Founder Inner Conflict: Engineer vs.&nbsp;Entrepreneur</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>When working on a project, I constantly find myself at odds with&#8230;myself. It&#8217;s a battle between the engineer in me wanting to write ideal code and the entrepreneur in me who just wants it working. The engineer wants ideal code, while the entrepreneur is fine with it being hacked together as long as it works great.</p>
<p>I go through phases where the engineer takes over. There&#8217;s an itch that has been irritating him for weeks, maybe even months, and damn it, he&#8217;s going to scratch it. He starts writing some kind of framework, or refactoring some code, or implementing some new, fascinating approach he read about, or working on a cool, new feature that is out of scope. At the same time, from the back seat, the entrepreneur starts nagging for updates on the overall progress of the project. Unfortunately though, scratching these itches rarely moves the project forward, so the entrepreneur is relentless. With every line of code, the entrepreneur is becoming more and more irritating. Eventually the nagging is more irritating than the original itch, so the engineer drops his tangential coding and resumes hacking the project forward.<span id="more-10036"></span></p>
<p>When I only had client work, I allowed the engineer to scratch a few itches per project. It improved my code base, often saving me time on the next project, and increased <a href="http://gettingreal.37signals.com/ch10_Optimize_for_Happiness.php">developer happiness</a>. Sure, the project price was set and I would make more money by just hacking, but I valued developer happiness and it was worth the tiny cost.</p>
<p>But now in the throes of building a new business, I can&#8217;t afford to let the engineer take over at any point. <a href="http://blog.asmartbear.com/value-time.html">A founder&#8217;s time is expensive</a> and any time spent working on the business should be moving it forward. A balance is needed between the engineer and the entrepreneur. Neither should take over completely. The entrepreneur keeps things moving forward and the engineer ensures that hacking doesn&#8217;t create a complete disaster.</p>
<p>New businesses need to hack things together so they can start generating revenue as soon as possible. Adii <a href="http://adii.me/2012/02/bootstrappingdont-reinvent-the-wheel/">shares</a> a great inside story on this. WooThemes hacked together their membership and payment processing backend when they started. And hacked away at it for three years, until they were in a better position to rewrite it. The engineer cringes at the idea of hacking away at crappy code for three years, but the entrepreneur slaps him, quoting WooThemes&#8217; success.</p>
<p>Fortunately, I&#8217;ve found that the entrepreneur&#8217;s nagging is much more intense when there&#8217;s no revenue, which makes it easier to keep the engineer in check. That&#8217;s not to say the engineer doesn&#8217;t try to take over as often as before. He does. But the mere threat of intense nagging sends him back to hacking.</p>
<p>Vote this up on HN:<br />
<a href="http://news.ycombinator.com/item?id=3620954">http://news.ycombinator.com/item?id=3620954</a></p>
<p>The post <a href="http://bradt.ca/blog/founder-inner-conflict-engineer-vs-entrepreneur/">Founder Inner Conflict: Engineer vs.&nbsp;Entrepreneur</a> appeared first on <a href="http://bradt.ca">bradt.ca</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://bradt.ca/blog/founder-inner-conflict-engineer-vs-entrepreneur/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://bradt.ca/blog/founder-inner-conflict-engineer-vs-entrepreneur/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=founder-inner-conflict-engineer-vs-entrepreneur</feedburner:origLink></item>
	</channel>
</rss>
