In a recent blog posting, I talked about convincing a client it’s time to refactor. Well, I have spent the last few days convincing myself that it is time to do some refactoring of SMTP Diagnostics.
For those who don’t know, SMTP Diagnostics is a mailer program enabling you to troubleshoot problems with outgoing email and assist with configuring outgoing (SMTP) servers.
I have been putting off refactoring while still adding new features. I did this to “save time” but the code base has reached a point where it is now more time consuming then it should be to add new features. I can put it off no longer.
What’s fun about this exercise is that I get to build from the knowledge obtain thus far. Version 1.0 did not have a solid object model and a lot of the code was specific to this one project. Version 1.2 and 1.3 moved away from that by incorporating common code, or a common framework, that I’m using in a second product code-name Vertigo. Version 1.4, which is the next release, will finally have a reusable object model that will make adding new features much easier. For example, adding command line support will be a snap once the new object model is in place.
Look for the beta release for version 1.4 in a few days.
Tonight I discovered an interesting Visual Studio.NET 2003 bug. Seems devenv.exe will throw an unhandled exception when the constant in the sample code below is compiled. From what I can tell, it looks like devenv.exe chokes on the length of the contant. Remove line 73 (LEFT OUTER JOIN…) and the exception is not thrown.
The exception only occurs when compiling the project from within VS.NET 2003 (devenv.exe). The command line compiler csc.exe does not have the problem. Also, the problem does not occur if you are using Delphi 2005. Makes me wonder what devenv.exe is doing during the project build process.
<pre>
001 using System;
002
003 namespace ConsoleApplication3
004 {
005 /// <summary>
006 /// Summary description for Class1.
007 /// </summary>
008 class Class1
009 {
010 /// <summary>
011 /// The main entry point for the application.
012 /// </summary>
013 [STAThread]
014 static void Main(string[] args)
015 {
016 const string SQL_FORMAT = @”
017 SELECT
018 1 AS Tag,
019 NULL AS Parent,
020 NULL AS [users!1],
021 NULL AS [user!2!strUSID],
022 NULL AS [user!2!strPrefixID],
023 NULL AS [user!2!strFirstName],
024 NULL AS [user!2!strLastName],
025 NULL AS [user!2!strUSName],
026 NULL AS [user!2!strAdd1],
027 NULL AS [user!2!strAdd2],
028 NULL AS [user!2!strTown],
029 NULL AS [user!2!strState],
030 NULL AS [user!2!strPostCode],
031 NULL AS [user!2!strCountry],
032 NULL AS [user!2!strTel],
033 NULL AS [user!2!strFax],
034 NULL AS [user!2!strEMail],
035 NULL AS [user!2!strLocked],
036 NULL AS [user!2!dtmLockedDate],
037 NULL AS [user!2!strSuspenseReason],
038 NULL AS [user!2!intLogonCount],
039 NULL AS [user!2!dtmCreatedDate],
040 NULL AS [user!2!strMedicalSpecialtyID],
041 NULL AS [user!2!blnChangePassword],
042 NULL AS [user!2!strInstitutionalUser],
043 NULL AS [user!2!strCreditCardExpDate]
044 UNION
045 SELECT
046 2 AS Tag,
047 1 AS Parent,
048 NULL,
049 Users.strUSID,
050 strPrefixID,
051 strFirstName,
052 strLastName,
053 strUSName,
054 strAdd1,
055 strAdd2,
056 strTown,
057 strState,
058 strPostCode,
059 strCountry,
060 strTel,
061 strFax,
062 strEMail,
063 strLocked,
064 dtmLockedDate,
065 ID_LoginSuspenseReason.strSuspenseReason,
066 intLogonCount,
067 dtmCreatedDate,
068 strMedicalSpecialtyID,
069 blnChangePassword,
070 strInstitutionalUser,
071 strCreditCardExpDate
072 FROM ID_Login Users
073 LEFT OUTER JOIN ID_LoginSuspenseReason ON Users.strUSID = ID_LoginSuspenseReason.strUSID
074 {0}
075 FOR XML EXPLICIT
076 “;
077 }
078 }
079 }
</pre>
We are happy to announcement the new SMTP Diagnostics web site. Find the latest download, purchase your license, view the FAQ, and more.
http://www.smtpdiagnostics.com/
White Peak Software is donating 10 licenses of SMTP Diagnostics to Seth Dillingham’s PMC fund raiser. The licenses along with other software that will be auctioned off in September to raise money for the Jimmy Fund. All proceeds from the auction will go to the Pan-Mass Challenge, and in turn to the Jummy Fund, for the research and treatment of cancer at the Dana-Farber Cancer Institute in Boston.
The PMC is a 192 mile bike ride across Massachusetts. This year’s ride had approximately 4,000 riders.
I recently changed the way perm links are generated for my blog entries. However, I totally forgot about making the change to my RSS feed. I guess that’s what happens when you introduce a technology like RSS and it runs problem free for months if not years. Still, that’s no excuse for overlooking the RSS feed with regard to the new perm links.
I will have a fix in place within a couple of days. Sorry it can’t be sooner but my plate is full at the moment and since that has been no complaints it’s not a top priority issue.
My apologies to those of you reading the blog through an RSS reader.
You know what I dislike the most about doing client work? You see problems in the code, no bugs - things are working correctly, but the code itself is a problem. Finding code smells for example, and you know the best thing to do is refactor the code to save tons of time in the future. But some clients don’t get this and the ones that do get it will still say no to refactor work.
You tell the client you want to spend a couple of days to refactor the code but the client says “No”. To the client the code is working fine. Refactoring is going to cost more money and require more testing while providing little benefit to the end user. But the reality is ongoing maintenance cost for the code is going to cost more, which down the road could increase the cost to the end user, if refactoring does not occur. What is often overlooked is that maintenance costs can be lowered in the long term as a result of a refactoring investment.
Refactoring is something that should be embraced, not feared or viewed as an unnecessary cost. Refactoring improves the quality of the code and reduces code maintenance in the future and thus reduces cost.
A joy I have in writing my own software is being able to make the refactoring call myself. I don’t always say yes to refactoring but when the time is right I refactor the code. I don’t concern myself with the initial refactoring and testing costs because I know it will save me time and money in the long run.
So when is the time right? That’s a hard question to answer because the right time can be different for each shop and software package. For me a right time is when working on a new feature I find a number of code smells affecting the quality of the feature. Assuming a refactor will only affect a small portion of the entire system and it will take no more than a couple of days to refactor and test the changes, I make the yes call to refactor the code.
Do I worry about breaking existing functionality as a result of refactoring? Absolutely, especially when refactoring occurs in a critical area such as processing a credit card transaction. This is why it is so important to have automated scripts and unit tests in place. Automated regression testing definitely helps ease concerns.
So how do I convince the client it is time to refactor code? I wish there was a magic answer to the question that could be applied to all cases but there isn’t. Getting the client to okay a refactor takes having the client’s trust in your abilities and decisions.
One way to gain trust is to know when to recommend a refactor. You should make a point not to scream “fire” each time you find code smells. If each time you are asked to implement a new feature and you come back with “Before I can implement the feature I need to re-write all this other code,” your client’s trust in you will most definitely drop.
You also need to be able to show the client the long term value gained by refactoring. For instance, make the client aware of the various code smells and discuss the impact the repeated (or similar) code has to maintenance should a bug be found or a business requirement changes in the future. Also, remind the client that the need is fresh in your head but 6 months later it is going to take more time research the impact. Plus, the number of smells may have grown between the time the problem was first identified and the time code has to change, resulting in higher costs.
Lastly, be mindful of the business side. Many times developers only think of the technical aspects with no regard to the business side of things. Understand what is happening on the business side of a project will help when discussing a refactoring effort with a client. This can also help you understand why the answer is “No” and could help you identify a better “right time” to refactor.
Convincing a client it is time to refactor is no trivial task but it is possible.
I have been writing a number of stored procedures that return report data for a client. Each of these reports can be filtered by a date range, so I needed a simple way to check that a date was within a user defined date range.
As an additional requirement, if the user does not specify a begin date or end date as part of the date range then the earliest or latest supported date respectively should be used. In other words, if the user does not specify a begin date then the earliest date supported by SQL Server should be used as the begin date, and if the user does not specify the end date then the latest date supported is used for the end date.
My original approach was to use a BETWEEN clause in the SELECT statements. However, this made the code messy especially with the ISNULL call used to set the begin end dates when NULL. So I swapped out the code with a user-defined function.
The user-defined function fnIsInDateRange takes 3 parameters:<ul><li>Date to check</li><li>Beginning date of the date range</li><li>Ending date of the date range</li></ul>
The function returns one of the following:<ul><li>-1 if the date is before the range</li><li>0 if the date is within the range</li><li>1 if the date is beyond the range</li></ul>
Here’s the code for those who want it.<pre>
CREATE FUNCTION dbo.fnIsInDateRange
/*
Determines if the date time value is within the date time range.
Returns:
-1 if the date is before the date range.
0 if the date is within the date range.
1 if the date is after the date range.
Usage:
SELECT dbo.fnIsInDateRange(‘2005-08-16’,’2005-08-01’,’2005-08-15’)
SELECT * FROM exams WHERE dbo.fnIsInDateRange(dtmExamDate,’2005-08-01’,’2005-08-15’)=0
*/
(
@value DATETIME,
@rangeFrom DATETIME,
@rangeTo DATETIME
)
RETURNS SMALLINT
AS
BEGIN
DECLARE @result SMALLINT
IF @value < ISNULL(@rangeFrom,'1753-01-01 00:00:00.000')
BEGIN
SET @result = -1
END
ELSE IF @value > ISNULL( @rangeTo, ‘9999-12-31 11:59:59.997’ )
BEGIN
SET @result = 1
END
ELSE
BEGIN
SET @result = 0
END
RETURN @result
END
</pre>
I’ve always said I’m lazy programmer, which makes me a good programmer. For instance, I’m too lazy to perform the same task over and over on a computer so I write a tool. And I hate writing the same code over and over. I would rather write it once, package it up in a library, and forget the details. All I want to remember is the API.
I also believe my lack of a quality education helps me think of solutions that smarter individual may never even consider. My seemingly childish approach to problem solving helps me think of solutions to the most time consuming yet simple problems.
But Philipp Lenssen at Google Blogoscoped has really summed up why good programmers are lazy and dumb. Give it a read.
Amped 3, Burnout Revenge, and Dead or Alive 4.
While I believe Burnout Revenge will also be released on the Xbox in September, I plan to wait until Xbox 360. The latest disc from OXM has a playable demo of Burnout Revenge and a video showcase on Amped 3 and DOA4. All three games look awesome.
My ISP must perform some emergency maintenance on my SQL Server box. The forums site may be down for 15 minutes. Repair work is scheduled to start at 2:00 pm MST (GMT-7).
Update: All done.