<?xml version='1.0' encoding='utf-8' ?>
<!--  If you are running a bot please visit this policy page outlining rules you must respect. http://www.livejournal.com/bots/  -->
<rss version='2.0' xmlns:lj='http://www.livejournal.org/rss/lj/1.0/' xmlns:media='http://search.yahoo.com/mrss/' xmlns:atom10='http://www.w3.org/2005/Atom'>
<channel>
  <title>Give me code or give me death...</title>
  <link>http://natrusso.livejournal.com/</link>
  <description>Give me code or give me death... - LiveJournal.com</description>
  <lastBuildDate>Wed, 20 Aug 2008 17:15:23 GMT</lastBuildDate>
  <generator>LiveJournal / LiveJournal.com</generator>
  <lj:journal>natrusso</lj:journal>
  <lj:journalid>16208627</lj:journalid>
  <lj:journaltype>personal</lj:journaltype>
  <atom10:link rel='hub' href='http://pubsubhubbub.appspot.com/' />
  <image>
    <url>http://l-userpic.livejournal.com/77630706/16208627</url>
    <title>Give me code or give me death...</title>
    <link>http://natrusso.livejournal.com/</link>
    <width>75</width>
    <height>100</height>
  </image>

<item>
  <guid isPermaLink='true'>http://natrusso.livejournal.com/1307.html</guid>
  <pubDate>Wed, 20 Aug 2008 17:15:23 GMT</pubDate>
  <title>Recursively Delete a Directory Structure</title>
  <link>http://natrusso.livejournal.com/1307.html</link>
  <description>So you want to delete a directory programmatically, but you&apos;re not quite sure just how to do it?&amp;nbsp; You&apos;re vaguely aware that the process requires this nasty thing called &quot;Recursion&quot;, but the very act of uttering the word causes a negative physical reaction?&amp;nbsp; Don&apos;t sweat it.&amp;nbsp; We&apos;ve all been there.&lt;br /&gt;&lt;br /&gt;Yes, recursion can sometimes be a difficult topic to wrap your mind around.&amp;nbsp; But the process of deleting a directory (with all of its subdirectories) is actually a great place to start the learning process.&amp;nbsp; It&apos;s a great place because it&apos;s a concept that we&apos;re all already familiar with.&amp;nbsp; I mean, we must delete umpteen directories every week, right?&amp;nbsp; So we&apos;re not going to have to try and grapple with the problem domain &lt;i&gt;and&lt;/i&gt; the algorithm at the same time.&lt;br /&gt;&lt;br /&gt;There are many definitions of the general term &quot;Recursion&quot; (often having to do with a mathematical operation being defined in terms of itself).&amp;nbsp; In Computer Science, however, we get a little more specific than that.&amp;nbsp; Recursion, to us programmers, describes a situation where a function &lt;i&gt;calls itself&lt;/i&gt;.&amp;nbsp; There are some problems (like traversing all of the nodes in a tree, or deleting a directory structure) that are &lt;i&gt;extremely&lt;/i&gt; easy to solve within the context of a function that can call itself.&amp;nbsp; Those same problems, however, are often painfully difficult to solve (or horribly inefficient) if another approach is taken.&lt;br /&gt;&lt;br /&gt;The following code is an example (a &lt;b&gt;&lt;i&gt;horrible &lt;/i&gt;&lt;/b&gt;example) of a recursive function:&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://img.photobucket.com/albums/v398/Mujahid/Blogging/Recursion/Merrily.jpg&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;&lt;br /&gt;That function does exactly what it implies:&amp;nbsp; It merrily goes nowhere....&lt;i&gt;forever&lt;/i&gt;.&amp;nbsp; This is obviously a problem, right?&amp;nbsp; A single call to MerrilyGoNowhere() will bring the walls of reality shattering down around us, leaving our program as little more than a steaming pile of hot rubbish.&amp;nbsp; Well, maybe not that drastic, but at the very least we&apos;ve just entered the shadowy-realm of the non-terminating loop.&amp;nbsp; It&apos;s a place we don&apos;t want to be.&lt;br /&gt;&lt;br /&gt;The problem is that it&apos;s a place you might frequent until you get over the initial learning curve of recursion.&amp;nbsp; There must be a step, somewhere in that function, that steps us &lt;i&gt;back out&lt;/i&gt; of the recursion and up the call stack.&amp;nbsp; Let&apos;s refactor a little bit.&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://img.photobucket.com/albums/v398/Mujahid/Blogging/Recursion/MerrilyRefactored.jpg&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;&lt;br /&gt;The key to recursion is to only recurse &lt;i&gt;conditionally&lt;/i&gt;.&amp;nbsp; Some set of circumstances &lt;i&gt;must&lt;/i&gt; be true in order for you to take the recursive step.&amp;nbsp; Otherwise, you step out of the function, which steps back up the call stack and stops your function from creating a non-terminating loop.&amp;nbsp; The &quot;Else&quot; block above isn&apos;t technically required, but it demonstrates that you can do some other processing if your recursion condition isn&apos;t met.&lt;br /&gt;&lt;br /&gt;Take a quick walk through the code above, step by step:&lt;br /&gt;We launch our program, and a call to MerrilyGoNowhere() takes place.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;On this first pass, let&apos;s say that WeHaveEnoughEnergy() returns &quot;true&quot;, so we call MerrilyGoNowhere() again.&lt;/li&gt;&lt;li&gt;On the 2nd pass, WeHaveEnoughEnergy() returns &quot;true&quot; again, so we call MerrilyGoNowhere() Again.&lt;/li&gt;&lt;li&gt;On the 3rd pass, however, let&apos;s say that WeHaveEnoughEnergy() returns &quot;false&quot;.&amp;nbsp; In this case, we skip the recursive step and go straight to &quot;GetSomeRest&quot;.&lt;/li&gt;&lt;li&gt;The function then returns up the call stack to the previous call (the 2nd call) to MerrilyGoNowhere().&amp;nbsp; However, no further processing is required in that function, so we return up the call stack to the previous call (the 1st call) to MerrilyGoNowhere().&amp;nbsp; &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Once again, we find that no further processing is required, so we return up the call stack once more.&amp;nbsp; On this return, we&apos;ve completely left MerrilyGoNowhere() behind, and our program can continue or shut down gracefully, having avoided a non-terminating loop.&lt;/li&gt;&lt;/ol&gt;Those are the basics of recursion, and those basics rarely change even for the most complex problems.&amp;nbsp; Let&apos;s apply what we&apos;ve learned here to the process of deleting an entire directory tree.&lt;br /&gt;&lt;br /&gt;First, some groundwork.&amp;nbsp; We &lt;i&gt;know&lt;/i&gt; that in order to delete a directory structure, we&apos;re obviously going to need to delete the &lt;i&gt;files&lt;/i&gt; in those directories, right?&amp;nbsp; In fact, programmatically, we need to delete the files prior to attempting to delete the directory.&amp;nbsp; Let&apos;s write a simple function that handles deleting a file:&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;http://img.photobucket.com/albums/v398/Mujahid/Blogging/Recursion/DeleteFile.jpg&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;&lt;br /&gt;As you can see it&apos;s a fairly simple program.&amp;nbsp; It merely sets the attributes of a given file (Element) to FileAttributes.Normal and then attempts to delete that file.&amp;nbsp; I&apos;ve written the code in such a way that if an exception occurs, the function returns false.&amp;nbsp; Otherwise, we return true.&amp;nbsp; You don&apos;t &lt;i&gt;have&lt;/i&gt; to make a separate function to handle file deletions.&amp;nbsp; I just did so to clean up the code you&apos;ll see below.&lt;br /&gt;&lt;br /&gt;Ok, so we have a function that will delete a file that we pass to it.&amp;nbsp; So how do we delete the directory structure?&amp;nbsp; Well, recursively, of course!&amp;nbsp; Let&apos;s look at the following code:&lt;br /&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; src=&quot;http://img.photobucket.com/albums/v398/Mujahid/Blogging/Recursion/DeleteDirectory3.jpg&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Here we can see that recursion is our friend once again.&amp;nbsp; The function takes one parameter &quot;directoryPath&quot;, which is the fully-qualified path to the directory we want to delete.&lt;br /&gt;&lt;br /&gt;Let&apos;s walk through the code:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;We call &quot;DeleteDirectory()&quot; and pass it a path to the directory we want to delete.&lt;/li&gt;&lt;li&gt;We check to see if that directory actually exists.&amp;nbsp; If it does not, we display an error message.&lt;/li&gt;&lt;li&gt;Get a list of all of the elements in the directory.&lt;/li&gt;&lt;li&gt;Begin looping through those elements.&amp;nbsp; If we find that the current element we are looping through &lt;i&gt;is itself a directory&lt;/i&gt;, then we call DeleteDirectory once more, passing it that element (which we now know to be a path to a subdirectory).&lt;/li&gt;&lt;li&gt;This process continues until we find an element that is actually a file.&amp;nbsp; When we find a file, we simply make a call to DeleteFile(), passing it the file we just discovered.&lt;/li&gt;&lt;li&gt;When no additional files or directories are found, the function returns up the call stack.&lt;/li&gt;&lt;li&gt;The next line of code deletes the now-empty directory.&lt;/li&gt;&lt;/ol&gt;There is some additional refactoring that I&apos;d love to do to that function (I don&apos;t like some of the names I&apos;ve chosen for methods/variables, etc.), but that&apos;s the algorithm in a nutshell!</description>
  <comments>http://natrusso.livejournal.com/1307.html</comments>
  <category>recursion</category>
  <category>c#</category>
  <category>algorithms</category>
  <lj:mood>tired</lj:mood>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://natrusso.livejournal.com/1062.html</guid>
  <pubDate>Wed, 13 Aug 2008 19:35:10 GMT</pubDate>
  <title>Don&apos;t Forget to Flush!</title>
  <link>http://natrusso.livejournal.com/1062.html</link>
  <description>I saw something new today that I haven&apos;t seen since my C++ days:&amp;nbsp; a stream that needed to be flushed.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;I have to admit, this one boggled me for about 15 minutes before I realized what was going on.&amp;nbsp; Then I caught a mistake I haven&apos;t made in years:&amp;nbsp; I forgot to close an open StreamWriter.&lt;br /&gt;&lt;br /&gt;The function is a simple one.&amp;nbsp; It iterates through decorated methods in my Program.cs file, finds the command name through reflection (thanks to a colleague of mine), and streams the command name to a text file for later review (after some collection manipulation and sorting).&amp;nbsp; Here was the original method:&lt;br /&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; src=&quot;http://img.photobucket.com/albums/v398/Mujahid/Blogging/DumpCommands1.gif&quot; /&gt;&lt;br /&gt;&lt;br /&gt;As you can see, it looks pretty innocent.&amp;nbsp; Except for one small problem:&amp;nbsp; In a collection of 60 strings, only 44 1/2 of the strings were being streamed!&amp;nbsp; Yes, you saw that correctly.&amp;nbsp; Only 44.5 of the 60 total strings were being written to the text file.&amp;nbsp; Then I had a C++ flashback that threw me into a post traumatic stress episode...&lt;br /&gt;&lt;br /&gt;Back in C++ we had to routinely &quot;flush&quot; our streams to make sure all the data made it safely to its destination.&amp;nbsp; I don&apos;t ever recall having to explicitly flush ANYTHING in C# or, dare I say it, VB.Net (&amp;lt;gasp&amp;gt;)!&amp;nbsp; Then I looked back at my code and slapped myself square on the face.&lt;br /&gt;&lt;br /&gt;I forgot to close the stupid stream :(&amp;nbsp;&amp;nbsp; 15 minutes down the toilet because I forgot to flush (pun intended).&amp;nbsp; In C#, making a call to StreamWriter.Close() causes an implicit flush to take place.&amp;nbsp; No, I don&apos;t know or understand the details yet.&amp;nbsp; I just know it happens.&lt;br /&gt;&lt;br /&gt;With a single line of code:&amp;nbsp; sw.Close();&amp;nbsp; I was able to successfully stream all of my commands to the text file.&amp;nbsp; Here is the finished product:&lt;br /&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; src=&quot;http://img.photobucket.com/albums/v398/Mujahid/Blogging/DumpCommands2.gif&quot; /&gt;&lt;br /&gt;&lt;br /&gt;Not much of a difference in implementation, huh?&amp;nbsp; But a HUGE difference in effect.</description>
  <comments>http://natrusso.livejournal.com/1062.html</comments>
  <category>streams</category>
  <category>file i/o</category>
  <category>c#</category>
  <lj:music>The sound of compiling code...</lj:music>
  <media:title type="plain">The sound of compiling code...</media:title>
  <lj:mood>energetic</lj:mood>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://natrusso.livejournal.com/883.html</guid>
  <pubDate>Mon, 04 Aug 2008 21:05:38 GMT</pubDate>
  <title>VSMDI...Shmee-SMDI</title>
  <link>http://natrusso.livejournal.com/883.html</link>
  <description>In our day-to-day development efforts we often find ourselves confronted with an issue screaming to be dealt with by excessive force.  Anyone who has spent &lt;span style=&quot;font-style: italic;&quot;&gt;any&lt;/span&gt; time with Microsoft&apos;s VSMDI file can empathize.  If you have more than one developer on the project, you are guaranteed to run into the dreaded &quot;multiple VSMDI file&quot; issue.  It often happens with only one developer, for that matter.&lt;br /&gt;&lt;br /&gt;From an application developers perspective, this isn&apos;t much of a problem to have to deal with in many circumstances.  From an automations perspective, however, it can quickly become a nightmare.  In automations, we need to be able to predict the environment we&apos;re executing in, or at least be able to establish a certain state that we can rely on.  The VSMDI file allows neither of those solutions to work.  So what are we to do about it?&lt;br /&gt;&lt;br /&gt;Ignore the VSMDI file.&lt;br /&gt;&lt;br /&gt;Say what?&lt;br /&gt;&lt;br /&gt;You heard me.  Ignore the VSMDI file.  You don&apos;t need it.  The only thing you need is the assembly that contains the unit test(s) you are interested in.  You&apos;re probably thinking &quot;so you expect me to maintain a list of every unit test assembly in the project?  Are you out of your mind?&quot;  No, and Yes, respectively, but the latter is neither here nor there.&lt;br /&gt;&lt;br /&gt;All you need to know is the topmost parent directory of your application (the root of all directories that could possibly contain a unit test directory...without going &lt;span style=&quot;font-style: italic;&quot;&gt;too&lt;/span&gt; far up the directory hierarchy.  It does no good to go beyond the scope of your application, so why bother?)  Once you have that directory, a simple recursive search will do all you need it to do.&lt;br /&gt;&lt;br /&gt;The following C# function will recursively search and find any assemblies that match the search criteria that you specify.  In the case of my organization, any assembly that contains unit tests actually contains &quot;UnitTest&quot; or &quot;UnitTests&quot; in the file name, so that makes my search much easier.  I also exclude any &quot;obj&quot; directories, or any directories that happen to be named for the service account that runs our automation (any directory with the name of the service account was most likely generated by previous test runs, and will contain assemblies you don&apos;t want to re-run). [Sorry about the spacing...still trying to figure out how to best post formatted code on LiveJournal.]&lt;br /&gt;&lt;br /&gt;&lt;font size=&quot;2&quot; style=&quot;color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: rgb(51, 102, 255);&quot;&gt;&lt;/span&gt;&lt;/font&gt;&lt;b&gt;private void LocateAndExecuteUnitTests(string RootDirectory)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String[] FileSystemEntries;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FileSystemEntries = Directory.GetFileSystemEntries(RootDirectory);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (string Entry in FileSystemEntries)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (Directory.Exists(Entry))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Directory found.&amp;nbsp; Need to recurse (if not the \obj directory).&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( (!(Entry.Contains(Path.DirectorySeparatorChar + &quot;obj&quot;)) || (Entry.Contains(Path.DirectorySeparatorChar + &quot;AcctName&quot;))))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LocateAndExecuteUnitTests(Entry);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Restrict execution to only those assemblies that contain &quot;UnitTests&quot; or &quot;UnitTest&quot; in the file name.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( (Entry.ToUpper().Contains(&quot;.UNITTEST.DLL&quot;)) || (Entry.ToUpper().Contains(&quot;.UNITTESTS.DLL&quot;)) )&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ExecuteAssemblyWithMSTest(Entry);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Once I&apos;ve located a valid assembly file, I pass it to &lt;i&gt;ExecuteAssemblyWithMSTest&lt;/i&gt; like this:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;private void ExecuteAssemblyWithMSTest(string PathToAssembly)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; StringBuilder sb = new StringBuilder(string.Empty);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sb.Append(&quot; /testcontainer:&quot; + PathToAssembly);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sb.Append(&quot; /publish:&quot; + StringConstants.TFS_SERVER_NAME);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sb.Append(&quot; /publishbuild:&quot; + LatestNamedCIBuild);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sb.Append(&quot; /flavor:&quot; + &quot;\&quot;&quot; + StringConstants.OA_BUILD_FLAVOR_CI + &quot;\&quot;&quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sb.Append(&quot; /platform:&quot; + &quot;\&quot;&quot; + StringConstants.OA_BUILD_PLATFORM_CI + &quot;\&quot;&quot;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sb.Append(&quot; /teamproject:&quot; + &quot;\&quot;&quot; + StringConstants.TFS_CI_TEAM_PROJECT + &quot;\&quot;&quot;);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Process proc = new Process();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.EnableRaisingEvents = false;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.StartInfo.WorkingDirectory = StringConstants.OA_PATH_ROOT_DIRECTORY;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.StartInfo.FileName = StringConstants.MSTEST_PROCESS_NAME;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.StartInfo.Arguments = sb.ToString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.StartInfo.CreateNoWindow = false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.StartInfo.UseShellExecute = false;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.Start();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc.WaitForExit();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; catch (Exception ex)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; logger.LogException(ex, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw ex;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The above function builds a process object and then executes that process.&amp;nbsp; MSTest is invoked (via the process object), and ultimately publishes the test results back to our TFS server.&lt;br /&gt;&lt;br /&gt;Voila!&amp;nbsp; No more VSMDI files!</description>
  <comments>http://natrusso.livejournal.com/883.html</comments>
  <category>vsmdi</category>
  <category>testing</category>
  <category>mstest</category>
  <category>automation</category>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
