<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-24679746</atom:id><lastBuildDate>Wed, 28 Aug 2024 22:23:24 +0000</lastBuildDate><title>Daft Developer</title><description>Sharing my experiences and thoughts on .Net Web Development and Agile development practices including Scrum, Lean and XP.</description><link>http://daftdeveloper.blogspot.com/</link><managingEditor>noreply@blogger.com (Seann Hicks)</managingEditor><generator>Blogger</generator><openSearch:totalResults>36</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-8953222262282043824</guid><pubDate>Wed, 06 Jan 2010 18:57:00 +0000</pubDate><atom:updated>2010-01-07T10:37:48.495-08:00</atom:updated><title>New Scrum Metric Proposal - &quot;Hits and Misses&quot;</title><description>&lt;p&gt;If you want to compare Scrum Teams, compare them on who is achieving the Sprint Commitment (what I call &quot;Hits and Misses&quot;), not the Velocity.&lt;/p&gt;&lt;h3&gt;Meaningful Metrics&lt;/h3&gt;&lt;p&gt;One of the dangers with metrics, is that when we find a way to measure something, we tend to focus attention on it.  Usually more attention than the metric is worth.  So we like to oversimplify.  What the &quot;P/E ratio&quot; say about the quality of a Company?  What does &quot;velocity&quot; say about the quality of a Scrum Team?  Both are pretty subjective numbers.  The P/E ratio is tied to the stock price which is tied to the market&#39;s opinion of the value of the company, and markets are very emotionally driven.  A Scrum team&#39;s velocity is tied to the User Story estimates which are subjective.  The outcomes of comparing team velocities will most likely be negative, teams might inflate estimates or work overtime to increase their velocities.&lt;/p&gt;&lt;h3&gt;Hits and Misses&lt;/h3&gt;&lt;p&gt;I think it is very important to encourage the idea of Sprint Commitment.  The team should take the Sprint Commitment very seriously.  One way to encourage this is to measure how the team is succeeding in achieving the Sprint Commitment.  I call this &quot;Hits and Misses&quot;.  Essentially, every Sprint Commitment achieved is counted as a &quot;hit&quot;, every missed commitment is a &quot;miss&quot;.  If management is won&#39;t let go of the need to compare teams, I would give them the &quot;Hits and Misses&quot; metric instead of &quot;Velocity&quot;.  Of course there is a danger that teams will under commit.  If this is happening, I might look into presenting both &quot;Hits and Misses&quot; and &quot;Velocity&quot;.  However, I rarely see teams under commit, the tendency is always to over commit.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2010/01/new-scrum-metric-proposal-hits-and.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-4832845853190596895</guid><pubDate>Tue, 20 Oct 2009 03:13:00 +0000</pubDate><atom:updated>2009-10-20T11:01:17.293-07:00</atom:updated><title>Office Kanban - My Early Experiments</title><description>&lt;p&gt;This works well, and it&#39;s fun, but a couple of things I&#39;ve noticed...&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;You will need some Wall Space&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;My cube (yes, I work in a cube :p) does not have a lot of wall space.  And what it does have is covered by this strange mesh made of recycled carpet or upholstery or shoes or something, so stickies don&#39;t really adhere to it.  I almost used my window for the board, which would have worked great, but would have made me crane my neck to see, and I was worried might look a bit show-off-ish for people looking in from outside because they would see my sticky to solve the NP-complete travelling salesman problem in nLogN operations securely placed on the &quot;done&quot; side of the board - more about this in an upcoming post (ok, no - not really).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;When lo, what did I see?  My embedded whiteboard, easy to reach and visible from my computer, the perfect rectangular shape, I could write on it (not with the Jiffy markers though, really hard to get that cleaned off - trust me) and it had excellent sticky adherence.  Good-bye whiteboard, hello Kanban board!&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Small tasks, but not too small&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I started out really small, like 20 stickies a day, it was crazy, like &quot;read email&quot;, &quot;clean coffee cup&quot;, &quot;recycle TPS reports&quot;, &quot;select new screen background&quot;.  I think I was just reveling in the amazingness of the board, and stuff was just getting done.  Whatever I put up there, like magic.  Lots of tiny tasks was creating a lot of churn, I was spending too much time writing up stickies, and going to the stationery cabinet for more stickies, and stealing stickies from co-workers.  I was also using the Jiffy markers A LOT and I think they were having an affect on me (like I became extra talkative and paranoid and really thirsty all the time).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;So I made the tasks bigger, too big unfortunately.  Like days, and days big.  And this allowed me to get distracted.  So, say, instead of writing an AD interface like my task said - I was adjusting my chair height, and then my desk height and then my lamp angle - and those things were NOT on the board.  The Kanban &quot;flow&quot; was not happening.  I could see it was time to right-size my tasks.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Somewhere in between nano-tasks and uber-tasks was right-sized tasks.  How big are they?  somewhere around 2.  Hours that is.  I like them around this big.  Why?  Well, at the end of the day I can look at the &quot;done&quot; column and say &quot;That was a pretty good day, look at all those stickies in there&quot;.  Smaller tasks keep the focus, and keep you coming back to the board to move them, which keeps you focused on the board, like a game of tag, a back and forth, a conversation, a flow...&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;A Great Topic of Conversation&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;My visitors like to talk about the board.  Like &quot;What have you got there?  Is that the sticky board you were talking about?  Do you have MS Project installed because it can manage tasks? No? Well put in a request to have it installed, and I&#39;ll help you get started with it.  I think you&#39;ll really like Project. And you&#39;ll see you don&#39;t need this sticky board.&quot;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Or, &quot;Are you using those stickies to track your work, because we have a task tracking system you know.  In fact we have two, and people are expecting you to keep that up to date.  Well, it doesn&#39;t really matter if the task tracking system works for you or not, the important thing is that we ALL use it.  It&#39;s written in our SOX compliance documentation.  Where is that documentation? I don&#39;t know, and I don&#39;t think we&#39;re allowed to see it anyway.&quot;.&lt;/p&gt; &lt;br /&gt;&lt;p&gt;Here&#39;s a picture of it before someone orders the janitorial staff to remove it.&lt;/p&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXP73-1nk5D6CkL64n8MB3iInfYvyhlmpK5fbKv-VPhu6b3omxqDQFJ9Oy6F7iM2iwNDOAJ6_Q2SEW6JmjaE7xtqR5hUZ4jHDiZUmNghxG2d0oAE5LdvUi-W668P9f4XSRm5w/s1600-h/OfficeKanbanBoard.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXP73-1nk5D6CkL64n8MB3iInfYvyhlmpK5fbKv-VPhu6b3omxqDQFJ9Oy6F7iM2iwNDOAJ6_Q2SEW6JmjaE7xtqR5hUZ4jHDiZUmNghxG2d0oAE5LdvUi-W668P9f4XSRm5w/s400/OfficeKanbanBoard.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5394528026035722962&quot; /&gt;&lt;/a&gt;</description><link>http://daftdeveloper.blogspot.com/2009/10/office-kanban-my-early-experiments.html</link><author>noreply@blogger.com (Seann Hicks)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXP73-1nk5D6CkL64n8MB3iInfYvyhlmpK5fbKv-VPhu6b3omxqDQFJ9Oy6F7iM2iwNDOAJ6_Q2SEW6JmjaE7xtqR5hUZ4jHDiZUmNghxG2d0oAE5LdvUi-W668P9f4XSRm5w/s72-c/OfficeKanbanBoard.jpg" height="72" width="72"/><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-1207286715746041303</guid><pubDate>Tue, 06 Oct 2009 00:30:00 +0000</pubDate><atom:updated>2009-10-13T09:37:42.810-07:00</atom:updated><title>A Look at 10 iPhone Twitter Apps</title><description>&lt;p&gt;A quick summary of 10 iPhone Twitter client applications I have been trying out.  I have ordered them by preference, starting with my favorite.  My analysis is heavily based on how I use Twitter.  I&#39;ve noted what I like and dislike about each app and added a few screen shots of each.&lt;/p&gt;&lt;p&gt;Prices are based on the Canadian iTunes store&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td width=&quot;10%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 65px; height: 80px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLfdyB-BYyux0juaIMTLFxyOdwucypEUV0Nei9FShlyLctUchNQa0EqexeUAcnFqUoMHrnMFUOjPZBUkSZ-lMReEps-eS_rFrrKV-P3bBcQSO6FhvALpoP_5HD43ojvyP2xWQ/s200/Tweetie+2+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391037658325364946&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;Tweetie 2&lt;/h3&gt;Price: $2.99&lt;br/&gt;Vendor: Atebits&lt;br/&gt;Go to the Tweetie 2 &lt;a href=&quot;http://www.atebits.com/tweetie-iphone/&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td width=&quot;33%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8AjE-gyk0j7plU-uYAvNtVBFnIe2SB39nUfooByjEEwgmj-wohXIDDxbXUhWkB4KareyNAjwfsXka05SNTCuNuVFfuVZwv7JhFaTd4lEwpoVUTa_caBOUd238g0muEg5fitI/s200/Tweetie+2+Feed.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391038373307383154&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td width=&quot;33%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaVKYUIU4au8VnIRNs9xRDYQuTzI4if5rQFJOjS3829QQgwoNR8Ew6Kv3uA-wgF1NPd8_bXUlmAy-4-wVjVCNc4FPNZ4eYNQD629fQhPFmY4fnBbONtjkZ-AOhWAjASRonXf4/s200/Tweetie+2+Profile.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391038586531445090&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td width=&quot;33%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4I31aGXxMczFdTUzD5XRp-Ybcrh4f0_NgSXkT9Uf4PMYymx1tbvSy5G6xS5RQgkmqUQI6jfbNUlmeozRnyxZc3MppNcy_u6utkRdoR98ca-X1HdZr-y7OLmfv0_5UH-xgJgo/s200/Tweetie+2+Follow+Cost.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391038807909807282&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Wow, what a great product.  Tweetie 2 is fast and stable like its predecessor, but offers a rich set of new features.&lt;/li&gt;&lt;li&gt;At $2.99 it is the cheapest of the full featured Twitter client apps.&lt;/li&gt;&lt;li&gt;Slick integration with follows back and follow cost.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Refresh your timeline - &quot;pull down&quot; on the timeline to refresh it. Great idea&gt;&lt;/li&gt;&lt;li&gt;Following/Followers list management is very easy&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;I can&#39;t seem to get to the Public Timeline.&lt;/li&gt;&lt;li&gt;It replaces the old Tweetie, so it really cost me $3.99.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 70px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHPsl5jFbAs2iVsd1LOYRkrk2ytQPvst0N0DqU0ObV7ZVgpTzgFXrG8Fq5x6_Fb-dh1a9d7LJyRDs-7iQ_SzWUXiozIAovND4jVWrr-A4Jz4g8Q9H8XqX5Lg5U3xGSesqEJc4/s200/SimplyTweet+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390057461393930882&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;SimplyTweet&lt;/h3&gt;Price: $4.99&lt;br/&gt;MotionObj&lt;br/&gt;Go to the SimplyTweet &lt;a href=&quot;http://motionobj.com/simplytweet/&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPZiILz7mk8jBVsqN33K2WI-FF7f7nFTy69ej6ZKnXTct72t1mZlEy1pp-YH9OyoI6VLSgngq4twD3cmYCNe8b6onPsBA-1z2iZHqR4mjzabvTGSHSCFK3xXKb0y54XtGjBKU/s200/SimplyTweet+Feed.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390033285415422770&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-u33chfgNKwGsmK7DwEJx6gCw5HXCOVg4_cj2TnDBSBkj2B4KD1GQYuuI0-OdglAd0wviv__KiQO8z2pv8rcZ0SPUxYguHYi0xVcfxniAKXaiJtKh3jmiyUc6xRRMNKqPRwI/s200/SimplyTweet+Profile.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390033477342834578&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEje6b-VNRUnW-RnQwW_1qGHwlJj-Ll2PgTMwp0OUQyY056tAWERvWtQHP7VljLbhA05ASgMcZDiaAgBqIfvueAeH497iDsOlY8EkfRO7NJneCDseLk_pUWmUbAbQjmMZqk9ZOI/s200/Simply+Tweet+Nearby.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391033466872619282&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;DM and Reply alert - This works like text messaging, so you could use SimplyTweet for texting too.&lt;/li&gt;&lt;li&gt;Conversations - Very nice.  It can be easy to lose track of conversation threads and replies, SimplyTweet can show reply threads.&lt;/li&gt;&lt;li&gt;Bubble Tweets - I find bubbled tweets easier to read&lt;/li&gt;&lt;li&gt;Follows back - this feature is well integrated into the product and is displayed on the user profile&lt;/li&gt;&lt;li&gt;Nearby - You can specify the proximity of nearby tweets from 1 to 50 miles&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Follower/Following Lists - I can&#39;t seem to display these lists.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 70px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL87iETF0xi_LHIXxhkggkLcpI5kHCa_BIdkK8jEgLUFKMwYS1JIZdm77oEDr3GWS2APf8cr6HaqOO_jPBRUop-JFrJC-dkig43CHvvDlOcGAxK0REgmugI9CeTxt_DJ_X5Ek/s200/Echofon+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390056492619144754&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;Echofon&lt;/h3&gt;Price: $4.99&lt;br/&gt;Naan Studio Inc&lt;br/&gt;Go to the Echofon &lt;a href=&quot;http://echofon.com/&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPX-lFgQtq7gOS7w00u9zb80c5g251wBxjKv3bLEcexFw9xbmjV7EP3FxctJtDJpD45Va2zk6F7t3RssBj8HfczoJL8WU9hBKJ0vt9sAQSqhZflVVeRrdYKtCPMzf1CKs8hkM/s200/echofon+feed.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390030385234371890&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZlAIYfvB8mqS9-2TFmhojy87hWFcxSgZl-ntmH_q5E6r_P6g13fpteqCRv1CpdSYPySDwZPeW-g8LeoXP5X51o0LjDr2PAOBKH2XldnGN15sJr5K1U5ZBSJ8uZwPYUZGTwBk/s200/echofon+profile.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390030533766633634&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhDo6mKpvs49I0Im7Xgyv18cWTut_ZN7ZxWWsttugFH5obsXnd-njSgN6xL4BcgTJNJDWxLlv4TWrim-eAGVaZ999IdXV0w8TUuMlHo83DmqaQ5TpK61VJEmDdAoANMEciYls/s200/Echofon+Bio.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391028302382223954&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;br /&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;I&#39;m calling this the &#39;people finder&#39; - from within the tweet window, you can pull up a list of people to choose from for shout-outs.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Links are auto-shortened with bit.ly.  This just happens automatically once you tweet.&lt;/li&gt;&lt;li&gt;Profile detail shows if you are following someone and if they are following you back.&lt;/li&gt;&lt;li&gt;Following/Follower lists on profiles.  My &quot;can&#39;t live without&quot; feature.&lt;/li&gt;&lt;li&gt;Trends - Go to current trends through the search feature.&lt;/li&gt;&lt;li&gt;Nearby Tweeters.  Again through the search window&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;The Name - EchoFon?  Is Naan Studio Inc a Swedish company? because &#39;Echofon&#39; sounds like the name of an Ikea product.&lt;/li&gt;&lt;li&gt;I can&#39;t find the &#39;go to user&#39; feature.  But the search can be used to find people.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td width=&quot;10%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 70px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUvST01NGd6m5Yhi7zJQEzwsLivD449cSWfwRd7lYXjLvUzzoRdc8EHCWcPvrWjCrtllfm5uQ5kQWsN9wD1Rbg_z625pCGHnbNK_g18JmeXFOyhYcE35cUdhk_UYW8_o7Dw4w/s200/BirdFeed+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390055284562768450&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;BirdFeed&lt;/h3&gt;Price: $4.99&lt;br/&gt;Vendor: System Of Touch&lt;br/&gt;Go to the BirdFeed &lt;a href=&quot;http://birdfeedapp.com/&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td width=&quot;33%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLpwnHLqHW0cTYJ3PT_2gVFORqIRTe9mgnBHYQ-Mx5TdXtC-Zq7phaxdWa8GsTdXUDyn78OqhhfwY-v2-_FwwjoeLmf-EQLBGbgfSCzirWFsH2f1vAbg1p2pQOsG6eeuVdMNE/s200/birdfeed+feed.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5389964530135248498&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;/td&gt;&lt;td width=&quot;33%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKlCYzozqtwOpU5HdMVcYayLbnpPck7Xn1uPjxD7gHZ70QhbZqGhGl-KeA16gTIhsBYvOcDu-pusrtWJ9iYa9jaWsjQ39oGbySKZ2JaDRiu6Z4wLqDyvFW0ZjvmATmqQgWDTQ/s200/birdfeed+profile.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5389965109144924194&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td width=&quot;33%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsIZvnIcoVU7HmH4UXNoRVCor4AHgcYlzVqJN5ZI_P_Eeox4ntzFSVPSolHRpKNv8IYwba9r2CZODpEpuWPBYo_tKQzE2yBs2GxRjoppd8aD7Y8ANRHsKFDzq6k0vpOGebg3A/s200/birdfeed+menu.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391020282480344594&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Bubble format and fast scrolling - I prefer the bubble format, even though it takes up more screen space.&lt;/li&gt;&lt;li&gt;Tied into external tools - Query tweeps agaist FollowCost, DoesFollow, Overlapr - could be better integrated into the app, but still cool.&lt;/li&gt;&lt;li&gt;Basics like ReTweets, DMs, Replies and favorites are quickly accessible and easy to find.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;No URL Shortening - It either can&#39;t do URL shortening or I don&#39;t know how to find the feature.  But when I paste a URL, it violates the 140 character limit and I can&#39;t post the tweet.&lt;/li&gt;&lt;li&gt;Profile Details - While I can look at profiles and see bio&#39;s and follower/following counts, I can&#39;t view followers/following lists.  I tend to use these to follow new people.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 70px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiAK9TlQGphVD7ffX8tZ2gvL2kMKwHObpEg_zn5Wf9A4F_AkSAuU8u5E47d6t07R46tsUkRig7M9kV5mHuS2xpXVGG1rtALnqXH7cmjmWE3TTJW6jlHa61fkiNHt6e9CQQKAA/s200/Twittelator+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390057952105784754&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;Twittelator&lt;/h3&gt;Price: $4.99&lt;br/&gt;StoneDesign&lt;br/&gt;Go to the Twittelator &lt;a href=&quot;http://www.stone.com/Twittelator/&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg5QdDUnvlLYJRRDTvavXSXIGoFaZ-2f1DafiWX25IqKL3Cs1MnEx2gzFI7XWTTjT73o9g41IMle7vqyCsvBUoLh0hlTU9KTJ_dapZmN0tIOyV0XXyQuPrJGO9h79y0ljUNdo/s200/Twittelator+Feed.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390034867276386562&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz18Moa88SIj7dK_-R_iQTOu7CAcyRBzCz6yOPokiwEzkRLEj5dXAAHNT2iwYZ1Y1E_cWkqtfxe4M7S0uo2LZzdvdeXn8lD_4I1732lPkMYK6ghFbnFD6MDDbbmwher3dcEVg/s200/Twittelator+Profile.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390035034964560242&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2LUrd8XyKWsnrFUuGnecd2lsdAK7o_m4ku6DX-rQ4I_yk09fJARDsd4MnYLqPGZEi_jYxb4HfVB7CD-bpJnqEirYeU9LixKkV27GfgQMtcPm0scr9a-bNPzmcrTUMxD1Hp78/s200/Twittelator+Emotes.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391798183507558130&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Horizontal aspect - You can view link web pages horizontally.&lt;/li&gt;&lt;li&gt;Paper clip links - Links in a Tweet include a paper clip icon which can be clicked to navigate to the link.  Reduces some navigation overhead.&lt;/li&gt;&lt;li&gt;TwitPic display - thumbnails of photos are displayed in tweets.&lt;/li&gt;&lt;li&gt;Mute - This seems contradictory to the spirit of Twitter, but sometimes, maybe I don&#39;t want to hear @Alyssa_Milano&#39;s daily minutiae.&lt;/li&gt;&lt;li&gt;Groups and subgroups - I think these are specific to Twittelator.  There are so many sites trying to provide this feature.&lt;/li&gt;&lt;li&gt;Link shortener - j.mp&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Tweet actions menu (to retweet, reply, etc.) was slightly difficult to find.  Tap above the tweet text to activate.&lt;/li&gt;&lt;li&gt;The Windows 3.1 style shading on text makes it very hard to read.&lt;/li&gt;&lt;li&gt;UX - This application just doesn&#39;t look that good.  Might be time to hire a design and graphics expert.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 69px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNogOLDBgUeXAGIcCZTXGm3CERy1YFQx2ju-pVLnpPEhaWpynxWOHpFymlshRUp036gQPWWr23ycWTgrJLRwRe2LVdOnbXZL5UeAXMw9XzYukfdn33QiUB4I_W4u8Ebg8xYio/s200/Twitterrific+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390057703108322434&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;Twitterrific&lt;/h3&gt;Price: Free (displays advertising)&lt;br /&gt;IconFactory&lt;br/&gt;Go to the Twitterrific &lt;a href=&quot;http://iconfactory.com/softwa/twitterrific&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEiNX3poZgLFar8yczjyQJhT0Wloh1FxZs5XQCGYxfevau0mJPHtFDlEmds7VWCTnEE5zHZBFNQDNtFCalCCvuH_IIHynuYUbr_u8YofRsxP7CbVabHj91BWgqb8NxebzIlW0/s200/Twitterific+Feed.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390034131120445730&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCMceuzDhzNCzPg_vw5yjYhY5AUxU52XLxQJ68yqehAUtQPGzHJgLAo98B_RpR9drj0-5TT107LutnbOFt1AnnZXcSVggXyU8_v4UdqRT14biWU4ibS8LTW9MG65PDWhinE-4/s200/Twitterific+Profile.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390034306318858066&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPmBEw54JDkJnYiZI2XVpoQTA1TqbPLd625-1Dc7nX7oYjtvCjUiuJTfNdzW8EnTHSTFDir0nQEhmKYVyS3LFs0xrWY5N50q-du7H-sl9lHdOHbGQRIAF9Bwt1e-nqoITOhrk/s200/Twitterific+Menu.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391036129939427202&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;br /&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;What a slick interface - shading and colouring looks great.&lt;/li&gt;&lt;li&gt;Feed display - I can collapse tweets to 3 different sizes&lt;/li&gt;&lt;li&gt;Access to the public timeline, which I only use to play with the translator&lt;/li&gt;&lt;li&gt;Translation - This works very well, especially on latin based languages.  Thai, Malay not bad.  Japanese? Not so good.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;It makes &quot;chirping&quot; sounds when you refresh your feed.  This is cute, like... once.&lt;/li&gt;&lt;li&gt;Ads.  Not really intrusive, but I feel like I need to click them or they&#39;ll start charging for this app.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td width=&quot;10%&quot;&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 69px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiG94ahysR6Vky025vSzLGFCqeB1GbDwjQYV5OVURA5Pk3B5USPlWwWtJnbg72blCUlWugQeCENkMCxPQ9NV_h-Sd6nZJmMNQNMQmjDkBeh-8QABzagbbboxt3gzQQVH0qJPA/s200/Tweetdeck+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390055782935456722&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;TweetDeck&lt;/h3&gt;Price: Free (Beta Version)&lt;br /&gt;TweetDeck&lt;br/&gt;Go to the TweetDeck &lt;a href=&quot;http://tweetdeck.com/beta/&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAeE-P3jPMgVAzE_nGl2iQc_aXe1embn6A-9F4bKJQnpFiRJL6UhCAkJk0WfaDqk1s75injuSZHnDELjdzY50B4qK_BAwntaOHDtpotNUIEs_JPiOPF2Msx9d51H0leTxXeJs/s200/TweetDeck+Feed.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5389966820002827058&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSYNkwXLQ3st1Ke8HN2GJUo9xbVkoJXUL8rJ9kc3ziSqWUnOq-qb2HhVIsvuaaVThCGAjX6TEhL3Xm4Hc87vrfAtogZj_uArJDqtI5cqeCZZuuV7F1cGf2daCXMxlWkNZB6wc/s200/TweetDeck+Profile.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5389968383729330274&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcgPrPrrHZPs_L2oX75EMxqhyphenhyphenv4KyPZK97fB-zIRBWhTunNcP5ovJB5OmBAog79Z5v136967Vu3VMysODc08_Efr28ZjL4A9m-Wfisq0sP8GyO9_rN-UDe4UE9TD6has4ST0M/s200/TweetDeck+Columns.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391024944182716930&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Columns - I love columns.  I generally use columns to store searches on keywords and hash tags.  With TweetDeck my searches are always available, and I can quickly scan them.&lt;/li&gt;&lt;li&gt;URL Shortening - there is a small icon of chain with &#39;shrinking&#39; arrows on the tweet window to shorten URLs.  Tweetdeck uses the bit.ly shortener.&lt;/li&gt;&lt;li&gt;Location Tweeting - Not my thing, but it&#39;s there.&lt;/li&gt;&lt;li&gt;TwitPic Photos - Take a shot, or choose one from your library.  I like this feature.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Unstable - Tweetdeck crashes on a regular basis for me.  Usually when I start it up, it&#39;ll just disappear.  Annoying.&lt;/li&gt;&lt;li&gt;Column Maintenance - I have a super hard time closing columns, tapping the &#39;gear&#39; in the top right corner is very challenging.  This could just be me.&lt;/li&gt;&lt;li&gt;Column update messages - I have a lot of columns and when I start Tweetdeck I get a load of intrusive messages indicating how many new tweets have been added to each column.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 69px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinNBeRDzu5XYELXDvChYZJxEuuXOqhuwBOcjERYcc26kivrATZnhvg6SwBbZhdRa7ljiPOdSPdc-ipW5HQecWdyVIhBncsIcTBacfZAB49Ir6RUacLI2kcCpoMEhFh8Xj5cEg/s200/Tweetie+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390056045743132562&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;Tweetie (1)&lt;/h3&gt;Price: No longer available&lt;br/&gt;AteBits&lt;br /&gt;Go to the Tweetie &lt;a href=&quot;http://www.atebits.com/tweetie-iphone/&quot;&gt;site.&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjE05VSj9d6Roa2ArHdIr_WCKlaMWaSaPQNz2dyHu7GmiHlB2eSpCASROqteezWRw6WJ0Ffd5T8rJjXZGECPiSSuHfTdC8C4yElPoBZAIW0_TRxqA7mHNZohyphenhyphenJ0_pNYppDgn0Q/s200/Tweetie+Feed.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390029670202648002&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUrQcREJ0oc7mezwoLne75dLUFtZGdftReJ4_20Koi2qem6LClTz6IHRj_NiLVD9ZKtnbSlycskAEJz-BVef3fD0vJISMwqMfp_F-J3-Dhajjopwb8eTdNV0QkdkQA-djvXH4/s200/Tweetie+Profile.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390029877486163330&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFVPTdsbWbWOQ0TNse3u4XrvP4X0yk9dOgThnd83P0g2FxPbsFphq96fiD1Izzqow9d_SeOCp9aGKqZ4v1CIlC69ZB-pZF7AvsyakRz6GwAe2MVsNDLeX1ohYTH_RhULkjXzs/s200/Tweetie+Nearby.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391027188977009106&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Fast - Tweetie loads fast, and scrolls nicely.  The line display lets you move through tweets like a rocket.&lt;/li&gt;&lt;li&gt;My Profile - I think every Twitter app should have strong profile maintenance.  Tweetie makes it easy to view your bio, followers/following lists and time-line.&lt;/li&gt;&lt;li&gt;Nearby - You can see who&#39;s tweeting &#39;nearby&#39; if you allow Tweetie to broadcast your location.  Again, not my thing, but still cool.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Trends and Public Timeline - I generally avoid the trending topics (mega spamage).  I think being able to follow trends is an important part of Twitter and it&#39;s good that Tweetie offers this.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Follows me? I can get to my following list, but there&#39;s something I like about knowing if someone I&#39;m following is following me back.  Tweetie doesn&#39;t have this feature.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br/&gt;&lt;br/&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 70px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghr0qZddtJSmrLIb2h6Y6e-IIpgVrr3cWtmAmz2j_Jx2aRkehDP7HsRcPJFvD6Zppw70mmJrdff-7sePnE4NtHKntcQH5QVo_oKHfWYg3rIkrjYJ-cvx5qVcSR7p_cTbL4nmQ/s200/iTwitterPro+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390056990506389218&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;Twitter Pro&lt;/h3&gt;Price: $0.99&lt;br/&gt;iApp Ventures LLC&lt;/br&gt;Web Site?&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYXDWWyUq-GtlL0Q9RwcvlFz9POaJf8v6z-GkyKOJhuVp3ddoXYr52N54vkRfcTC3Qc0kvsYxM_TGHmTluolx5wz_c3pfdb8gxA4oRNUQ52ZH3YLWn_ilmHpj5dizWEfq1n90/s200/iTwitterPro+Feed.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390031998266623026&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtvmBArFtM5ezpo_f39hkSxRCsR03pQueDUuvJs9ERE5MVbCHv82exreiWOlasiWD1jsc8tyFGEKKyvUm8z6ePv0vTLLnsykekNb397D78sJnDjVWBdvmIqxrrb33pVSdYC8s/s200/iTwitterPro+Following.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5390032166520317730&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;ummmm?  Well... it has a nice icon.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Scrolling is very jumpy - probably the most irritating feature.&lt;/li&gt;&lt;li&gt;Can&#39;t view profiles, or add users.&lt;/li&gt;&lt;li&gt;It costs a dollar - I can&#39;t believe this app got through testing?&lt;/li&gt;&lt;li&gt;What is this app even called? iTwitter Pro or Twitter Pro?&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 67px; height: 81px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMr4Cjkazui_XVizV5dbyijXVfIlrbhA1nXNy1sG3YMHus8ABJbHWVQiXPBI-CQkh2cPB5JURLEN_n3asVT8xm9-BMwf_n6FgP3fP32KvKY1VOUzOQgbXDbjRR5SeSJ-1YhLM/s200/tweeter+icon.PNG&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391047271934721218&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot;&gt;&lt;h3&gt;Tweeter&lt;/h3&gt;Price: Free&lt;br/&gt;Takuma Mori&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;img style=&quot;margin: 0pt 10px 10px 0pt; cursor: pointer; width: 134px; height: 200px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuNfl7PNtxRR-oIeIhalFdzfDFf0E3FkfSKZXUoiZctY9Rxn4gkRBdpbSfFkZpJya1CjYviH6MBJBFWrSlFGKGk7goArYm1G7v2n9dneRleiHHY6idbmyocfDllsqlmfwXOz4/s200/Tweet.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5391047562175157026&quot; border=&quot;0&quot; /&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Nice&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Simple.  It works as advertised - all you can do is tweet. That&#39;s it.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;3&quot;&gt;&lt;h3&gt;What&#39;s Lame&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Well, this app doesn&#39;t really do much.&lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;</description><link>http://daftdeveloper.blogspot.com/2009/10/look-at-10-iphone-twitter-apps.html</link><author>noreply@blogger.com (Seann Hicks)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLfdyB-BYyux0juaIMTLFxyOdwucypEUV0Nei9FShlyLctUchNQa0EqexeUAcnFqUoMHrnMFUOjPZBUkSZ-lMReEps-eS_rFrrKV-P3bBcQSO6FhvALpoP_5HD43ojvyP2xWQ/s72-c/Tweetie+2+icon.PNG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-4089584468027788723</guid><pubDate>Tue, 29 Sep 2009 03:56:00 +0000</pubDate><atom:updated>2009-09-28T21:56:32.049-07:00</atom:updated><title>Organizational Challenges: Product Ownership</title><description>&lt;p&gt;Who&#39;s in Charge Here?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Identifying the Correct Product Owner&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;In Scrum, the Product Owner is essentially the person &#39;in charge&#39;.  They are accountable for guiding the construction and delivery of a product.  A valuable product with high quality, desirable features.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The Product Owner is an important job, definitely the most important in a Scrum project.  The Product Owner should be&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Engaged - Driving the product with enthusiasm&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Involved - Part of the Team and available for questions&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Empowered - Able to make decisions about the product on the spot&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supported - Backed by stakeholders and management&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Accountable - Comfortable with defending product decisions&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;The Business Analyst&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The first thought, was to assign the Business Analyst the job of &#39;Product Owner&#39;.  We tried this and ran into some issues&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Invalid Feature Prioritization - features were initially prioritized in &#39;build&#39; order&lt;/li&gt;&lt;br /&gt;&lt;li&gt;No Cutting - All features had to be delivered (Essentially all high priority)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;No immediate answers - all questions had to wait for stakeholder approval&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;On this project we were lucky, and it so happened that the B.A. involved the supervisor of our user base in order to show her what was involved in building a software system.  After 5 sprints, the supervisor began assuming the role of Product Owner.  Cutting features and deciding on priorities.  The Product Owner required a high degree of coaching and support from the BA and the Scrum Master, but it was still clear that the accountability was with the Product Owner alone.&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Getting the right Product Owner was critical to the success of Scrum on this Project.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2009/09/organizational-challenges-numero-uno.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-133148661830384238</guid><pubDate>Sat, 28 Feb 2009 15:12:00 +0000</pubDate><atom:updated>2009-02-28T09:02:10.009-08:00</atom:updated><title>Bringing SCRUM to my organization</title><description>&lt;p&gt;How do I successfully bring SCRUM into my organization?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Believe&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I&#39;ve discovered that the first, most important thing, is to really BELIEVE.  Believe in SCRUM and that you can make it happen where you work.  Last year, before my training, while I had some experience with SCRUM at my previous employer, I didn&#39;t have a firm grounding in it a.k.a. formal training.  While I was learning SCRUM, I asked the instructors a lot of questions about how to successfully convince my management that SCRUM was going be useful for us (Thanks very much to the instructors at &lt;a href=&quot;http://www.berteigconsulting.com&quot;&gt;Berteig Consulting&lt;/a&gt; for being so helpful).  On the last day of our training there was a somewhat confrontational, but extremely inspiring back-and-forth between a student in the class and the instructor regarding co-location and productivity.  The student insisted co-location was impossible, and the instructor kept asking &#39;why&#39; until it was learned that the company the student worked at was choosing to value &#39;other concerns&#39; over productivity.  What I saw in that dialogue, was the sorts of challenges I was going to have to deal with.  Essentially, the people I work with, and my management telling me what I wanted to achieve was &#39;impossible&#39;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Our instructor said that being a Scrum Master required courage and a belief that nothing is impossible.  I used to laugh at statements like that, but for some reason, I&#39;m changed... I totally believe that I can make anything happen.  Now that I have formal training in SCRUM and the SCRUM-Master designation.  I am working on bringing SCRUM to my organization and blogging about my journey.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;So Far, So Good&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;What have I achieved so far?  Well, I have engaged my supervisor.  He did some SCRUM presentations to our organization last year, and while he is not a scrum master, he has a good working knowledge of the process.  We put together a 1/2 hour presentation where I did a comparison of waterfall and SCRUM, and described the SCRUM process.  He continues with how SCRUM can benefit our company and how it fits, and the organzational impacts.  We presented to our team (development team) and to our direct management.  We are calling our presentation the &#39;SCRUM roadshow&#39;.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Road Show&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The SCRUM presentation we have so far is very simple.  We have some notes that we&#39;ve jotted down, and NO SLIDES.  We have handouts as take-aways, but the presentation is done completely on a white-board.  Except for the fact that my back is turned to the audience while I&#39;m writing (I&#39;m working on sideways writing now), I think the lack of slides makes the presentation more engaging.  So, for the moment, this is our road show.  We are presenting it to everyone who could possibly be impacted, first within the IT organization, and soon, hopefully to the business.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Pilot&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;We have the go-ahead to pilot SCRUM with some small, low visibility projects, to get our feet wet and get our team used to the process.  I am seriously considering buying the &lt;a href=&quot;http://www.scrumalliance.org/&quot;&gt;Scrum Alliance&lt;/a&gt; scrum board game as a way of solidifying the SCRUM process with our team.  In any event, I will be Scrum-Mastering 2 pilot projects.  We have additional projects that will be managed in our traditional fashion, so it will be interesting to compare results.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Challenges&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Of course, resistance to change is evident everytime I even mention SCRUM.  I get the best response from people who have managed waterfall projects and have felt the pain of those projects.  So I&#39;m focussing on describing the pain of waterfall and hopefully hitting a nerve.  This seems to make people much more responsive to a new approach.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Enter, Denial.  I see denial when I talk about waterfall pain.  This is much more difficult to deal with than resistance to change.  Success stories work here, and I have a small one that I share (more on that later).  I think talking about your own challenges with waterfall projects gives you credibility and shows people that &#39;owning up&#39; to problems actually shows strength (instead of incompetence).&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;A Small Success&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;We have a project that, for various reasons, is about 12 months overdue.  The software has a large list of bugs and some small feature requests.  All of which are listed in our QA tool.  I have taken on the task of rescuing this project.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Before I applied SCRUM to it, I had some of the following issues,&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Seemingly flip-flopping requirements&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Massive quality issues&lt;/li&gt;&lt;br /&gt;&lt;li&gt;BA/PM unaware of project status&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Blame and unclear responsibilities&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Lack of BA engagement&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;The first thing I did, was estimate the complexity of each of the bugs and changes in the bug tool using a number between 1 and 5 (I.e. a 2 is 2x the complexity of a 1).  I then asked the BA to prioritize the items and mark the bugs that are critical to the release as &#39;high&#39;.  At first the BA just said everything had to be fixed.  I told him that would mean we don&#39;t release for several months.  I also showed him that some of the bugs were just minor inconveniences, and that the cost of fixing them was quite high (this happens all the time in software development).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Prioritizing the bugs, forced the BA to actually read and understand each issue.  I had effectively put the BA in the Product Owner role.  Immediately the BA&#39;s engagement increased, and he had an excellent idea of the project status and what was standing between where we were, and our release to production.  I had also made the Product Owner (our BA) responsible for the release date.  He was deciding how many sprints we would be doing based on how he rated the criticality of items.  This also helped stop the flip flopping of requirements.  Now, it was not longer the developer&#39;s fault for the late release, the team was accountable, and the team included the BA.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;On the quality issues, I engaged another developer to do QA. Bugs or Changes weren&#39;t closed unless the QA developer was satisfied.  I encouraged her to be very strict and to note everything using the tracking tool.  On many occasions, she would re-open items I thought were ready to go.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I set each sprint to a 1 week duration, including a planning meeting on monday, daily scrums and a demo and retrospective on friday.  I&#39;ve found the Demo to be absolutely critical to ensuring a clean sprint completion, it serves as an excellent test for &#39;shippable software&#39;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;4 sprints later, I believe the product is ready to release.  And I believe I have won over the BA to the SCRUM process.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2009/02/bringing-scrum-to-my-organization.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-5307535062165730203</guid><pubDate>Sat, 10 Feb 2007 04:25:00 +0000</pubDate><atom:updated>2007-02-09T20:51:39.245-08:00</atom:updated><title>Unit Test Granularity</title><description>&lt;p&gt;How many assertions should I have in my automated Unit Tests?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Unit Tests are Methods Too&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Test cases just like methods in your code.  They should be designed using the smae principles.  Specifically, a method should be highly cohesive.  It&#39;s purpose should be focused and well defined.  Test cases are no exception.  A test identifies and verifies a specific scenario with the intention of revealing a defect.  An automated unit test method with proper cohesion must have this single-minded focus.  It may be somewhat extreme to demand 1 assert per test.  The single assertion test is however and good ideal to strive for.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Many Tests Make Light Work&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;While the purpose of your existing tests is to catch regression.  They can also be useful helping determine the cause of introduced errors.  A group of test failures are more likely to point to a cause than a single test, due to the simple volume of information.  Looking at a group of failed tests raises the question &#39;why are these tests failing, what do they have in common?&#39;  A single failed test immediately becomes a debugging effort&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Read this &lt;a href=&quot;http://msdn.microsoft.com/msdnmag/issues/06/01/UnitTesting/default.aspx&quot;&gt;MSDN Unit Testing Article&lt;/a&gt; for some good unit testing guidelines.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2007/02/unit-test-granularity.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-115904980232432905</guid><pubDate>Sat, 23 Sep 2006 21:53:00 +0000</pubDate><atom:updated>2006-09-23T15:18:14.450-07:00</atom:updated><title>Attaching Debugger takes Forever!</title><description>&lt;p&gt;Why does it take so long for Visual Studio to start my web app in debug mode?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Check your Symbol Server&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;If you have a symbol server set up, and have the following environment variable &#39;_NT_SYMBOL_PATH&#39;, your debugger may be retrieving symbols from a slow store.  After some experiments (like uninstalling and reinstalling VS addins, etc.), I removed the _NT_SYMBOL_PATH variable from my system and voila! attaching the debugger is fast again.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Investigation Required&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Some analysis required here.  My symbol path is quite long.&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;SRV*C:\data\symbols\OsSymbols *http://msdl.microsoft.com/download/symbols; c:\data\symbols\ProductionSymbols; C:\Program Files\Microsoft Visual Studio .Net 2003\SDK\v1.1\symbols;C:\winnt\system32&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;I thought to slowly remove stuff in the path until performance returned.  It appears that VS caches the symbol path however (blah), making the investigation slow.  I am suspicious of the microsoft web address.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Culprit&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;It appears that removing the microsoft web address has fixed the problem.  It&#39;s not really necessary to check for those symbols over and over again anyway.  So I have removed it from the _NT_SYMBOL_PATH variable and I will put it back when I need it.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/09/attaching-debugger-takes-forever.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-115853078536107608</guid><pubDate>Sun, 17 Sep 2006 21:32:00 +0000</pubDate><atom:updated>2006-09-17T15:13:39.313-07:00</atom:updated><title>Less Up Front Design == More Supple Design?</title><description>&lt;p&gt;Does reducing the amount of up front design encourage a cleaner, more supple application design?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;One Case&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;My little (ongoing) project, the &lt;a href=&quot;http://www.carcarecalendar.com&quot;&gt;car maintenance reminder app&lt;/a&gt;, is a good example of how designing for one feature at a time encourages a highly extendable code base.  Since the amount of time I can spend on it is highly variable, I am not able to plan sprints.  So, I merely tackle each item on the backlog as they come.  Once I&#39;m happy with the feature, I release it.  In a more usual agile development environment it is fairly inefficient to release software each time a feature is completed.  There is usually a fair amount of process involved.  In my case however, not having paying customers, or customers at all for that matter, means I&#39;m pretty much free to take risk.  I don&#39;t need the release rigor.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Anyway, back to suppleness I noticed that adding a new feature meant fully re-examining the system design as a whole.  Talk about reducing the cognitive load!  I only had to ensure that 1 new feature could be integrated with the current model.  If it couldn&#39;t, I would look at what needed to be added or changed to make it work.  Re-examining the design helped me look at awkward areas and forced me to think about them over and over.  In the beginning the design refactoring took longer than the changes to add the feature.  I swear that this ratio changed as the model built up however.  At the moment the model is extremely extendible and I have a collection of &#39;patterns&#39; that I have used throughout the system that I can draw upon for new features.  I have not created frameworks, I have common code and patterns but &lt;b&gt;no frameworks!&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Getting Real&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;My experience with carcarecalendar.com does not match my experience on real projects with budgets and ROI.  Corporations are very concerned about risk these days.  Planning is still the favourite for risk mitigation.  I think planning is good as a communication tool.  It is very considerate to inform people that they will be needed to do some work for you, in advance, and preferably with a time frame.  I dislike &#39;emergency&#39; panic situations that come out of bad planning.  It is not this type of planning that I question.  It is system architecture and design.  My aging brain is having more and more difficulty remembering, and grasping massive and intricate systems (or I just don&#39;t care so much anymore).  I am more capable and successful at designing for a handful of needs than a multitude.  Using 100 requirements to accurately design a system is just not possible.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;But lack of planning means risk, doesn&#39;t it?  If we can plan, we must plan.  And system design is planning.  I would suggest that the only &#39;planning&#39; exercise that is worth pursuing is proof of concept type stuff.  Where you&#39;re breaking new ground.  Everything else is just project manager CYA.&lt;br /&gt;&lt;h3&gt;But UML?&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;UML should be used to describe a design as it exists at a certain point in time, not as a way to plan creation of software, but to  tell the story of already functioning code. By the way if you like UML, try &lt;a href=&quot;http://staruml.sourceforge.net/en/&quot;&gt;StarUML&lt;/a&gt; it&#39;s much better than Rose or Visio (it doesn&#39;t beat a white board though).&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Design as You Go&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;So dispense with the docs and pictures, keep things thin.  Nobody wants to read that stuff, and anyway software never turns out as designed.  Stop wasting time doing stuff you hate and start building, your software will be softer, your customers will be happier and you will be too.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/09/less-up-front-design-more-supple.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-115760277992394625</guid><pubDate>Thu, 07 Sep 2006 03:36:00 +0000</pubDate><atom:updated>2006-09-06T21:19:39.986-07:00</atom:updated><title>Agile Methodologies - Anti-Reusability?</title><description>&lt;p&gt;If you subscribe to the Agile notions of YAGNI (You aren&#39;t gonna need it) and DTSTTCPW (do the simplest thing that could possibly work), are you potentially writing code with limited reusablility?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Purpose of a Routine&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;In Steve McConnell&#39;s &lt;u&gt;Code Complete&lt;/u&gt; he identifies many reasons to create a routine.  Avoiding duplicate code is the most popular, but there are other reasons too.  One of them is &#39;Promoting code reuse&#39;.  In the Agile age then, is it still prudent to try to make code reusable?  Or should developers simply abstract when the need arises?  Developing for the immediate need, and not some uncertain future.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;If we apply the Agile XP practices in their purest form, it could be argued that new routines should only be created when needed.  Perhaps that&#39;s not quite right.  Perhaps we should create a function or method when a &lt;i&gt;refactoring&lt;/i&gt; requires it.  This still means blocking out any thoughts of reusability.  Making a method more &#39;reusable&#39; than necessary still contradicts the spirit of agile.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;For example, if my code always multiplies a number x by y and y is always 2 my function should look someting like,&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;public int multiplyXAxisBy2(x)&lt;br /&gt;{&lt;br /&gt;  return x * 2;&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;If my multiply method took two arguments x and y and multiplied them it could be argued that I am &#39;building for the future&#39;.  Alright, I&#39;ll admit this is a contrived and extreme example.  But I have seen developers argue for hard-coded values in their methods on the grounds that exposing those values as parameters violates the YAGNI principle.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Balance&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Like most things in software, there is no simple answer.  The best you can hope for is &#39;it depends&#39;.  In the case of agile design and reuse, the &#39;it depends&#39; postulate seems to hold.  YAGNI is perhaps a reaction to the &#39;modeling the world&#39; design dreams of the past.  It is a way to pull back on the programmers reigns and say &#39;Hey, the customer needs something real, today! stop dreaming and get on track&#39;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Achieving balance between YAGNI and REUSE means looking at you method interface and asking &#39;does it stand on its own, does it make sense?&#39;.  Constantly changing method names through refactorings is probably and indication of a poor interface.  The method names should hardly change at all, so make them specific and understandable.  The parameters should jive with the method name.  For example, a method like SaveAttachment() should take a parameter like an Attachment object.  It should not take parameters that leave the caller trying to understand the internals of the method.  Something like SaveAttachment(UserLogin, Attachment, UrlLink, AttachmentType) is probably a sign of a bad object design (some of these parameters should probably be contained in the object itself).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;So, in sum I have to say use your judgement and try to look at your interfaces in isolation, not as interconnected pieces, and hopefully you will create reusable code without straying too far from YAGNI.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/09/agile-methodologies-anti-reusability.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-115479995570069695</guid><pubDate>Sat, 05 Aug 2006 17:33:00 +0000</pubDate><atom:updated>2006-08-05T11:55:36.346-07:00</atom:updated><title>An Acceptable Validation Strategy</title><description>&lt;p&gt;How do you architect business validation logic without creating duplicate code, but ensuring a positive user experience?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Validation as Business Logic&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Mandatory data fields, or duplicate checks are user/business imposed requirements.  These types of requirements are best served by the code in the system that reflects the business - the Domain Model.  The business &#39;Domain&#39; is the part of the system that immediately reflects the needs and requirements of the business and other vested interests.  The &#39;Model&#39; or &#39;Domain Model&#39; is the code that implements these requirements.  All business validations must exist in the model or business layer.  This is why the model &lt;i&gt;exists&lt;/i&gt;, its job is to model the business, and it is the part of the system that is &lt;b&gt;accountable&lt;/b&gt; for the business requirements.&lt;/p&gt;&lt;br /&gt;&lt;h4&gt;Implementation&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;In C# I like to separate the model classes from other utility classes with a namespace, say the &#39;model&#39; namespace.  If the model is large, the key classes can be further separated into a &#39;core&#39; namespace.  At the moment my prefered method for communicating validation errors from the model is through exceptions, using the built in .NET framework exceptions and implementing my own custom exceptions (make sure to run FX Cop against your code to ensure your exceptions are CLS compliant).&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Validation as Presentation Logic&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Exceptions from the model layer passed up to the application screens lead to a very poor user experience.  The user is forced to work through each validation one at a time, and the exceptions messages may not be very helpful.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;It is not the responsibility of the model layer to solve this problem.  The model reflects and ensures the business requirements, not the user requirements.  The presentation layer is the user part of the system, if it enforces business requirements, it is merely doing this to improve the user experience.  Re-implementing business validation with validation controls or code behind checks is a good way to improve the user experience.  The presentation layer understands the &lt;i&gt;user&lt;/i&gt; and can provide far better messages and help than the model layer.  The presentation layer should wrap the model by either ensuring that the model doesn&#39;t return errors, or if that is not possible, by translating errors and retrieving other information about an error to help the user fix the problem.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Validation as Data Logic&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Databases also provide tools to enforce business logic.  Unique constraints, foreign key constraints, non-null fields, these are all database imposed business validations.  It is possible to structure a relational database without these things and make it the responsibility of the application to manage the data, but that is not a practical solution.  Many times data must be manipulated &#39;behind the scenes&#39;, usually for technical/performance reasons.  The database exists to store the model, and it must be able to do this reliably.  Reliablity and integrity is why validations exist in the database.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Validations in all Layers&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Although it may be more difficult to manage/change, a good system has business validations all layers.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Presentation Layer - screens may implement entry validations to provide a rich user experience&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Business Layer - Model objects enforce business rules because that is their job&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Data Layer - Databases enforce business rules to ensure data integrity, which ensures that the data layer will satisfy the demands of the model.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I have seen many strategies that try to move validation logic into a single place.  For example, Data driven strategies where a validation can be changed by simply updating a row in a table.  The &#39;&lt;a href=&quot;http://www.nakedobjects.org/static.php?content=naked-objects-pattern.html&quot;&gt;Naked Objects&lt;/a&gt;&#39; pattern attempts to improve transparency of business validations in a business layer.  Some of these strategies create a great deal of complexity and an unpleasant learning curve.  Homegrown meta-data systems require maintenance developers to essentially understand the entire system grasp the impact of a change.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In my straight forward model, admittedly, the simple task of making a field mandatory requires updating code in the presentation layer, changing the business model object, and setting not-null on the database table.  While this change has broad impact on the system, it is an intuitive change.  Each code change is highly isolated from the rest of the application.  It is very easy to understand a system that is implemented in this way, and that is very important.  Using a well understood validation pattern is acceptable, but creating an abstract/opaque solution reduces maintainability.  Nobody likes maintenance work, so make it &lt;b&gt;straightforward&lt;/b&gt;.  How the change is made must be obvious not necessarily easy.  Yes it is easy to change a validation by updating a row in a validation logic table but is it obvious?  What is the impact to the system?  How do you know it&#39;s going to work - talk to the developer that coded it?  Not acceptable.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Validations in each of the layers all stem from the same requirement, but have distinct purposes.  Trying to combine these purposes is difficult and probably not worth the complexity.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/08/acceptable-validation-strategy.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-115116497104102246</guid><pubDate>Sat, 24 Jun 2006 15:37:00 +0000</pubDate><atom:updated>2006-06-26T21:01:11.113-07:00</atom:updated><title>Can&#39;t Open Dump File in Visual Studio</title><description>&lt;p&gt;I don&#39;t seem to have the option to open &#39;Dump Files (*.dmp; *.mdmp)&#39; in Visual Studio&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Debugging with WinDbg&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;WinDbg is shipped with the &lt;a href=&quot;http://www.microsoft.com/whdc/devtools/debugging/default.mspx&quot;&gt;Debugging Tools for Windows&lt;/a&gt;, and it is considered the most powerful windows debugging tool.  It is truly a tool for the expert.  As a senior developer I am often called upon to &#39;fix the problem&#39; which is usually a production problem that cannot be reproduced in test.  Since this has become a frequent occurence, I have decided to take on learning how to use WinDbg.  Some good resources I&#39;ve found so far include the Microsoft Patterns and Practices document &#39;&lt;a href=&quot;http://msdn.microsoft.com/practices/Topics/manage/default.aspx?pull=/library/en-us/dnbda/html/dbgrm.asp&quot;&gt;Production Debugging for .Net Framework Applications&lt;/a&gt;&#39; and John Robbins&#39; &lt;a href=&quot;http://www.amazon.com/gp/product/0735615365/sr=8-2/qid=1151163946/ref=pd_bbs_2/104-2195655-6747120?ie=UTF8&quot;&gt;Debugging Applications for Microsoft .Net and Microsoft Windows&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Why Can&#39;t I Open Dump Files?&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;My current employer provides the development team with a &#39;corporate&#39; install of Visual Studio .Net 2003, which includes only the features they think we need.  Since   most development is done in C# and some in VB.Net, C++ is not included in the install.  I believe C++ is required to open dump files in Visual Studio .Net.  You can see what languages and tools you have installed by going to Help -&gt; About Microsoft Development Environment...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I wonder why I would have to have C++ installed to open dump files?  Presumably it is expected that dumps are generated for native C++ code, and if you don&#39;t develop in that language you don&#39;t look at dump files.  Maybe it is not useful to look at a dump of managed code in Visual Studio?  I imagine you would have to load the SOS debugger extension to debug a dump in studio, I seem to be having more luck using WinDbg anyway.  Stick with WinDbg, I sure it&#39;s worth the effort to learn.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/06/cant-open-dump-file-in-visual-studio.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114987982427145715</guid><pubDate>Fri, 09 Jun 2006 18:26:00 +0000</pubDate><atom:updated>2006-06-09T12:32:41.046-07:00</atom:updated><title>.Net Assemblies and Layered Architecture</title><description>&lt;p&gt;How should you physically implement a logically layered application?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;My Story&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;In the book &lt;a href=&quot;http://domaindrivendesign.org/book/&quot;&gt;Domain Driven Design&lt;/a&gt; Eric Evans encourages the use of &lt;a href=&quot;http://www.martinfowler.com/eaaCatalog/repository.html&quot;&gt;Repositories&lt;/a&gt;.  A repository is an object that is responsible for persistence and retrieval of Model or Business Objects.  The intent of the repository is to abstract the object storage mechanism.  The interface to a repository should not be specific to a database technology, or any technology being used to permanently store or &#39;persist&#39; objects.  This abstraction allows you to easily change the underlying storage technology.  Adding support for a new storage technology (I.e. another relational DBMS), means creating another matching set of repository objects.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Multiple Assemblies&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;With this in mind, I decided to create 2 assemblies to match the 2 layers.  A Domain Model assembly and a Persistence assembly.  The idea being that the persistence assembly could be dynamically loaded at runtime and thus chosen from a list of assemblies that support various DBMSes&lt;/p&gt;&lt;br /&gt;&lt;a href=&quot;http://photos1.blogger.com/blogger/2118/2562/1600/multipleAssemblies.0.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;&quot; src=&quot;http://photos1.blogger.com/blogger/2118/2562/320/multipleAssemblies.0.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;After running &lt;a href=&quot;http://www.gotdotnet.com/team/fxcop/&quot;&gt;FX Cop&lt;/a&gt; on my work, I tried adding the CLS Compliant flag to assembly.info to satisfy the FX Cop audit.&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;using System;&lt;br /&gt;[assembly:CLSCompliant(true)]&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The CLSCompliant attribute caused my Persistence assembly to fail on compilation, due to the fact that it referenced and returned types declared in &lt;strong&gt;another&lt;/strong&gt; assembly.  The other assembly was of course my Model assembly.  This got me thinking...&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Single Assembly&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;In other projects I have worked on, the persistence layer and model layer were combined in one assembly.  The repository classes were included in a seperate namespace (and project sub-folder) and were therefore still distinct from the model objects.  So &lt;strong&gt;logically&lt;/strong&gt; the layers were seperate, but the &lt;strong&gt;physical&lt;/strong&gt; implementation was a single assembly. The 2 layers worked very closely together and in many ways really acted like one layer.  So this configuration also made sense.&lt;br /&gt;&lt;a href=&quot;http://photos1.blogger.com/blogger/2118/2562/1600/singleAssembly.0.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;&quot; src=&quot;http://photos1.blogger.com/blogger/2118/2562/320/singleAssembly.0.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;The difficulty with the single assembly comes from the desire to &#39;swap&#39; between persistence technologies.  In the combined model, you can no longer load the repository objects you need at run-time.  Supporting multiple database technologies  requires the Domain assembly to contain multiple sets of repository objects collected in different namespaces, you would use the &lt;a href=&quot;http://en.wikipedia.org/wiki/Strategy_pattern&quot;&gt;Strategy pattern&lt;/a&gt; to provide the runtime loading (abstraction from specific types), and some sort of controller to serve up the concrete repositories (based on a config setting perhaps).  Each of the architectures has benefits and drawbacks.  I like to reduce my assembly count though, and systems don&#39;t generally support a huge set of relational database technologies.  So I think I will move my seperate assemblies into one.  As the XP mantra goes, &#39;you aren&#39;t gonna need it&#39; (Y.A.G.N.I.).&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/06/net-assemblies-and-layered.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114927575241569858</guid><pubDate>Fri, 02 Jun 2006 18:36:00 +0000</pubDate><atom:updated>2006-06-03T09:59:20.226-07:00</atom:updated><title>Application Specific Exception Classes</title><description>When should you create specialized exceptions in your C# application?&lt;br /&gt;&lt;p&gt;The .NET framework provides a few exceptions for use by applications, they are,&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;ApplicationException&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ArgumentException&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ArgumentNullException&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ArgumentOutOfRangeException&lt;li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;and others...&lt;br/&gt;&lt;br /&gt;These exception types are very useful and cover most cases.  But they are usually not enough.&lt;br /&gt;Let&#39;s say you have some special validation where arguments to a method have to &#39;jive&#39;.  Such as a ChangePassword method on a User object.  The ChangePassword method takes maybe three arguments, oldPassword, newPassword and verifyNewPassword.  The newPassword and verifyNewPassword arguments must match.  You could just throw an ArgumentException and populate the message appropriately.  This causes a potential issue since by raising a generic exception type you are forcing the &lt;i&gt;caller&lt;/i&gt; to handle the exception in a generic way.  If the caller wants to do something special for this error, it has to parse the error message. Not very nice.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Error Codes&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The structured programming world employs &lt;i&gt;error codes&lt;/i&gt; to relay the exception type.  You could implement this in C# by defining a special exception type extended from Exception and include an integer ErrorCode property. Then assign every specific exception type a number using a series of enum types defined in the custom exception class.  Use enumeration types to logically group exception types, and reduce the need to reserve blocks of numbers.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The error code solution works.  However, imagine what the handler looks like from the caller&#39;s point of view.  There is probably a switch statement of some kind.  Not a very Object Oriented construct.  In and Object Oriented design switch statements on enum codes do not belong.  See &lt;a href=&quot;http://www.refactoring.com/catalog/replaceTypeCodeWithSubclasses.html&quot;&gt;Replace Type code with subclass&lt;/a&gt;. Enter Custom Exception Classes.  &lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Custom Exception Classes&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;If you have error type codes, and you have switching logic on those codes, applying &#39;Replace Type Code with Subclass&#39; will lead you down the path of custom exceptions.  This essentially means creating a catalog of specialized exceptions derived from the Exception class in the System namespace.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Deciding what exception classes to create can be difficult.  Over specialized classes could result in a huge and unwieldy group of exceptions.  Under specialized classes are essentially meaningless.  As in all object modelling, you identify classes to represent &lt;strong&gt;concepts&lt;/strong&gt; in the domain.  Exceptions are no exception(!) to this rule.  Ask, what error condition &lt;strong&gt;concept&lt;/strong&gt; you are trying to communicate.  You might create an exception class to represent violation of an object relationship, or an exception to indicate an attempt to create a duplicate object.  These classes would most likely include information regarding the objects involved, so as much information can be relayed to the user interface.  Depending on the situation your exception classes may be more specific.  For example, you may wish to express certain &lt;i&gt;types&lt;/i&gt; of object relationship violation errors.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Be Agile&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Attempt to model your exceptions based on the current need.  Don&#39;t attempt to satisfy all possible scenarios.  If you know how the caller will deal with the exception, model to that requirement.  Extend later.  If you find yourself tempted to add error numbers and switch on them - refactor to create exception classes.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;It is somewhat painful to create exception classes &lt;strong&gt;properly&lt;/strong&gt;.  Run &lt;a href=&quot;http://www.gotdotnet.com/team/fxcop/&quot;&gt;FxCop&lt;/a&gt; against your assembly and you&#39;ll see what I mean.  If you are creating many exception classes it would be wise to create a codegen macro in VS, or at least have some code to copy/paste from to make it easier.  You may also find yourself creating exception classes that add nothing.  No additional data or methods, just a new type.  I think this is OK, so long as you have catch statements for those exception types.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/06/application-specific-exception-classes.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114870576439466021</guid><pubDate>Sat, 27 May 2006 04:04:00 +0000</pubDate><atom:updated>2006-05-26T21:56:04.443-07:00</atom:updated><title>Handling the Browser &#39;Refresh&#39; Button</title><description>&lt;p&gt;When the user hits the &#39;refresh&#39; button, the page resends the previous request to the server, which usually results in &lt;i&gt;unexpected behavior*&lt;/i&gt; for web applications (*bugs).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;One of the difficulties with building Web Applications is the fact that they are hosted within a &#39;browser&#39;.  The browser contains features that allow the user to customize their Internet browsing experience.  On this note, it is EXTREMELY annoying when an application attempts to mess around with browser settings, it not threatening (in a securityish sort of way).  So, in my opinion, disabling the refresh button is not an option!  Besides, the user can always hit ctrl-r to get a browser refresh (and yes you could probably catch ctrl-r with javascript but that&#39;s not the point).&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Response.Redirect&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;My current preferred method of dealing with the refresh button is to redirect back to the current page for all data modifying postback events.  Something like the following,&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;Response.Redirect(&quot;Model.aspx&quot;);&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;where Model.aspx is the current page.  It might not be a great idea to hardcode the page name (in case you want to change it).  I&#39;ve seen code where each page exposes a Url property.  In this case the code would look like the following.&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;Response.Redirect(this.Url);&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Placing these redirects in the event handlers ensures that if the user hits &#39;refresh&#39; immediately after a data changing postback the event will not be re-fired.  The refresh merely calls the redirect again (essentially) and reloads the page (as expected! how wonderful and easy too!).&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Drawbacks&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;While the redirect method works, it is not without its difficulties.  Remember, it is like a fresh navigation to the page, so it refires your &quot;if( !Page.IsPostBack )&quot; code.  This can be a problem.  Usually the &quot;!IsPostBack&quot; code populates list controls    and re-running this code will cause current selections to be lost (very irritating for the user).  Normally the ASP.NET Viewstate mechanism ensures the current selections on list controls are maintained through postbacks.  My solution to the list selection problem is to store the current selection in Session and set the list selection manually.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Besides the extra management of Session variables, there is also the performance issue to consider.  Each postback is now causing 2 hits to the server.  Potentially calling into the database for data already displayed on the page.  This seems like a high price to pay to deal with the &#39;refresh&#39; button.  Output caching may be a solution here...&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/05/handling-browser-refresh-button.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114806847883149803</guid><pubDate>Fri, 19 May 2006 19:02:00 +0000</pubDate><atom:updated>2006-05-19T12:55:58.926-07:00</atom:updated><title>Cost of Calling Methods in C#</title><description>&lt;p&gt;What does a method call cost in C#? Should I use temps to reduce method calls?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In university, so many years ago, we learned that one of the most performance intensive operations was the &lt;strong&gt;method call&lt;/strong&gt;.  The professor explained the work required to create a stack frame, and move values into it, etc.  Well... that was then, when maybe compilers weren&#39;t quite so efficient.  Is this still true?  What is the overhead when calling a method with a modern language like C#?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Why do I care about this?&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;&lt;a href=&quot;http://www.martinfowler.com&quot;&gt;Martin Fowler&lt;/a&gt; in his book &lt;a href=&quot;http://www.amazon.com/gp/product/0201485672/qid=1148068025/sr=2-1/ref=pd_bbs_b_2_1/102-1020092-8922519?s=books&amp;v=glance&amp;n=283155&quot;&gt;Refactoring: Improving the Design of Existing Code&lt;/a&gt;, (which I am currently re-reading) describes several &lt;strong&gt;method creation refactorings&lt;/strong&gt;, one of which is driven by the desire to remove temporary variables from a method (Replace Temp with Query).  The motivation behind this refactoring is based on the idea that temporary variables encourage large methods and make refactoring difficult.  Upon reading this refactoring I recalled the university lecture where we discussed the cost of calling methods and use of the C++ &lt;strong&gt;inline&lt;/strong&gt; keyword (inline is a C++ compiler hint to not actually create a function and call it, but to generate inline code instead).  In C++ &#39;inline&#39; exists because function calls can be expensive.  So I created a simple test to measure the overhead with method calls.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Method Call Overhead Results&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Here is the code I wrote to measure the method call overhead, one method that simply does a calculation inline, and another that calls a function to do the calculation.&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class MethodCallCost&lt;br /&gt;{&lt;br /&gt;  private int _iterations;&lt;br /&gt;  private int _valueA;&lt;br /&gt;  private int _valueB;&lt;br /&gt;&lt;br /&gt;  public MethodCallCost(int iterations, int valueA, int valueB)&lt;br /&gt;  {&lt;br /&gt;    _iterations = iterations;&lt;br /&gt;    _valueA = valueA;&lt;br /&gt;    _valueB = valueB;&lt;br /&gt;  }&lt;br /&gt;  public void MethodCall()&lt;br /&gt;  {&lt;br /&gt;    double temp = 0;&lt;br /&gt;    for( int i = 1; i &lt; _iterations; i++ )&lt;br /&gt;    {&lt;br /&gt;      temp = CalculateAmount(_valueA, _valueB, i);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  private double CalculateAmount(int valueA, int valueB, int divisor)&lt;br /&gt;  {&lt;br /&gt;    int result = valueA * valueB / divisor;&lt;br /&gt;    return result;&lt;br /&gt;  }&lt;br /&gt;  public void Inline()&lt;br /&gt;  {&lt;br /&gt;    double temp = 0;&lt;br /&gt;    for( int i = 1; i &lt; _iterations; i++ )&lt;br /&gt;    {&lt;br /&gt;      temp = _valueA * _valueB / i;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;I created the MethodCallCost object to run 3,000,000 iterations.  Here are some results,&lt;br /&gt;&lt;table border=&quot;1&quot; cellpadding=&quot;1&quot;&gt;&lt;br /&gt;&lt;tr&gt;&lt;th nowrap&gt;Method Call (Ms)&lt;/th&gt;&lt;th nowrap&gt;Inline (Ms)&lt;/th&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;93&lt;/td&gt;&lt;td&gt;47&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;110&lt;/td&gt;&lt;td&gt;47&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;93&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;94&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;94&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;94&lt;/td&gt;&lt;td&gt;47&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;94&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;94&lt;/td&gt;&lt;td&gt;47&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;109&lt;/td&gt;&lt;td&gt;63&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;78&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;So, my conclusion (with this simple and perhaps insufficient test) is that method calls are cheap.  The benefits of the &#39;Replace Temp with Query&#39; are probably worth it.  Now of course you may have a complex method that calls a webservice, or into a database, and in that case of course it probably makes sense the store the result instead of requerying.  There are still judgement calls to be made, but go ahead and create method calls, just remember to profile and tune.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/05/cost-of-calling-methods-in-c.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114784448351802947</guid><pubDate>Wed, 17 May 2006 04:32:00 +0000</pubDate><atom:updated>2006-05-16T22:42:19.446-07:00</atom:updated><title>UrlReferrer - Handle with Care</title><description>&lt;p&gt;What page was the user on before this one? Hey! Reponse.UrlReferrer seems to have that information.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Ok, I&#39;ll admit I&#39;m a little scared of this feature.  It seems to undermine the &lt;b&gt;atomicity&lt;/b&gt; of web requests.  If I could trust it though, UrlReferrer would be extremely handy for a web app with sophisticated navigation.  Imagine multiple ways to get to a screen (as any &lt;strong&gt;good&lt;/strong&gt; app should allow), and the user hits the &#39;cancel&#39; button, and magically is returned to the previous screen.  After all, the user&#39;s natural expectation is to be returned to the screen they were just on isn&#39;t it?.  Here&#39;s some code that does just that.&lt;/p&gt;&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private void btnCancel_Click(object sender, System.EventArgs e)&lt;br /&gt;{&lt;br /&gt;  if(Request.UrlReferrer != null)&lt;br /&gt;    Response.Redirect(Request.UrlReferrer.AbsoluteUri);&lt;br /&gt;  else&lt;br /&gt;    Response.Redirect(&quot;Dashboard.aspx&quot;);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;This code ensures that the UrlReferrer HTTP Header is set, and if it is redirects the browser to the referrer page.  Otherwise the user is sent to a default page.  Note here, NUnitAsp doesn&#39;t set the UrlReferrer header hence my null check.  But...&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Postbacks&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;If the page posts back, the Url Referrer is set to the current page, and your navigation is now broken.  You either have to store the referrer in the page load event for later use, or not put any postbacks in the page (danger! danger! you will probably forget about this and break your redirection).  I don&#39;t know about you, but my pages post back &lt;strong&gt;a lot&lt;/strong&gt; especially in their immature state.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Server.Transfer&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;You can&#39;t use Reponse.Redirect to navigat to pages that reference the UrlReferrer.  Reponse.Redirect causes the browser to send a GET request for the new page.  The subsequent requests, such a POST to go to another page, now have the Url Referrer of the current page because of the GET request made by Response.Redirect (or something like that, &lt;a href=&quot;http://www.ethereal.com&quot;&gt;trace the packets&lt;/a&gt; and you&#39;ll see what I mean).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The more I write about it, the more convinced I am to avoid Url.Referrer.  The design limitations to &#39;make it work&#39; are extremely constraining and easy to forget.   It doesn&#39;t work with NUNitAsp (important for me, anyway).  I am also unsure of which browsers even support this header.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Alternatives&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;You can potentially change your navigation strategy and use the bread crumb trail approach.  Just save a navigation tree in the user&#39;s Session.  I have also had some success passing navigation information as URL Query parameters (I.e. page.aspx?PreviousPage=Main.aspx).  UrlReferrer is a fragile construct, try to find another way.</description><link>http://daftdeveloper.blogspot.com/2006/05/urlreferrer-handle-with-care.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114749283268875860</guid><pubDate>Sat, 13 May 2006 03:48:00 +0000</pubDate><atom:updated>2006-05-12T21:00:32.696-07:00</atom:updated><title>Protected or Private?</title><description>&lt;p&gt;As of late, I&#39;ve been setting the access level on class properties to protected.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I was creating a class, and the protected keyword came up in intellisense and I paused to think.  Maybe if a class inherits from this class it would be useful to have access to the member data of the class, the same goes for private methods, why not make them protected?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;So, right now, I am generally going with &#39;protected&#39; for all internal class stuff.  Classes that I &lt;strong&gt;know&lt;/strong&gt; will not be extended will have private members, I ensure to &lt;strong&gt;seal&lt;/strong&gt; those classes.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I imagine that a truly carefully designed class has a mix of private, protected and public access levels, and that setting everything internal to &#39;protected&#39; is a bit naive. but for now...&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/05/protected-or-private.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114727660326249942</guid><pubDate>Wed, 10 May 2006 15:32:00 +0000</pubDate><atom:updated>2006-05-10T08:58:02.913-07:00</atom:updated><title>OleDbCommand Parameters - Order Matters</title><description>&lt;p&gt;Having trouble with your MS Access Update statement?  Check your parameter order.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;So I&#39;ve written a small parameterized Update statement to create an OleDbCommand.&lt;br /&gt;I was surprised to find that my Update command was returning 0 rows updated.  Everything looks fine on inspection, so I pull the command into MS Access and replace the parameters with the specified values and it works fine.  My code looks like this,&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;string sql = &quot;UPDATE tblMemberVehicle SET MemberId=@MemberId, VehicleTypeId=@VehicleTypeId, Identifier=@Identifier WHERE Id=@Id&quot;;&lt;br/&gt;&lt;br /&gt;OleDbCommand dbCommand = new OleDbCommand(sql, conn );&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@Id&quot;, OleDbType.Integer).Value = vehicle.Id;&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@MemberId&quot;, OleDbType.Integer).Value = vehicle.User.Id;&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@VehicleTypeId&quot;, OleDbType.Integer).Value = vehicle.VehicleType.Id;&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@Identifier&quot;, OleDbType.VarChar).Value = vehicle.Identifier;&lt;br/&gt;&lt;br /&gt;&lt;/div&gt;Parameter names all match nicely, no exceptions from the database.  I check the rows updated after running my ExecuteNonQuery statement, and always - 0 rows updated.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I have another update statement which is working, so I take a quick look at it.  Lo and behold, the @Id was the &lt;strong&gt;last&lt;/strong&gt; parameter added, and it corresponds with the parameter order as it appears in the SQL statement.  Could it be that OleDbCommand works just like OdbcCommand and requires parameters specified in the SQL specified order?  I begin to suspect that the parameter names are actually meaningless and conduct a small experiment.  I change the parameter names to nonsensical names and leave the sql parameter names alone.&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;&lt;br /&gt;string sql = &quot;UPDATE tblMemberVehicle SET MemberId=@MemberId, VehicleTypeId=@VehicleTypeId, Identifier=@Identifier WHERE Id=@Id&quot;;&lt;br/&gt;&lt;br /&gt;OleDbCommand dbCommand = new OleDbCommand(sql, conn );&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@Foo&quot;, OleDbType.Integer).Value = vehicle.User.Id;&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@Bar&quot;, OleDbType.Integer).Value = vehicle.VehicleType.Id;&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@Try&quot;, OleDbType.VarChar).Value = vehicle.Identifier;&lt;br/&gt;&lt;br /&gt;dbCommand.Parameters.Add(&quot;@This&quot;, OleDbType.Integer).Value = vehicle.Id;&lt;Br/&gt;&lt;br /&gt;&lt;/div&gt;Any guesses as to what happens?  Surprise, surprise, this code works.  The parameter names are truly meaningless, well against MS Access anyway.  I believe SQL Server respects these parameter names and actually &lt;strong&gt;uses them&lt;/strong&gt;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;And all this time I&#39;ve been sooo careful about my parameter names, sigh.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/05/oledbcommand-parameters-order-matters.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114715047156375490</guid><pubDate>Tue, 09 May 2006 03:42:00 +0000</pubDate><atom:updated>2006-05-08T21:54:34.586-07:00</atom:updated><title>Returning Null Objects</title><description>&lt;p&gt;What does it &#39;mean&#39; when a method call returns a Null object?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I believe you must define interfaces very carefully.  Firstly, because once an interface is in use it is difficult to change later.  Second, because every method name, parameter, output is part of the description of what the interface does.  The interface &#39;expresses&#39; a mental model to the developer using it.  It explains how the library works, or presents a mental model that can be used to apply the library effectively.  The interface tells the developer how the library &#39;works&#39;, so while the code only deals with inputs and outputs, we developers use interface I/O to fabricate an understanding of what&#39;s happening under the covers.  It is therefore important to  specify an interface carefully.  So, with that in mind, I propose a few instances where it makes sense to return a Null object from a method.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Errors&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;If you are not using exceptions to relay errors, or unexpected system conditions, and you are instead setting a global or passed-in error structure, your method should return a Null object reference.  You don&#39;t want the processing to continue as if nothing was wrong.  In the case of an error, the application should proceed into recovery mode.  Oh look, I got a Null, something bad must have happened.  I can either check the error structure and avoid using the Null object, or I can ignore the error structure and get an Null Object exception (No! Bad programmer!).&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Object not found&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;In many cases I have returned Null from a &#39;find&#39; or &#39;get&#39; method when it was unable to retrieve a requested object.  While it is necessary to check the return value for Null in these cases, the implementation is simple.  A Null object is sometimes even acceptable, as it forms an input to another method which allows a null parameter.  I am still in favor of this use of Null object references.  After all the database supports the concept of Null too.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Null Object Pattern&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The &lt;a href=&quot;http://www.cs.oberlin.edu/~jwalker/nullObjPattern/&quot;&gt;Null Object Pattern&lt;/a&gt; entails essentially &#39;stubbing&#39; out methods and creating an indistiguishable object from the real object.  The client object can use the Null object as it would the real thing, and no unpleasant Null checks are required.&lt;br /&gt;I believe the Null Object Pattern is only useful for stubbing out code, where the object&#39;s behavior is not important to the client object.  Maybe I can stub out the application logging object for example.  If I haven&#39;t configured my logging, the logging system returns a Null logging object that doesn&#39;t fail on the log call, but does nothing.  I can&#39;t however, stub out the Math object from which I am expecting performance of important calculations.  It seems that I would be at risk of introducing difficult to find bugs, I would rather get an &#39;object reference not set&#39; exception than a series of zeroes displayed in a report.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Empty List or Null&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;If my method returns a list of objects, and there are no objects to return, it makes sense to me to return an &lt;strong&gt;empty list&lt;/strong&gt;.  See &lt;a href=&quot;http://blogs.sun.com/roller/page/tor?entry=code_advice_9_avoid_null&quot;&gt;Tor Norbye&#39;s blog&lt;/a&gt; post on this.  You certainly can&#39;t return either Null or empty list and ascribe the same meaning to both, they&#39;re two different things.  Empty list is easy, no objects found.  Null?  That just means an error to me, like the object wasn&#39;t properly initialized or something.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/05/returning-null-objects.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114659633325603926</guid><pubDate>Tue, 02 May 2006 18:19:00 +0000</pubDate><atom:updated>2006-05-02T12:01:10.423-07:00</atom:updated><title>Repository Create Pattern</title><description>&lt;p&gt;I&#39;m working on creating a new object creation pattern.  This new pattern is an elegant solution that fits within the Repository pattern described in &lt;a href=&quot;http://domaindrivendesign.org/book/&quot;&gt;Domain Driven Development&lt;/a&gt; by Eric Evans.  I&#39;m trying to ensure the following constraints in my application,&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Objects are valid at all times&lt;/li&gt;&lt;br /&gt;&lt;li&gt;An Entity object without an identifier (db key) is invalid&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Don&#39;t want to have to use GUIDs as identifiers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Objects that cannot be saved (because of their state) are invalid&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Essentially, if an object exists I must be able to save it without getting constraint errors from the database.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Introducing &#39;Repository Create&#39;&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The Repository Create pattern works a lot like the factory patterns.  You &lt;strong&gt;create&lt;/strong&gt; objects &lt;strong&gt;through&lt;/strong&gt; the repository.  &#39;Entity&#39; objects (objects that must be persisted) must all be created by a repository.  That repository ensures uniqueness of business keys (if there are any) and applies an id to the new object.  Any errors creating this new object and &#39;no object for you!&#39;.  The application never has partially formed, or duplicate objects floating around.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;What about updates?  Model objects should not permit updating of their keys.  Simple.  But what if I modify a property and thus make it a duplicate?  You shouldn&#39;t be able to do this.  Properties that are part of the uniqueness constraint must be modified through the repository, by a repository &lt;strong&gt;Update&lt;/strong&gt; method.&lt;br /&gt;&lt;h3&gt;An Example&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Let&#39;s say I&#39;m writing a project management application and I have a &#39;Project&#39; object.   The Project object must have a unique name so users can identify it, but that name can change too.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I simply create my project objects by calling &#39;create&#39; and passing the project name to my project repository.&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;Project p = projectRepository.Create(&quot;Project 1&quot;);&lt;/div&gt;If the name Project 1 is a duplicate I get an exception from the repository.  If not, I get a new Project object, with a valid database Id and I am assured the name is not duplicated.  Updating the name would look something like this&lt;br /&gt;&lt;div class=&quot;codeblock&quot;&gt;pRep.Update(p, &quot;Project One&quot;);&lt;/div&gt;Access to the project name has to be restricted either by using the C# internal keyword or Interface casting* (*A slippery way to implement &#39;friends&#39; in C#).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;This is the Repository Create pattern in a nutshell.  It seems to be working fairly well so far, of course my requirements have also been fairly simple and I haven&#39;t had to do much optimization.  More to come on this pattern...&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/05/repository-create-pattern.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114645930095748561</guid><pubDate>Mon, 01 May 2006 04:11:00 +0000</pubDate><atom:updated>2006-04-30T21:55:00.993-07:00</atom:updated><title>Apply a Default Sort Order</title><description>&lt;p&gt;Always sort your object lists&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I tend to get pretty lazy with my coding sometimes.  My TODOs occasionally require more typing than it would take to actually do the TODO (I sometimes get lazy with the thinking part).  So why would I recommend* (*insist on) sorting every list of objects in your system?  Two reasons.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;It makes testing a whole lot easier&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You&#39;re less likely to have an unsorted list of items appear in your GUI&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I have only just started doing this with my code* (* I may post a more educated and experienced blog later which includes the phrase &#39;it depends&#39; when talking about when to sort lists) and it seems to be paying of so far.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Writing Tests on Sorted Lists&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;In the past I would put together a small &#39;IsInList&#39; method to test that an object was in a list.  Or I would create a &#39;GetObjectInList&#39; to get an object and test it for expected values.  The fact that the objects were stored in a random order of course made this necessary.  The unsorted nature of the lists also meant the search had to be sequential which seemed amateurish (the performance implications were negligible since the lists were tiny, but...).&lt;/p&gt;&lt;p&gt;In any event, the nice thing about using a database is the ease of adding sorting.  With a simple ORDER BY in MS Access I can sort on any column ascending or descending.  Very little code.  The .Net Framework also provides some simple and powerful ways to sort lists. Sorting my lists makes testing easier since object location is predictable.  Testing is all about ensuring predictability anyway, right?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Your GUI lists should have default sort orders&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;If you have object lists and a GUI, you probably have list controls.  I&#39;ll bet users are expecting data in those lists to be sorted either according to a scheme of their choosing, or in a commonly expected order.  Ah, but if you use a layered design* (*like most kind-hearted developers) the question here becomes... where should sort logic exist?  In the presentation layer or in the business model?  I prefer a thin* (*super thin) presentation layer, the only code in the presentation layer should deal with form and page controls.  I suggest then, that sorting objects is the responsibility of the model and is a &lt;strong&gt;business rule&lt;/strong&gt;.  Sorting is part of the analysis of data, like calculations.  Testing logic in the GUI is also more difficult, so move as much logic as possible into to the model layer.  If you are consciously sorting your object lists and your tests are checking this, there is very little chance of a nasty unsorted list control bug appearing in your gui (this happened to me once, and was caught by a customer! egad.)&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The .Net Framework offers some powerful sorting tools namely the IComparable interface, and Comparer objects.  These two tools give you unlimited sorting power, easily and elegantly.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/04/apply-default-sort-order.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114593826322747427</guid><pubDate>Tue, 25 Apr 2006 03:24:00 +0000</pubDate><atom:updated>2006-04-24T21:11:03.310-07:00</atom:updated><title>ASP.Net Timeout</title><description>&lt;p&gt;HTTPUnhandledException: Request Timed Out.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Every now and then, when a request had a lot of work to do, I would get this exception.  It didn&#39;t &#39;ripple up&#39; from a method in the regular way that an exception does.  It just caused execution to cease and was caught by the default error handler.  There are a few places where timeouts are set.  One is in IIS.  In the Web Site properties there is a setting called &#39;connection timeout&#39;.  I thought this might be the  candidate.  So I increased the timeout, but no luck.  The operation causing the timeout was an data integration over a network.  The integration API had a connection timeout setting too.  Changing this timeout had no effect.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Another strange behavior was that in debug mode the problem went away.  The operation would complete without the time out.  Sadly, I had to debug the problem in release mode, with many symbol files missing.  At around the same amount of time I would get an &#39;ObjectDisposedException&#39; on my NetworkStream object, followed by a ThreadAborted exception.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Turkey Trails&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I found some threads online about the .Net garbage collector prematurely destroying objects if it thought they were out of scope (or might as well be since there were no subsequent references to them).  So I daftly started adding Garbage Collector control statements to my code to preserve my objects.  Of course this just &#39;felt&#39; wrong, but hey, desparate times...  Well of course no luck.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Day 2&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;It always helps to be fresh, and to bring in a fresh perspective.  So, with the help of a coworker we clarified 2 important clues.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Something is terminating the worker thread&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The thread is terminated after 90 seconds&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;There was definitely a timeout &lt;strong&gt;somewhere&lt;/strong&gt; which was bringing everything to a halt.  I already knew it wasn&#39;t the IIS connection timeout, but what about this ASP Script timeout under virtual directory configuration?  It was set to 90 seconds too!  Alas, it was not the culprit.  Which of course made sense after I tested it, but ya never know.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Almighty Interweb&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;How did I code before the Internet? (Yes I am that old).  Some Googling later I discovered, da na na naah!  An obscure, yet highly important setting in machine.config.  Something that ASP.Net uses to terminate threads.  Something that is defaulted to 90 seconds.  The answer, the culprit!  A glorious little piece of XML.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;ExecutionTimeout&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Allow me to introduce &lt;strong&gt;executionTimeout&lt;/strong&gt;.  It looks like this,&lt;br/&gt;&lt;br /&gt;&lt;codeblock&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;&amp;lt;system.web&amp;gt;&lt;br /&gt;&lt;spacer type=horizontal size=20&gt;&amp;lt;httpRuntime executionTimeout=&quot;90&quot;/&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&amp;lt;/system.web&amp;gt;&lt;br /&gt;&lt;/codeblock&gt;&lt;br /&gt;It is set to 90 seconds in machine.config, and does not appear in the default web.config file that VS.NET creates for Web Applications.  Every ASP.NET developer should be aware of this setting.  Why is it kept hidden away?  Ahhh, the eternal mysteries...  Anyhow, there it is.  Add it to your web.config.  Pass it on.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/04/aspnet-timeout.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114556067274093347</guid><pubDate>Thu, 20 Apr 2006 18:21:00 +0000</pubDate><atom:updated>2006-04-20T12:17:52.800-07:00</atom:updated><title>ToString()</title><description>&lt;p&gt;What should I do with ToString()?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In the Microsoft .Net Framework, ToString is defined in &lt;i&gt;Object&lt;/i&gt; and is therefore ubiquitous (and everywhere too).  You have the chance to override ToString whenever you want.  But if you&#39;re thinking &#39;hmm... I don&#39;t override tostring() very much&#39;, then I would suggest maybe it&#39;s time to start.  It certainly is handy to call whenever you need to output an error message.  I almost just expect it to work (but nothing in life is free).  Just call ToString() on your object and add to your trace messages.  Very nice, but for the fact that error log messages aren&#39;t the only possible use for ToString().  Alas, with such a ubiquitous method, come multitudinous options for usage.  Int.ToString() is an example of the obvious and straightforward, but what do you with an object with multiple properties and subobjects, and inheritences and so forth?&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Proposed Behavior&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I like the idea of ToString() displaying object &lt;strong&gt;identity&lt;/strong&gt;.  If you work with objects that model something real, like a business concept then output information that identifies the object.  Not the internal key or database id, but the &lt;strong&gt;natural key&lt;/strong&gt;.  The data that people undestand to represent the essence of the thing.  For instance, if your object represents a customer, ToString() would return the customer&#39;s name.  Return a string that would be identifiable to a user using your software.  Take a look at how DateTime.ToString() works, it is a good, simple model.  The default ToString() returns the date as a string formatted according to the current thread culture.  It displays the date in a way that I can read it.  DateTime.ToString() takes the various members (day, month, year etc.) of DateTime and assembles them into a reasonably formatted presentation.  Most objects have a &#39;name&#39; property or a similar identifier.  This is what ToString() should output.  Converting an Address object with ToString() would probably output the address as it would be written on a letter.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Complex Objects&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;What about something like a Report object?  Should ToString() return the report content as a properly formatted string?  I would suggest that this kind of behavior is beyond the intent of ToString().  I would be inclined to only output details that again, &lt;strong&gt;identify&lt;/strong&gt; the report, like its title, run date, and so on, not the content of the report.  This should be left to specialized methods that can be expanded to more flexible use.  Output a string that would would be written on a file folder tab.  The content is &lt;i&gt;in&lt;/i&gt; the folder, ToString is the human readable index.  The information you would need use to retrieve the object.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Format Providers&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Now it gets complicated (well, more complicated for me). What about customizing the output of ToString()?  Back to my favorite, DateTime.  People want to see dates in all sorts of different ways.  DateTime.ToString() solves this by taking an optional IFormatProvider to display dates according to cultural preferences.  Exposing ToString() with parameters is a good way to provide extra control over formatting.  Again it still only outputs identity, just in a different format.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I am starting to implement the base ToString() on every class I write, and it is very convenient.  I truly dislike seeing the Namespace.classname output when I call ToString(), very annoying in my view.  Implement ToString and make users of your code happy (including yourself).&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/04/tostring.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114533425926987571</guid><pubDate>Tue, 18 Apr 2006 04:14:00 +0000</pubDate><atom:updated>2006-04-17T22:03:19.436-07:00</atom:updated><title>Elegance is Not Optional</title><description>&lt;p&gt;I have an old book (1994) on Prolog called &lt;u&gt;The Craft of Prolog&lt;/u&gt; written by Richard A. O&#39;keefe (MIT Press).  One of the crucial &#39;values&#39; of the book is that &quot;Elegance in Not Optional&quot;.  I find this to be a very striking statement, as I have always felt that design suffered under the realities of practical use.  Application performance always seems to ruin a program.  It may be that Prolog, because it runs in a isolated workspace has the pleasure of elegance.  There are no nasty operating systems or databases to deal with.  Yet, I am still drawn to this vision, I want to believe.  I have added a small quotation from the text.&lt;/p&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;h3&gt;Elegance is not optional (1)&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;What do I mean by that?  I mean that in Prolog, as in most halfway decent programming languages, there is no tension between writing a beautiful program and writing an efficient program.  If your Prolog code is ugly, the chances are that you either don&#39;t understand your problem or you don&#39;t understand your programming language, and in neither case does your code stand much chance of being efficient.  In order to ensure that your program is efficient, you need to know what it is doing, and if your code is ugly, you will find it hard to analyse. (&lt;u&gt;The Craft of Prolog&lt;/u&gt;, Richard A. O&#39;keefe, MIT Press 1994)&lt;/p&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;I believe that a developer should strive for readability first and foremost.  Most of the guidance in &lt;u&gt;Code Complete&lt;/u&gt; (Steve McConnell, Microsoft Press) is aimed at improving code readability.  I would even go so far as to say readability first, function second.  You can always debug a readable program and make it right.  But a mess of code that works might as well not exist since any changes to it probably mean rewriting it anyway.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Readability is About Design&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Comments, documents, unit tests, all of these things merely &lt;b&gt;support&lt;/b&gt; readability and understandability.  In the way that a textbook supports a professor.  The professor must still be able to teach and must stand on their own with or without the text to be truly effective.  Such is also true with code.  It should make sense without the supporting pieces, since so often these pieces fall into disrepair.  Code is never 100% covered by tests.  Comments are usually sparse, out of date or poorly written, as with documentation.  The organization and naming in the code must point to its intent and function.  The code &lt;strong&gt;expresses&lt;/strong&gt; the solution, it doesn&#39;t just implement it.  This should be the developers prime concern, express the problem and its solution with the program itself.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Elegant Design&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;What is elegance?  Have you ever heard the expression &#39;Form follows function&#39;?  Everything in the system must have a good reason for being there.  Nothing is superfluous, there is no programmer ego, or &#39;interesting&#39; technology, or &#39;feats&#39; in the code.  The code should read like a well written technical document.  Step by step to a predicatible ending.  Detail at each level is consistent, methods and data are grouped logically.  Nothing is out of place or mysterious.  Design also subscribes to the expression &#39;Function follows form&#39; whereby choosing an elegant form results in a superior design.  This implies an intuitive element to coding where you can&#39;t just apply rules and refactorings. If it looks good it probably is good.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;If you understand the problem, you should be able to look at the solution and say ahhh... yes, I see.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/04/elegance-is-not-optional.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-24679746.post-114525070031499280</guid><pubDate>Mon, 17 Apr 2006 04:49:00 +0000</pubDate><atom:updated>2006-04-16T22:39:29.616-07:00</atom:updated><title>Exceptions vs Returns Codes - Performance Implications</title><description>&lt;p&gt;I am working on inventing or finding a satisfactory validation strategy for my .Net web applications.  The final model must appease the following criteria,&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;I must be able to validate the entire screen in one shot.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There must be no duplicated code.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Objects in the system must always be &#39;valid&#39;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I want to use as much C# as possible, and minimize javascript&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;I must be able to validate the entire screen in one shot&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;This is purely a usability concern.  Who wants to go through one error at a time on a page?  Fix this, now fix that, opps... this one too.  Oh, that would be a duplicate, you&#39;ll have to come back later.  I think the system should be able to point out all problems immediately.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;There must be no duplicated code&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I don&#39;t want to maintain two sets (or more) of validation code.  As in, validation objects on the page, and validations in the model layer.  This requirement is in conflict with the other two, which makes my demands demanding.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Objects in the system must always be valid&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I don&#39;t like the idea of an object being somehow malformed or incomplete.  A situation where I have to check an object before I do anything seems to contradict my understanding of encapsulation.  An object that starts throwing errors because it is invalid contradicts the very reason for using objects.  Which is, &lt;strong&gt;keeping things simple&lt;/strong&gt;.  This requirement conflicts with requirement 2 because now you can&#39;t just take the validations out of the object, to ensure validity the object has to do validation.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;I want to use as much C# as possible, and minimize javascript&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I like C#.  Visual Studio is an excellent IDE for coding in C#.  C# is strongly typed and Object Oriented and is a fine language all aroung.  I do not like Javascript.  It is not strongly typed, and not really Object Oriented.  Javascript is difficult to debug and VERY browser specific.  The end.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The requirements may be asking for too much, but I feel that there is somehow a way to satisfy them all, and still have a fairly elegant solution.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Back to Performance&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;So, I am currently comparing validation through exceptions against the more traditional (pre OO) return codes.  My first test is performance.  I have read much on the Internet regarding the poor performance of exceptions so I wanted to see it for myself and get some hard numbers.  The evidence indicates that Exceptions are slower (Using 1.1 of the .Net Framework).  Slower by about 100%!&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Tests&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The tests were fairly simple.  I created an class whose constructor took 2 arguments, a string and an integer.  If the string was null, or empty that was a validation error.  If the int wasn&#39;t between 0 and 100 that was an error.  The exceptions method just threw the various &#39;Argument&#39; exceptions provided by the framework and filled in a nice descriptive error message which was passed to Debug.WriteLine.  The Return code method was more difficult to code and even required a trip through the debugger (Remember, I am a bit daft after all).  I had to pass an error object into the class constructor so I could get the same amount of information about the validation errors.  Foolishly I declared this object as a &#39;struct&#39; which meant it was being copied to the stack and thrown away, with all my error information too!&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Results&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;My test tried to create an object with a problem that would be caught with each of the validations. This code was run in a 10,000 iteration loop, with DebugView (&lt;a href=&quot;http://www.sysinternals.com&quot;&gt;sysinternals.com&lt;/a&gt;) open to verify the output.  Here are the numbers (ms) I got for 8 runs&lt;/p&gt;&lt;br /&gt;&lt;table&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Exceptions&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Return Codes&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;9,157&lt;/td&gt;&lt;td&gt;5,234&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;9,953&lt;/td&gt;&lt;td&gt;5,204&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;9,247&lt;/td&gt;&lt;td&gt;5,235&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;10,469&lt;/td&gt;&lt;td&gt;5,360&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;10,204&lt;/td&gt;&lt;td&gt;5,484&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;10,203&lt;/td&gt;&lt;td&gt;4,734&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;10,875&lt;/td&gt;&lt;td&gt;4,610&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;10,843&lt;/td&gt;&lt;td&gt;5,156&lt;/td&gt;&lt;tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;I can&#39;t account for why the exceptions test speed slowed down.  It would probably be worth looking into.  The tests were extremely quick to run considering they were attempting to create 30,000 objects each.  I was amazed that so many exceptions could be handled in 10 seconds.  So, while the exceptions method is much quicker to code and debug, the return codes method is the clear winner when it comes to performance.&lt;/p&gt;</description><link>http://daftdeveloper.blogspot.com/2006/04/exceptions-vs-returns-codes.html</link><author>noreply@blogger.com (Seann Hicks)</author><thr:total>0</thr:total></item></channel></rss>