<?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>Coyote Tracks &#187; coding</title>
	<atom:link href="http://kagan.mactane.org/blog/tag/coding/feed/" rel="self" type="application/rss+xml" />
	<link>http://kagan.mactane.org/blog</link>
	<description>The prints of an Internet-enabled coyote.</description>
	<lastBuildDate>Tue, 31 Jan 2012 03:26:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Beware of Optional Curly Braces&#160;&#8212; They Will Bite You</title>
		<link>http://kagan.mactane.org/blog/2012/01/30/beware-of-optional-curly-braces-they-will-bite-you/</link>
		<comments>http://kagan.mactane.org/blog/2012/01/30/beware-of-optional-curly-braces-they-will-bite-you/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 03:26:51 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bad ideas]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[silly coding tricks]]></category>

		<guid isPermaLink="false">http://kagan.mactane.org/blog/?p=441</guid>
		<description><![CDATA[I was looking through some PHP code from a third-party vendor recently, and saw something that made my jaw drop. It&#8217;s pretty innocent-looking, at first. Here&#8217;s a somewhat anonymized and genericized version of the code, but the thing that bothered me is still intact. It&#8217;s not really a bug, per&#160;se; the code will function as [...]]]></description>
			<content:encoded><![CDATA[<p>I was looking through some PHP code from a third-party vendor recently, and saw something that made my jaw drop. It&#8217;s pretty innocent-looking, at first. Here&#8217;s a somewhat anonymized and genericized version of the code, but the thing that bothered me is still intact. It&#8217;s not really a bug, <i>per&nbsp;se</i>; the code will function as intended.&nbsp;But&#8230;</p>
<div class="code">$currentRow = 0;<br />
$itemId = &#8220;&#8221;;<br />
$index = 0;<br />
while ($row = mysql_fetch_object($result)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($currentRow == 10) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;renderHeaderRow();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$currentRow = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// takes an itemId and displays relevant columns<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;renderSummaryRow($row->itemId);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$currentRow++;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($index > 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$itemId .=  &#8220;,&#8221;;    # interpolate a comma<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$itemId .= $row->itemId;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$index++;<br />
}</p></div>
<p>See the problem? Really, there are a few ways this can go wrong. To a quick glance, the only clue that line&nbsp;14 (&#8220;interpolate a comma&#8221;) is part of a conditional is its indentation. The indentation is important to a human reader&nbsp;&mdash; but <strong>absolutely irrelevant</strong> to the PHP interpreter, which simply treats the next line after the conditional as the conditional&#8217;s block. Regardless of how it&#8217;s indented, and regardless of what else is&nbsp;around.</p>
<p><em>The way it looks to a human <strong>is not</strong> the way it looks to the&nbsp;machine.</em></p>
<p>What happens if someone wants to add some logging? What if they add it after the comma&nbsp;line?</p>
<div class="code">if ($index > 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$itemId .=  &#8220;,&#8221;;    # interpolate a comma<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLog(&#8220;Added a comma&#8221;);<br />
$itemId .= $row->itemId;<br />
$index++;</div>
<p>Now the log claims the code has added a comma, even when it hasn&#8217;t. But still, it could be worse! What if you decided to add your logging <em>before</em> the other&nbsp;line?</p>
<div class="code">if ($index > 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeLog(&#8220;Adding a comma to itemId&#8230;&#8221;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$itemId .=  &#8220;,&#8221;;    # interpolate a comma<br />
$itemId .= $row->itemId;<br />
$index++;</div>
<p>Now it adds a comma no matter what&nbsp;&mdash; even the first time through the loop, when the string is empty. So instead of a string like <code>'123,124,125'</code>, $itemId will now have a leading comma: <code>',123,124,125'</code>. Since this value is getting stitched into a SQL query later on, it means your app will blow up with a SQL syntax&nbsp;error.</p>
<p>This is why Python makes whitespace significant to program flow. The way the indentation makes it <em>look like</em> the logical structure is, is how the structure <em>actually</em>&nbsp;is.</p>
<p>And this is also why Perl&nbsp;&mdash; of all languages, one that normally errs on the side of letting you leave out anything that can be inferred from context&nbsp;&mdash; <strong>Perl</strong> insists in its syntax documentation that <a href="http://perldoc.perl.org/perlsyn.html#Compound-Statements">in cases like this</a>, &#8220;the curly brackets are <em>required</em>&nbsp;&mdash; no dangling statements allowed.&#8221; (It then says, in typically Perlish fashion: &#8220;If you want to write conditionals without curly brackets there are several other ways to do it.&#8221;)</p>
<p>If you&#8217;re working in one of those languages that lets you omit curly braces around a single-statement conditional&nbsp;&mdash; <strong><em>DON&#8217;T DO IT!</em></strong> The potential maintenance and debugging problems are <em>not</em> worth the fun of saving two keystrokes (or just one, if you work in an editor that auto-closes your braces for&nbsp;you).</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2012/01/30/beware-of-optional-curly-braces-they-will-bite-you/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Problem With Jamie Zawinski and Regular Expressions</title>
		<link>http://kagan.mactane.org/blog/2011/08/16/the-problem-with-jamie-zawinski-and-regular-expressions/</link>
		<comments>http://kagan.mactane.org/blog/2011/08/16/the-problem-with-jamie-zawinski-and-regular-expressions/#comments</comments>
		<pubDate>Wed, 17 Aug 2011 02:43:11 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[opinion]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[snark]]></category>

		<guid isPermaLink="false">http://kagan.mactane.org/blog/?p=351</guid>
		<description><![CDATA[Jamie Zawinski, also known simply as jwz, is famous for his quote: &#8220;Some people, when confronted with a problem, think &#8216;I know, I&#8217;ll use regular expressions.&#8217; Now they have two problems.&#8221; It&#8217;s a very amusing line, and I can totally see why people all over the world are using it in their .sig files: It [...]]]></description>
			<content:encoded><![CDATA[<p>Jamie Zawinski, also known simply as jwz, is famous for his quote: &#8220;Some people, when confronted with a problem, think &lsquo;I know, I&#8217;ll use regular expressions.&rsquo; Now they have two problems.&#8221; It&#8217;s a very amusing line, and I can totally see why people all over the world are using it in their .sig files: It makes them feel better about not being able to understand regexes. And it looks more erudite than just saying, &#8220;Some dude who used to work for Netscape thinks they&#8217;re a bad idea, so I don&#8217;t have to worry about the difference between \w and \S.&#8221;</p>
<p>(It turns out the &#8220;now they have two problems&#8221; construction didn&#8217;t even originate with Jamie; it can be found&nbsp;&mdash; slamming awk instead of regexes&nbsp;&mdash; in the .sig line of a Usenet post in comp.sys.ibm.pc.rt in 1988, and perhaps even further. Maybe this is just another instance of how, as Jamie puts it, people <a href="http://en.wikiquote.org/wiki/Jamie_Zawinski">only remember the stupid stuff</a> he&nbsp;says?)</p>
<p>Anyway, as witty as this line is, it&#8217;s wrong. Like any other tool, regular expressions are designed to solve a particular class of problems. And, like every other tool, there is a class of problems that they&#8217;re just not so good at&nbsp;handling.</p>
<p>Regexes are excellent for doing pattern matching. If you need to ensure that a text string is, for example, a standard ISBN, or a postal code from a particular nation, or a valid username for a given spec. In any of these (fairly standard and common) validation scenarios, regexes will save you a lot of time and&nbsp;trouble.</p>
<p>To take a fairly trivial case, suppose you want to know if a given string is <span class="tooltip" title="For international readers: these are five-digit numbers, optionally followed by a dash and four more digits.">a valid US ZIP code</span>. If you don&#8217;t have regexes available (either because you&#8217;re working in a language that doesn&#8217;t support them, or because you haven&#8217;t learned them), your first thought might&nbsp;be:</p>
<div class="code">if (is_integer($input) &#038;&#038; $input < 100000 &#038;&#038; $input >&nbsp;0)</div>
<p>But this doesn&#8217;t work, because &#8220;2134&#8243; is not a valid ZIP code&nbsp;&mdash; although &#8220;02134&#8243; is, and can be found in Boston. Plus, this conditional won&#8217;t handle 9-digit ZIP codes. What you really need to test for is: &#8220;This string has 5 digits in a row. Then, optionally, it has a dash, and then 4 more&nbsp;digits.&#8221;<span id="more-351"></span></p>
<p>If you don&#8217;t have regexes, this is where you wind up writing something painful&nbsp;like:</p>
<pre class="code">if (strlen(input) != 5 &#038;&#038; strlen(input) != 10) {
    return false;
}
for (int i = 0; i < strlen(input); i++) {
    int ascii = chr(substr(input, i, 1));
    if (i = 5 &#038;&#038; ascii != 45) {
        return false;
    } else if (ascii < 48 || ascii > 57) {
       return false;
    }
}
return&nbsp;true;</pre>
<p>In fact, this isn&#8217;t some contrived example I whipped up to make the process seem more awkward than it has to be. This is the <em>best</em> solution I can find without regexes, and it relies on the fact that &#8220;the digits from 0-9&#8243; is a contiguous range of ASCII characters and values. If I&#8217;d instead wanted to allow &ldquo;upper- and lower-case letters, but <em>not</em> the characters ` [ \ ] or ^&rdquo; (a fairly reasonable restriction), I&#8217;d have to use <em>two non-contiguous</em> ranges, with a corresponding increase in complexity.</p>
<p>Now suppose you&#8217;ve decided usernames must be 4-20 characters long, and may contain letters, numbers, hyphens, underscores, and periods. If you look at this from a user&#8217;s perspective (where details of implementation are irrelevant, and all that matters is the actual functionality), this makes perfect sense: it lets users use all the characters they&#8217;re most likely to want. They can have names like John.Doe, mary_roe, curly-moe and b_hills90210.</p>
<p>But if you look at this as a developer who&#8217;s going to have to implement the filter (without regexes), it looks like a nightmare. &#8220;Do we really have to let them use underscores?&#8221; you whine (after a quick glance at <a href="http://www.ascii-code.com/">the ASCII table</a>). To which the answer is, pretty simply, &#8220;Yes.&#8221; Even people who never heard of an underscore five years ago now expect to be able to use one in their usernames, and if it&#8217;s not allowed, they&#8217;ll perceive your service as cheap and poorly-constructed.</p>
<p>But really, that isn&#8217;t the end of it. Taking a cue from DNS, you should probably ban the &#8220;separator&#8221; characters (period, underscore, and hyphen) from the initial and final spots in usernames. This means your complete function to test for username validity looks like&nbsp;this:</p>
<div class="code">function is_valid_username(input, &#038;errormsg) {<br />
&nbsp;&nbsp;if (strlen(input < 4)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;errormsg = &quot;Too short!&quot;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;if (strlen(input > 20)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;errormsg = &quot;Too long!&quot;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;for (pos = 0; pos < strlen(input); pos++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;char = substr(input, pos, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (pos == 0 || pos == strlen(input)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (! is_letter(char)) &#038;&#038; ! is_digit(char))) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errormsg = &quot;Invalid first/last character: \&quot;&quot; \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;. char . &quot;\&quot;&quot;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (! is_letter(char) &#038;&#038; ! is_digit(char) &#038;&#038; \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;! is_separator(char)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errormsg = &quot;Invalid character: \&quot;&quot; \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;. char . &quot;\&quot;&quot;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return true;<br />
}</p>
<p>function is_digit(char) {<br />
&nbsp;&nbsp;asc = chr(char);<br />
&nbsp;&nbsp;if (48 <= asc &#038;&#038; asc <= 57) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return false;<br />
}<br />
function is_letter(char) {<br />
&nbsp;&nbsp;asc = chr(char);<br />
&nbsp;&nbsp;if ( (65 <= asc &#038;&#038; asc <= 90) || \<br />
&nbsp;&nbsp;(97 <= asc &#038;&#038; asc <= 122) ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return false;<br />
}<br />
function is_separator(char) {<br />
&nbsp;&nbsp;asc = chr(char);<br />
&nbsp;&nbsp;if (asc = 45 || asc = 46 || asc = 95) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return false;<br />
}</div>
<p>If you&#8217;re not familiar with regular expressions, this is where you&#8217;re probably saying something like &#8220;Yeah, that looks like a fairly decent, and reasonably flexible, solution.&#8221; But the regex solution is so simple, it isn&#8217;t even worth its own&nbsp;function:</p>
<pre class="code">if (! matchRE(/^[a-z0-9][a-z0-9_\.-]{2,18}[a-z0-9]$/i, \
input)) {
    errormsg = "Requested username is not valid!";
}</pre>
<p>The people who already do understand regexes, on the other hand, might have noticed how the three <code>is_...()</code> helper functions in my earlier code sample are groping their way towards regular expressions&#8217; character&nbsp;classes.</p>
<div style="margin: 1em 40%; border-bottom: thin groove;"> </div>
<p>If regular expressions are such a powerful and useful tool, why did Jamie Zawinski dis them so harshly? One possible reason is simply &#8220;because he could&#8221;&nbsp;&mdash; the opportunity presented itself, and there&#8217;s a level on which wittiness is its own&nbsp;excuse.</p>
<p>But it&#8217;s also true that no matter how wonderful a tool is, it won&#8217;t give you any help if you use it on the wrong type of&nbsp;problem.</p>
<p>And regexes have been misused an awful lot, on a general class of problem that they&#8217;re simply not equipped to handle: text <em>parsing</em> is a much bigger problem than mere text <em>matching</em>&nbsp;&mdash; but they&#8217;re not completely unrelated; the second is an important part of the first. Which is why lots of people keep trying to parse text (like chunks of HTML) with nothing but&nbsp;regexes.</p>
<p>Which is like trying to build a cabinet with nothing but a screwdriver. A screwdriver is a nice tool, but when you turn it around and use it to drive nails, you&#8217;ll find that it leaves something to be desired. When you try to use it as a plane or an adze, you can&#8217;t help but be frustrated by its inadequacies, and when you need a saw, your screwdriver can&#8217;t even begin to do the&nbsp;job.</p>
<p>Blaming the screwdriver does not help. And deciding that screwdrivers are &#8220;another problem&#8221; and throwing away yours simply leaves you without a screwdriver when the time comes to drive (or remove) a&nbsp;screw.</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2011/08/16/the-problem-with-jamie-zawinski-and-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Developers Are Not QA Testers</title>
		<link>http://kagan.mactane.org/blog/2011/04/08/developers-are-not-qa-testers/</link>
		<comments>http://kagan.mactane.org/blog/2011/04/08/developers-are-not-qa-testers/#comments</comments>
		<pubDate>Fri, 08 Apr 2011 15:18:18 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bad ideas]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[complaining]]></category>
		<category><![CDATA[don't be ridiculous]]></category>
		<category><![CDATA[you fail]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/?p=303</guid>
		<description><![CDATA[When a company says &#8220;we can&#8217;t afford a QA department&#8221;, what they&#8217;re really saying is, &#8220;we accept that our software will be infested with bugs, and quality is not important to us.&#8221; When they compound this basic error by saying, &#8220;the developers will just have to do their own QA&#8221;, they prove that they have [...]]]></description>
			<content:encoded><![CDATA[<p>When a company says &#8220;we can&#8217;t afford a QA department&#8221;, what they&#8217;re really saying is, &#8220;we accept that our software will be infested with bugs, and quality is not important to us.&#8221; When they compound this basic error by saying, &#8220;the developers will just have to do their own QA&#8221;, they prove that <strong>they have no respect for developers <em>or</em> QA people</strong>, and you shouldn&#8217;t work for such a company in either&nbsp;capacity.</p>
<p>(Of course, a company like that isn&#8217;t about to hire any QA testers, so you folks haven&#8217;t got the option of working for them. And I&#8217;m not a QA tester, I&#8217;m a developer. So the rest of my advice is pretty much aimed at fellow devs&nbsp;&mdash; but that doesn&#8217;t mean I don&#8217;t respect you QA folks. Seriously, y&#8217;all deserve a lot more respect than you get, and I love it when you make my life easier by finding my bugs for me.)</p>
<p>The skills, talents, and basic mindset that make a good developer are entirely different from the ones that make a good QA person. Asking one to do the other&#8217;s job is a mistake as fundamental as expecting graphic designers and accountants to swap&nbsp;places. Let me explain:</p>
<p>Developers hate repetition. We hate having to repeat anything more than once or twice; that&#8217;s <a href="http://kai.mactane.org/essays/geek.php">why some of us become developers in the first place</a>: because we can write programs that automate repetitive drudgery, and hence banish it from our&nbsp;lives. <span id="more-303"></span></p>
<p>It&#8217;s not just repetitive <em>tasks</em>, either. A good developer will also have a strong aversion to repetitive code and data. If two procedures differ by only one or two minor details, a good developer will combine them into one, and parameterize it, or otherwise figure out a way for the machine to make the decision. (A poor coder, by contrast, will produce &#8220;copy-and-paste code&#8221; that&#8217;s scorned by more advanced devs, because <strong>it&#8217;s more brittle</strong> as well as being less elegant.) If data must be stored somewhere, the prevailing wisdom is to keep it in just one place&nbsp;&mdash; &#8220;Don&#8217;t Repeat Yourself&#8221; is the&nbsp;maxim.</p>
<p>But <strong>a good QA checker has to deal with repetition</strong> all the time. It&#8217;s a fundamental part of their job. Run this test plan&nbsp;&mdash; this particular series of actions&nbsp;&mdash; over and over again, on each of the platforms we need to test on. Log every step of it, so that if something does go wrong, we can reproduce&nbsp;it.</p>
<p>Then, once the coder has checked in the changes that (hopefully) will fix the bugs you found&#8230; <em>do it all over again</em>. Just the exact same way. On every&nbsp;platform.</p>
<p>This is a coder&#8217;s nightmare. A developer&#8217;s natural response to this situation is to write some kind of automated QA-testing software. Of course, that software itself will then have to be checked for bugs before you could possibly rely on it&#8230; and you can&#8217;t trust your software to find its own&nbsp;bugs.</p>
<p>In the absence of such software, asking a developer to do bug-checking means asking them to do something that goes against their very nature. You can force them to do it, but they&#8217;ll never be really good at&nbsp;it.</p>
<p>Of course, all of that is totally leaving aside the obvious point that asking someone to find the flaws in their own work means asking them to see past their own mental blind spots&nbsp;&mdash; a developer&#8217;s test plan will be based on the same assumptions as their code, without the coder even realizing it. And that&#8217;s to say nothing of the obvious implications once you put the human ego into the picture. Human beings are flawed, and too many see &#8220;find the bugs in your own code&#8221; as equivalent to &#8220;show me all the ways you&nbsp;suck&#8221;.</p>
<p>Don&#8217;t ask a developer to do QA. <strong>Development and QA testing are fundamentally different disciplines</strong>, with completely different skill-sets and personality requirements. A developer can no more cover for a QA tester than a QA tester could fill in for a developer.</p>
<p>And if you&#8217;re a developer at a company that wants you to do your own bug-checking? Your employer thinks what you do is so similar to bug-checking that you&#8217;ll be good at it. They think they can cut corners on quality, <em>and</em> pay you one salary to do two jobs at once (including one job that <em>it is in your nature to hate</em>). If you can&#8217;t make them see reason, it&#8217;s time to brush up your&nbsp;r&eacute;sum&eacute;.</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2011/04/08/developers-are-not-qa-testers/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Silly Coding Tricks: &#8220;Inverted&#8221; String Match</title>
		<link>http://kagan.mactane.org/blog/2010/02/13/silly-coding-tricks-inverted-string-match/</link>
		<comments>http://kagan.mactane.org/blog/2010/02/13/silly-coding-tricks-inverted-string-match/#comments</comments>
		<pubDate>Sat, 13 Feb 2010 23:20:19 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bad ideas]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[silly coding tricks]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/?p=187</guid>
		<description><![CDATA[First things first: Never actually do this. This is just a fun curiosity, for amusement value only. Because of the way JavaScript&#8217;s search() method works, you can do: var my_url = &apos;http://kai.mactane.org&apos;; if (! my_url.search(/http:\/\/kai.mactane.org/)) { &#160;&#160;&#160;&#160;alert(&#34;Your URL is http://kai.mactane.org&#34;); } else { &#160;&#160;&#160;&#160;alert(&#34;Nope, your URL isn&apos;t http://kai.mactane.org&#34;); } Try running this, and it will [...]]]></description>
			<content:encoded><![CDATA[<p>First things first: <strong>Never actually do this.</strong> This is just a fun curiosity, for amusement value only.</p>
<p>Because of the way JavaScript&#8217;s <code>search()</code> method works, you can do:</p>
<div class="code">var my_url = &apos;http://kai.mactane.org&apos;;<br />
if (! my_url.search(/http:\/\/kai.mactane.org/)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;alert(&quot;Your URL is http://kai.mactane.org&quot;);<br />
} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;alert(&quot;Nope, your URL isn&apos;t http://kai.mactane.org&quot;);<br />
}</div>
<p>Try running this, and it will correctly claim that &#8220;Your URL is http://kai.mactane.org&#8221;&nbsp;&mdash; even though there&#8217;s a ! at the beginning of the test. What gives?</p>
<p>That exclamation point <em>looks</em> like it&#8217;s saying: &#8220;If the search for <code>http://kai.mactane.org</code> within the given string fails&#8230;&#8221; But what that test is <em>really</em> saying is: &#8220;If the search for <code>http://kai.mactane.org</code> returns Boolean false&nbsp;&mdash; or <em>anything that evaluates as</em> Boolean false&#8230;&#8221;</p>
<p>What that <code>search()</code> call actually returns is <em>the character index</em> at which the needle is found, or -1 if it isn&#8217;t found. Since the needle is found at the very beginning of the haystack&nbsp;&mdash; at character index 0&nbsp;&mdash; <code>search()</code> returns zero. Which evaluates to false in a Boolean context.</p>
<p>If the needle isn&#8217;t found, the search will return -1&nbsp;&mdash; which <em>evaluates as true</em> in a Boolean context!</p>
<p>Effectively, the ! operator is reconciling the disagreement between <code>search()</code>&rsquo;s idea of falsehood and <code>if()</code>&rsquo;s idea of falsehood: <code>search()</code> returns -1 for a false result (a failed match), but <code>if()</code> considers -1 to be true, not false.</p>
<p>This trick only works with needles that you are sure will only occur at the beginning of the haystack.</p>
<p>Once again, you should <em>never, ever <strong>actually</strong> write code like this</em> for any serious purpose or in anything you intend to deploy in the real world. The maintainability problems are not worth the amusement of confusing all your co-workers with a test that looks reversed. Instead, use a positional anchor in your regex, and explicitly test against -1&#8230;</p>
<div class="code">if (my_url.search(/^http:\/\/kai.mactane.org) != -1)</div>
<p>&#8230;like any sensible JavaScript coder. (Yes, that last bit is intended to be ironic. I wrote something once about the silliness of having to add that extra test; I&#8217;ll have to see if I can find it and republish it here. )</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2010/02/13/silly-coding-tricks-inverted-string-match/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Testing Backward-Incompatible Changes</title>
		<link>http://kagan.mactane.org/blog/2009/10/12/testing-backward-incompatible-changes/</link>
		<comments>http://kagan.mactane.org/blog/2009/10/12/testing-backward-incompatible-changes/#comments</comments>
		<pubDate>Mon, 12 Oct 2009 18:06:20 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/?p=135</guid>
		<description><![CDATA[Let&#8217;s say one day you decide to add a feature to your software or service. For example, you need a new flag on user accounts, so that different types of users get different features. (These don&#8217;t even have to be tiered account levels; maybe accounts of type &#8220;music lover&#8221; get a widget in the sidebar [...]]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s say one day you decide to add a feature to your software or service. For example, you need a new flag on user accounts, so that different types of users get different features. (These don&#8217;t even have to be tiered account levels; maybe accounts of type &#8220;music lover&#8221; get a widget in the sidebar with suggestions for bands they might like while &#8220;sports fan&#8221; accounts get a sports scores widget instead.)</p>
<p>So, following good software development processes, you first write a couple of tests:</p>
<p><span id="more-135"></span>UserTypeOne gets features A, B, and C<br />
UserTypeOne does not get features D, E, or F<br />
UserTypeTwo does not get features A, B, or C<br />
UserTypeTwo gets features D, E, and F</p>
<p>Then you write some code that meets these tests, including creating a pair of mock users with the new flag set in both possible ways. You run the tests, and they all pass. Yay! Your new feature is ready!</p>
<p>Except that, of course, the moment you check this code in, you find that suddenly, <em>nobody can log in any more</em>. After all, the new flag is supposed to be mandatory, and that requirement is reflected in one of the changes you made to the login code. But the only users that have the NewFlag at all are the ones you just created in your fixture.</p>
<p>Sure, this will probably get caught before it goes live. Even if your entire QA department is asleep at the switch, if just one person in your organization tries to run even the most rudimentary tests, they&#8217;ll get back an error message. But &#8220;it didn&#8217;t get pushed to production&#8221; is only the most minimal level of success; I&#8217;d like to look at how <em>you</em>, the coder, could have prevented this problem. How could you keep this error from even getting checked in (and thus breaking the build and flat-lining your co-workers&#8217; productivity for a few hours)?</p>
<p>The way a real QA department would spot this problem is regression testing. But it&#8217;s not reasonable to ask every coder to do a full suite of regression tests before every commit.</p>
<p>The real failure, of course, is that you made a change that you didn&#8217;t test. But saying &#8220;force programmers to write better tests&#8221; is not a viable answer; people are imperfect. You might as well mandate &#8220;developers may not check in any code that contains bugs&#8221; as one of your organization&#8217;s rules.</p>
<p>I don&#8217;t have a good answer to this problem. Asking developers to also be QA people is completely impractical. The current solution seems to be &#8220;developers sometimes check in code which breaks the build, and blocks people&#8217;s productivity for a while&#8221;. (Distributed SCM can help mitigate this.)</p>
<p>Is there something QA people know that developers don&#8217;t, that would make an easy solution that we could adopt?</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2009/10/12/testing-backward-incompatible-changes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>What Characters Are Allowed in Twitter Usernames</title>
		<link>http://kagan.mactane.org/blog/2009/09/22/what-characters-are-allowed-in-twitter-usernames/</link>
		<comments>http://kagan.mactane.org/blog/2009/09/22/what-characters-are-allowed-in-twitter-usernames/#comments</comments>
		<pubDate>Tue, 22 Sep 2009 21:12:05 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[the world-wide conversation]]></category>
		<category><![CDATA[Twitter]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/2009/09/22/what-characters-are-allowed-in-twitter-usernames/</guid>
		<description><![CDATA[A while back, when I was writing Hummingbird, I needed to look for Twitter usernames in various strings. More recently, I&#8217;m doing some work that involves Twitter at my new job. Once again, I need to find and match on Twitter usernames. Luckily, this time, Twitter seems to have updated its signup page with some [...]]]></description>
			<content:encoded><![CDATA[<p>A while back, when I was writing Hummingbird, I needed to look for Twitter usernames in various strings. More recently, I&#8217;m doing some work that involves Twitter at my new job. Once again, I need to find and match on Twitter usernames.</p>
<p>Luckily, this time, Twitter seems to have updated its signup page with some nice AJAX that constrains the user&#8217;s options, and provides helpful feedback. So, for anyone else who needs this information in the future, here&#8217;s the scoop:</p>
<ol>
<li>Letters, numbers, and underscores only. It&#8217;s case-blind, so you can enter <code>hi_there</code>, <code>Hi_There</code>, or <code>HI_THERE</code> and they&#8217;ll all work the same (and be treated as a single account).</li>
<li>There is apparently no minimum-length requirement; <a href="https://twitter.com/a">the user a exists on Twitter</a>. Maximum length is 15 characters.</li>
<li>There is also no requirement that the name contain letters at all; <a href="https://twitter.com/69">the user 69</a> exists, as does a user <a href="https://twitter.com/____">whose name I can&#8217;t pronounce</a>.</li>
</ol>
<p>If you want a regex to match on this, <code>/[a-zA-Z0-9_]{1,15}/</code> would be nice and safe for use in both POSIX and Perl-style regex syntax. (If you&#8217;ve got Perl-compatible regexes, <code>/\w{1,15}/</code> is quick and easy.)</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2009/09/22/what-characters-are-allowed-in-twitter-usernames/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Hummingbird Updated to Version 0.60</title>
		<link>http://kagan.mactane.org/blog/2009/08/20/hummingbird-updated-to-version-0-60/</link>
		<comments>http://kagan.mactane.org/blog/2009/08/20/hummingbird-updated-to-version-0-60/#comments</comments>
		<pubDate>Fri, 21 Aug 2009 02:49:27 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[announcements]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[Hummingbird]]></category>
		<category><![CDATA[my projects]]></category>
		<category><![CDATA[oops]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[should have known better]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[Twitter]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/?p=109</guid>
		<description><![CDATA[I&#8217;ve always questioned the wisdom of building a startup company based around someone else&#8217;s platform, like Facebook games or Gmail inbox add-ons. You&#8217;re totally at the mercy of the other company. (Many people have found out how silly it was to go up against Microsoft or Apple in just the same way.) And yet, here [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve always questioned the wisdom of building a startup company based around someone else&#8217;s platform, like Facebook games or Gmail inbox add-ons. You&#8217;re totally at the mercy of the other company. (Many people have found out how silly it was to go up against Microsoft or Apple in just the same way.)</p>
<p>And yet, here I am with <a href="/software/hummingbird/">Hummingbird</a>, which is totally dependent on Twitter&#8217;s bandwidth. (In my own defense, I can only point out that: a) I wrote it because I needed the functionality; and b) I&#8217;m not building a money-making company around Hummingbird. I&#8217;m just giving it away.)</p>
<p>In the past couple of weeks, I&#8217;ve noticed that Twitter can sometimes take an astonishingly long time to provide an update. Hummingbird works by requesting URLs like <a href="http://twitter.com/statuses/user_timeline/kmactane.xml?count=25">http://twitter.com/statuses/user_timeline/kmactane.xml?count=25</a>, and (up until now) it worked on the assumption that Twitter couldn&#8217;t possibly take longer than 10 or 15 seconds to respond to such a request.</p>
<p>That turns out not to be the case.</p>
<p>In fact, my recent tests have shown that Twitter can sometimes take <em>over 5 full minutes</em> to finish responding to such a request. This is a problem, because the default installation of Hummingbird tries to update its cached data once every 5 minutes. And since I assumed the request would be fulfilled in, at most, 10% of that time, I didn&#8217;t bother building in any concurrency checking.</p>
<p>I&#8217;ve just completely reorganized most of Hummingbird&#8217;s architecture. The interface that a blog owner sees (in the sense of the configuration variables in <code>hummingbird.php</code>, and the calling syntax for blog pages and cron jobs) is still the same. However, all of the code for retrieving data from Twitter has been spun off into a new <code>hummingbird-cache.php</code> file, which can be launched into the background by the rest of Hummingbird, so that it can patiently take as long as it needs to in order to update the blog&#8217;s tweets cache.</p>
<h3>The Major Change This Causes</h3>
<p>In Hummingbird&#8217;s previous incarnations, it would freshen an out-of-date cache before displaying it. The assumption was that every once in a while, someone might have to wait a few more seconds before seeing your blog page. However, everyone would always see an up-to-date record of your tweets. (Where &#8220;up-to-date&#8221; means &#8220;no more than 5 minutes old, absolute tops&#8221;. If you tweet so relentlessly that that&#8217;s a problem, you probably don&#8217;t have enough time to keep up your blog&#8230;)</p>
<p>Now that we know that Twitter can take over 5 minutes to give you the data needed to freshen your tweets cache, that design is not acceptable. Instead, Hummingbird is now committed to showing the contents of your tweets cache as quickly as possible, and <em>only then</em> does it fork the cache-update process into the background.</p>
<p>This means that, <em>if you&#8217;re not using cron</em> or some other automatic job-scheduling facility to run Hummingbird every 5 minutes, visitors who arrive at your blog after a period of inactivity will see an out-of-date, stale listing of your tweets. </p>
<p>If you have a cron job set to keep Hummingbird fresh, you&#8217;ll be fine. (A page view will also trigger an update, so if you get so much traffic that there&#8217;s never a 5-minute period during which your page doesn&#8217;t get hit, you&#8217;ll also be fine.)</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2009/08/20/hummingbird-updated-to-version-0-60/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Notes on LJ Content Sieve</title>
		<link>http://kagan.mactane.org/blog/2009/07/04/notes-on-lj-content-sieve/</link>
		<comments>http://kagan.mactane.org/blog/2009/07/04/notes-on-lj-content-sieve/#comments</comments>
		<pubDate>Sat, 04 Jul 2009 21:07:42 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[my projects]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/?p=78</guid>
		<description><![CDATA[My latest project is something I call &#8220;LJ Content Sieve&#8221;: a Greasemonkey script to filter out content on one&#8217;s Livejournal views based on nearly any attribute of a post or comment. However, Livejournal is very customizable. It has 31 different &#8220;layouts&#8221;, each of which can then be further &#8220;themed&#8221; by application of CSS. This means [...]]]></description>
			<content:encoded><![CDATA[<p>My latest project is something I call &#8220;LJ Content Sieve&#8221;: a Greasemonkey script to filter out content on one&#8217;s Livejournal views based on nearly any attribute of a post or comment.</p>
<p>However, Livejournal is very customizable. It has 31 different &#8220;layouts&#8221;, each of which can then be further &#8220;themed&#8221; by application of CSS. This means that a user viewing a given journal, community, or single post, at a given time, may receive an HTML document containing any of 31 different DOM structures.</p>
<p>This means that finding a post&#8217;s author, or title, or even just a single post or comment, is not a straightforward process. I&#8217;ve set myself up a Ruby script that downloaded my &#8220;friends&#8221; view, a representative entry with lots of comments, and my own journal view, thus giving me a set of 93 fixtures that I can test against. The Ruby script also tweaked each fixture in the process of spooling it to my hard drive, by adding the proper calls to JSUnit files.</p>
<p>At the end of each document&#8217;s &lt;head&gt; element, there&#8217;s a call to <code>jsUnitCore.js</code>. Then, at the end of each &lt;body&gt;, I add a call to the <code>lj-content-sieve.user.js</code> script itself, as well as a set of test files that depends on which fixture this is. Every fixture gets an <code>ljcs-global-tests.js</code> call added to it&nbsp;&mdash; that file contains tests that should work anywhere, regardless of what sort of page you&#8217;re on.</p>
<p>Then all the &#8220;friends&#8221; page fixtures get an <code>ljcs-friends-tests.js</code> file, which tests operations that should happen on every friends page. For example, determining which entries need to be deleted. (In contrast, single-entry pages get <code>ljcs-entry-tests.js</code>, and the page from my own journal&nbsp;&mdash; which stands in for a view of a community&nbsp;&mdash; gets <code>ljcs-self-tests.js</code>.)</p>
<p>Finally, each fixture page gets a test file based on its layout: the &#8220;3 Column&#8221; layout gets <code>ljcs-3&nbsp;Column-tests.js</code>, while &#8220;Cuteness Attack&#8221; gets <code>ljcs-Cuteness&nbsp;Attack-tests.js</code>. (Hey, I didn&#8217;t write or name the layouts; I just have to make sure LJCS works with all of &#8216;em.) These files will test that the actual DOM manipulations work properly.</p>
<p>Without test-driven development and automated testing to ensure that each layout and page-type is being handled properly, I don&#8217;t think this project would be manageable at all.</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2009/07/04/notes-on-lj-content-sieve/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Would Shlemiel the Painter Optimize Prematurely?</title>
		<link>http://kagan.mactane.org/blog/2009/06/18/would-shlemiel-the-painter-optimize-prematurely/</link>
		<comments>http://kagan.mactane.org/blog/2009/06/18/would-shlemiel-the-painter-optimize-prematurely/#comments</comments>
		<pubDate>Thu, 18 Jun 2009 21:34:51 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[algorithms]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[Big O notation]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[diary]]></category>
		<category><![CDATA[my projects]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/?p=73</guid>
		<description><![CDATA[I don&#8217;t want to optimize this code prematurely. And &#8220;while you&#8217;re still writing it&#8221; is probably premature. On the other hand, totally ignoring algorithmic complexity is a sure route to a Shlemiel the Painter&#8217;s algorithm. Do I really want to just write the whole thing, and then start profiling it to see where the hot [...]]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t want to <a href="http://shreevatsa.wordpress.com/2008/05/16/premature-optimization-is-the-root-of-all-evil/">optimize this code prematurely</a>. And &#8220;while you&#8217;re still writing it&#8221; is probably premature. On the other hand, totally ignoring algorithmic complexity is a sure route to a <a href="http://www.joelonsoftware.com/articles/fog0000000319.html">Shlemiel the Painter&#8217;s algorithm</a>.</p>
<p>Do I really want to just write the whole thing, and then start profiling it to see where the hot spots are, and then possibly have to re-design the whole thing? That seems like the complete opposite of &#8220;work smarter, not harder&#8221;. Then again, it doesn&#8217;t matter if you write an <span class="mathVar">O(<i>n</i><sup>2</sup>)</span> or even <span class="mathVar">O(<i>n</i>!)</span> algorithm if <i>n</i> is always going to be small&#8230; and in this application, I expect low-to-middling <i>n</i> values.</p>
<p>Of course, even if <i>n</i> will always be small, and increasing CPU power is my friend&#8230; even if it performs fast enough to make users happy, I&#8217;ll still know there&#8217;s a problem down there in the details. That may be the deciding factor.</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2009/06/18/would-shlemiel-the-painter-optimize-prematurely/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>TDD and Peace of Mind</title>
		<link>http://kagan.mactane.org/blog/2009/05/04/tdd-and-peace-of-mind/</link>
		<comments>http://kagan.mactane.org/blog/2009/05/04/tdd-and-peace-of-mind/#comments</comments>
		<pubDate>Mon, 04 May 2009 16:53:10 +0000</pubDate>
		<dc:creator>Kai MacTane</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[my projects]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://kai.mactane.org/blog/2009/05/04/tdd-and-peace-of-mind/</guid>
		<description><![CDATA[Let&#8217;s face it, we&#8217;re not perfect. As much as I might realize that automated testing is a good practice, it still feels like a chore sometimes. In my latest round of personal-project development, just setting up a decent set of test fixtures and a working test framework turned into something of a hassle, as it&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s face it, we&#8217;re not perfect. As much as I might realize that automated testing is a good practice, it still <em>feels</em> like a chore sometimes. In my latest round of personal-project development, just setting up a decent set of test fixtures and a working test framework turned into something of a hassle, as it&#8217;s my first attempt both at Greasemonkey scripting <em>and</em> at building a script that will act on Livejournal pages. (Since LJ users can customize their views with any of 36 basic &#8220;styles&#8221;, this means quite a few fixtures.)</p>
<p>So it was awfully tempting to say &#8220;screw this!&#8221; and just start writing code&nbsp;&mdash; you know, &#8220;code that works, code that gets stuff done&#8221;. Actual <em>program logic</em>, instead of testing tools. &#8220;Why don&#8217;t I just get something built to begin with,&#8221; I asked myself, &#8220;and then I can try to test that?&#8221;</p>
<p>Ha, ha. Of course, we all know that starting off without any tests just makes it easier to continue without them later. So I took the virtuous road, forced myself to get tests working, but allowed myself to skimp by only setting up fixtures for 3 of the 36 styles. The other styles are something I will <em>have to</em> go back and fill in before I can release, so there&#8217;s no chance I&#8217;ll &#8220;give myself permission&#8221; to blow them off.</p>
<p>When you really want to be writing code (because let&#8217;s face it, you consider yourself a coder, not a tester), it&#8217;s pretty annoying to write tests instead. But writing tests is at least a form of writing; if you&#8217;re not even writing tests, but rather setting up a test framework and fixtures, that&#8217;s almost excruciating.</p>
<p>But it all became worth it the moment I made a change in one of my basic data structures. I had a structure (what a Perlist would call a hash of arrays) that held information about how to identify which LJ style the page uses. Then I realized I didn&#8217;t want to have a separate structure for how to manipulate the page; instead, all information about LJ&#8217;s styles and DOM structure should live in one place.</p>
<p>So I altered the structure, then altered the functions created so far that rely on it. And that&#8217;s where I would then have to wonder, &#8220;How badly did I break everything?&#8221; Instead, I just ran my JSunit tests again. And seeing them pass was instant peace of mind. I don&#8217;t have to worry that there&#8217;s some hidden flaw waiting there, ready to be exposed by a user doing something unexpected. And as I add the other styles, I can easily be sure my code works for them, as well.</p>
<p>I&#8217;m still fairly early in the development of this code, and while unit testing has cost me a bit of time, it&#8217;s given me back <em>peace of mind</em>. The time profit? I have no doubt that will come later. (And had I made any errors in my data-structure change, the unit tests would have helped me find them more quickly than the usual debugging methods.)</p>
]]></content:encoded>
			<wfw:commentRss>http://kagan.mactane.org/blog/2009/05/04/tdd-and-peace-of-mind/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