<item>
  <guid isPermaLink='true'>http://natrusso.livejournal.com/533.html</guid>
  <pubDate>Wed, 30 Jul 2008 20:37:41 GMT</pubDate>
  <title>Quality Assurance does NOT begin with the QA Team.</title>
  <link>http://natrusso.livejournal.com/533.html</link>
  <description>In my experience in software engineering, I&apos;ve found that many organizations claim to be &quot;quality-centric&quot;, or may have even achieved a certain CMM level.&amp;nbsp; In reality, however, these organizations rarely QA their products beyond simple manual test cases designed and implemented by a QA team (when the organization in question even has the luxury of a QA team).&amp;nbsp; These organizations believe that unit tests (generated by Visual Studio), combined with a manual test process, will shake any existing bugs out of a system.&amp;nbsp; They are correct in thinking they will find bugs in the product, but not for the reasons they think they are correct.&lt;br /&gt;&lt;br /&gt;Most organizations wait until the end of a development cycle to begin thinking about QA.&amp;nbsp; They schedule a certain period of time &lt;i&gt;at the end of the cycle&lt;/i&gt; to test and debug.&amp;nbsp; Yes, they are going to find bugs.&amp;nbsp; Moreover, the fact that they will find and fix bugs during that &quot;phase&quot; of development will have the overall effect of convincing that organization of the correctness of their approach.&amp;nbsp; &quot;See!&amp;nbsp; We TOLD you we&apos;d find bugs!&quot;&amp;nbsp; These organizations tend to suffer from one of the &quot;Self-fulfilling prophecies&quot; mentioned by Steve McConnell in his book &lt;u&gt;&lt;i&gt;Code Complete&lt;/i&gt;&lt;/u&gt; (&quot;We&apos;d better start coding right away because we&apos;re going to have a lot of debugging to do.&quot;).&amp;nbsp; Incidentally, if you don&apos;t have a copy of that book yet, run to the nearest book store and buy it.&amp;nbsp; Anyone who calls themselves software engineers today should have a worn-out copy of that book on their desk.&amp;nbsp; But I digress....&lt;br /&gt;&lt;br /&gt;The primary function of quality assurance is to avoid introducing bugs to begin with!&amp;nbsp; In the event that bugs &lt;i&gt;are&lt;/i&gt; introduced into the product, then the secondary function becomes detecting and fixing that bug &lt;i&gt;as close as possible&lt;/i&gt; to the time it was introduced.&amp;nbsp; It &lt;i&gt;is&lt;/i&gt; possible, not to mention highly desirable, to produce defect-free code.&amp;nbsp; Don&apos;t let anyone tell you otherwise!&amp;nbsp; No human endeavor is perfect, but that fact is often abused by developers who are, quite simply, too lazy (or burned out) to implement quality processes in their day-to-day routine.&amp;nbsp; So, how do we avoid introducing bugs to begin with?&lt;br /&gt;&lt;br /&gt;The first step on this path is recognizing that quality assurance does &lt;i&gt;not&lt;/i&gt; begin with the quality assurance team!&amp;nbsp; It begins with the individual developer at the keyboard who is trying to decide whether or not to expose a certain method on a class, or whether to refactor a certain block of code into a method of its own, or whether or not the class itself exposes a consistent level of abstraction.&amp;nbsp; (We could easily get derailed here by discussing upstream activities.&amp;nbsp; And while I agree that much of the individual developer&apos;s success is determined prior to his even being hired, I&apos;m going to take an &quot;all else being equal&quot; approach to this subject and concentrate exclusively on the individual engineer).&lt;br /&gt;&lt;br /&gt;We&apos;ve set the stage, so now let&apos;s talk about some specific things a developer can do in his/her day-to-day routine to increase the quality of their product.&amp;nbsp; Here are a few of my favorites, in no specific order...&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Agree on a set of coding standards&lt;/b&gt;&lt;/u&gt;&lt;b&gt;&lt;u&gt; early in the process&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;At the end of the day, it doesn&apos;t matter whether you use Hungarian Notation, or decide you&apos;re going to preface all of your Int32 variables with &quot;George_&quot;, as long as your entire team/organization agrees on &lt;i&gt;some&lt;/i&gt; standard and you &lt;i&gt;document that standard&lt;/i&gt;.&amp;nbsp; Discussion about coding standards can quickly devolve into mini holy wars, so I&apos;ll try to avoid that.&amp;nbsp; Suffice it to say that the purpose of a coding standard (within the context of Quality Assurance) has little to do with the quality of the standard itself.&amp;nbsp; The purpose of the coding standard is to ensure that all developers, both present and &lt;i&gt;future&lt;/i&gt;, understand how to interpret code they have before them.&amp;nbsp; Code that follows a strict set of conventions goes a long way towards achieving that lofty goal of &lt;i&gt;self-documentation&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Review all code during development&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;Many technical leads are reduced to nothing more than code &quot;traffic cops&quot; in certain organizations.&amp;nbsp; They go through a typical day from one code review meeting to another code review meeting, peering at sample code and trying to keep the development teams on the proper coding standard path.&amp;nbsp; This is a very limited view of code review, and it turns code review sessions into interrogations where developers feel they must defend every underscore and semicolon.&amp;nbsp; That is &lt;i&gt;not&lt;/i&gt; the sort of &quot;defensive coding&quot; we want our junior developers to employ!&amp;nbsp; Code review serves three primary purposes:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Catch poor design &lt;i&gt;implementation&lt;/i&gt; before other coding efforts become dependent on the poor implementation.&lt;/li&gt;&lt;li&gt;Enforce proper coding standards and naming conventions, etc.&lt;/li&gt;&lt;li&gt;Educate &lt;i&gt;all&lt;/i&gt; developers (not just the junior developers).&lt;/li&gt;&lt;/ol&gt;Many organizations I&apos;ve been associated with focus so much on item #2 above during review sessions, that they miss fundamental implementation problems, and they miss countless opportunities to educate their developers.&amp;nbsp; Well-planned and properly-executed code review sessions will have a dramatic impact on the level of quality in your product.&amp;nbsp; In fact, well-executed code review sessions are &lt;i&gt;so&lt;/i&gt; beneficial that the impact is not solely experienced by the current project!&amp;nbsp; Well-executed code review sessions increase the overall body of knowledge of your fellow developers (team leads included), which has a beneficial impact on any future projects your team tackles.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;TDD:&amp;nbsp; Test Driven Development&lt;br /&gt;&lt;/b&gt;&lt;/u&gt;There was a time when writing tests was exclusively the responsibility of the QA team.&amp;nbsp; But it has been shown, time after time, that developers who are savvy about test development routinely produce a higher quality product.&amp;nbsp; Before I talk about what Test Driven Development is, let me address what Test Driven Development is &lt;i&gt;not&lt;/i&gt;.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;It is not &lt;i&gt;Tester&lt;/i&gt; Driven Development.&amp;nbsp; It is not the purpose of the test team to decide what should or should not be developed.&amp;nbsp; Those decisions should flow naturally from the body of project requirements.&lt;br /&gt;&lt;br /&gt;Test driven development begins by developing a test.&amp;nbsp; That sounds a little counter-intuitive, until we dig a little further under the hood.&amp;nbsp; By developing your unit test first, you are making a statement about the class you have yet to implement.&amp;nbsp; You are defining the context within which &quot;success&quot; can be claimed.&amp;nbsp; In other words, you are explicitly stating the conditions under which a particular class can be considered &quot;successfully implemented&quot;.&amp;nbsp; The first time you compile this code, the test should fail...because there should be no class implementation yet.&amp;nbsp; The theory is that you develop the class until such time as the test passes.&amp;nbsp; When the test passes, you are done.&amp;nbsp; That class is now feature complete, and you can move on to other classes.&lt;br /&gt;&lt;br /&gt;This process is valuable for a number of reasons:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It forces the developer to understand the class &lt;i&gt;before&lt;/i&gt; he/she implements the class!&lt;/li&gt;&lt;li&gt;It catches most non-integration defects at the time they are introduced, which is the optimum time to catch &lt;i&gt;any&lt;/i&gt; defect.&lt;/li&gt;&lt;li&gt;It avoids waste by letting the developer know when they are &quot;finished&quot;.&lt;/li&gt;&lt;/ol&gt;Number 3 above is very important.&amp;nbsp; I&apos;ve lost count of the number of times I&apos;ve seen a good implementation go bad because the developer didn&apos;t know when to quit.&amp;nbsp; Being a perfectionist comes along with the territory of being a software developer.&amp;nbsp; Developers, by nature, tend to have a higher attention-to-detail than most other people.&amp;nbsp; But this can become a liability if it isn&apos;t kept in check.&amp;nbsp; We often desire to implement &quot;enhancements&quot; (or whatever we want to call them) in an effort to make our class &lt;i&gt;perfect&lt;/i&gt;.&amp;nbsp; We tend to lose sight of the fact that &lt;i&gt;perfection&lt;/i&gt; is defined by our requirements, not our personal goals.&lt;u&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&quot;Stop the Line!&quot;&lt;br /&gt;&lt;/b&gt;&lt;/u&gt;I&apos;ve recently been reading a book named &lt;i&gt;&lt;u&gt;&lt;span class=&quot;b&quot;&gt;Implementing Lean Software Development From Concept to Cash&lt;/span&gt;&lt;/u&gt;&lt;/i&gt; by Mary Poppendieck and Tom Poppendieck, at the behest of my employer.&amp;nbsp; This is a very interesting book for any software engineer who is interested in implementing more efficient processes that have withstood the test of time.&amp;nbsp; The book documents the amazingly efficient manufacturing processes of Toyota, and how those processes can apply directly to software development.&amp;nbsp; One of the principles (amongst many) the book recommends is taking what the authors call a &quot;Stop the line&quot; approach to development.&lt;br /&gt;&lt;br /&gt;At Toyota, the company has given each individual employee who is involved in the manufacturing process the ability to stop the production line at the first sign of a defect.&amp;nbsp; The line comes to a complete halt, and no further production takes place until the cause of the defect is discovered, and a solution is put in place.&amp;nbsp; This one seemingly-obvious concept has dramatically reduced the number of defects that pass through the quality assurance process and ultimately end up in the hands of consumers.&lt;br /&gt;&lt;br /&gt;At Thermo Fisher Scientific, we have translated this process into our own iteration-based development cycles.&amp;nbsp; When a defect is detected, development stops, and we find the cause of the defect before moving on to implementing more features.&amp;nbsp; We have gone so far as to declare a build &quot;broken&quot; if any single one of our &lt;i&gt;hundrends &lt;/i&gt;of unit tests fail.&amp;nbsp; What this means for us is that we cannot consider a feature implemented if it contains a defect &lt;i&gt;of any kind&lt;/i&gt;.&amp;nbsp; The cost of moving forward will simply be too great if we hold off on fixing our defects until some future point in time.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Manage Complexity&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;Steve McConnell, in his book &lt;u&gt;&lt;i&gt;Code Complete&lt;/i&gt;&lt;/u&gt;, reiterates several times (himself referencing an earlier author, whose name and work eludes me at the moment) that the &lt;i&gt;prime directive&lt;/i&gt; of software engineering can be summed up in two words:&amp;nbsp; &lt;i&gt;manage complexity&lt;/i&gt;.&amp;nbsp; The way in which we make a quality product is to implement it in the simplest fashion possible.&amp;nbsp; One of my pet peeves as a software engineer rears its ugly head whenever I see code that appears to be complex for complexity&apos;s sake.&amp;nbsp; There was a time when it was laudable to be as cryptic as you possibly could be.&amp;nbsp; He who fit as many operations as possible into a single line of C/C++ code was the winner.&lt;br /&gt;&lt;br /&gt;Then we, as an industry, started adding up the costs associated with software development and came to realize the folly involved in that approach.&amp;nbsp; Obfuscation is a noble effort when the goal in mind is security or protection of a valuable intellectual property.&amp;nbsp; But we should &lt;i&gt;never&lt;/i&gt; obfuscate something that is &lt;i&gt;intended&lt;/i&gt; for consumption by other developers!&amp;nbsp; We obfuscate our code in subtle ways that fall far short of direct encryption, but nevertheless have a similar impact.&amp;nbsp; We obfuscate our code every time we use a &quot;magic number&quot;, or create an inheritence hierarchy that resembles a pictogram of the Tree of Life.&amp;nbsp; We obfuscate our code whenever we create a series of sequential operations, but fail to document the sequence.&amp;nbsp; We obfuscate our code whenever we see that a method has a cyclomatic complexity measured in 4 digits, but fail to perform even the most basic refactoring.&amp;nbsp; We obfuscate our code when we insist on abbreviating all of our variables, even though the abbreviation only makes sense to us.&amp;nbsp; And the list goes on....&lt;br /&gt;&lt;br /&gt;The list above represents just a small fraction of the things we can focus on to increase the level of quality in our product.&amp;nbsp; What I find interesting is that the guidelines for increasing product quality also seem to be common-sense guidelines to becoming better software engineers.</description>
  <comments>http://natrusso.livejournal.com/533.html</comments>
  <category>code complete</category>
  <category>testing</category>
  <category>qa</category>
  <category>lean software development</category>
  <category>automation</category>
  <lj:mood>contemplative</lj:mood>
  <lj:security>public</lj:security>
  <lj:reply-count>0</lj:reply-count>
</item>
</channel>
</rss>
