Sunday, June 19, 2005

Agile Web Development with Rails

The Pragmatic Programmers are releasing a book soon: Agile Web Development with Rails

"Rails is a full-stack, open-source web framework that enables you to create full-featured, sophisticated web-based applications, but with a twist... A full Rails application probably has less total code than the XML you'd need to configure the same application in other frameworks."

The book is currently available as a Beta Book. After reading only 20% of the book I feel can recommend it. So far it's an easy read and Rails is easy to work with. That means that you can read the book and speak intellegently about Rails in no time, and the more languages you know the better you become at all of them.

Saturday, June 11, 2005

Controlling Subversion with Ruby and the command line

I love the syntax and ease of developing in Ruby. Unfortunately, there isn't much Ruby work. Therefore, I use Ruby as often as I can for simple tasks I'd like to automate.

Subversion's command line client does everything you need, but not always as easily as possible. For example, I'd like a command that adds all the unknown files returned from a `svn status`. Yes, you can `svn add .`, but that seems to try to add everything and produces warnings.

I think you see where I'm going with this.

Where should we start? With tests obviously.

require 'svndir'
require 'test/unit'

class SvnTestCase < Test::Unit::TestCase
def testMassAdd()
tempFile = createTempFile()
assert_equal("?",tempFileStatus(tempFile))

svnDir = SvnDir.new()
svnDir.add();

assert_equal("A",tempFileStatus(tempFile))
removeTempFile(tempFile)
end

def createTempFile(fileName = "tempFile.temp.temp")
flunk("#{fileName} file already exists") if File.exist?(fileName)
`echo fileName > #{fileName}`
assert(File.exist?(fileName))
fileName
end

def removeTempFile(fileName)
`svn revert #{fileName}` if tempFileStatus(fileName) == "A"
File.delete(fileName) if File.exist?(fileName)
assert(!File.exist?(fileName))
end

def tempFileStatus(tempFile)
`svn status`.to_s =~ /(\?|A)\s+#{tempFile}/
$1
end
end

Now that I know what I'm looking for from a SvnDir class I can implement it:

class SvnDir
def add()
svnStatus().each { |line| addFile(line) if unknown?(line) }
end

def svnStatus()
`svn status`.to_s.split(/\n/)
end

def unknown?(statusLine)
statusLine =~ /^\?/
end

def addFile(statusLine)
statusLine =~ /^\?\s+(.*)$/
`svn add #{$1}`
end
end

I'm still learning Ruby, so please comment with better ways to do this.

I asked Subversion guru Mike Mason if there was an easier way, and he suggested TortoiseSVN. It's true, adding is easier in Tortoise, but I'm just too much of a control freak to give up my command line.

Saturday, June 04, 2005

What are you testing?

When I first started testing I thought code coverage was the most important statistic. In the past I had written zero tests for my code. Once I learned there was a better way, I went to the opposite extreme. Because of this focus, I attempted to test every line of code.

Imagine a DTO:

public class Foo
{
public int Bar;
}

Would have a test:

Foo f = new Foo();
f.Bar = 1;
Assert.AreEqual(1,f.Bar);

The problem with this type of testing is that it attempts to test the features of the language. Not only is this unnecessary, but it adds code to the code base, thus increasing the complexity of the project.

Along the same lines:

public class Foo
{
public Foo()
{
Bar = 1;
}
public int Bar;
}

Can have the associated test:

Foo f = new Foo();
Assert.AreEqual(1,f.Bar)

Once again, there's no interaction and the test simply tests the features of the language. I picked this example because I saw something similar in a production system. On a side note, I believe the assignment should be done on the declaration line and the constructor should be removed. Then, it is more obvious how unnecessary the test becomes.

Lastly, I have another example from a production system. Class Foo needed to subscribe to an event on Class Bar. If Foo did not subscribe an error would not occur, but invalid results would. Thus, a colleague of mine asked how to test this situation. It turns out that you can mock the add_* method, and therefore test that the subscription occurs. However, I believe this is simply testing that a subscription occurs, not that when the event is raised Foo will handle it correctly. Therefore, a functional test was needed for the business requirement and testing the subscription was unnecessary.

These days, I find myself constantly asking the question "What are you testing". The value in testing is often at interaction points. State based testing adds value, but is usually only necessary after an interaction occurs. As a result much of my tests include mocks.

Code coverage is important and tests can ease maintenece issues; however, having unnecessary tests decreases readibility. Therefore, always know what you are testing.

Tuesday, May 24, 2005

UI Driven Service Layer Development

Pragmatic software development is about knowing which tools solve which problems best. For example, test driven development is wonderful for designing a domain layer. Unfortunately, mapping the UI interaction with the domain layer is often painful and done poorly. Using UI Driven Service Layer Development you can more easily decouple the UI layer from the domain layer.

Your team has tested and developed a domain layer. The classes are well designed and contain correct responsibility. You also have mock screens given to you by a UI designer. Your next task is to make the screens work with the existing domain layer. You should begin by creating the UI & service layer, but stub the service layer. Make the entire application "work" with hard coded data returning from the stubs. Patterns and logical groupings will begin to emerge in the stubbed service layer. This approach lends itself to easily refactoring the service layer to an interface that logically fits the UI. Finally, you convert the stubs into real classes that interact with the domain layer.

The finished product is an service layer that the UI can consume without being tied to the domain layer design decisions.

Friday, May 13, 2005

Coding standard election

Everyone sees the value in coding standards and agrees that we need coding standards. Unfortunately, the trouble starts when you begin compiling the coding standard list.

I'm very passionate about the perfection of the code I produce. That's not to say that my code is perfect, but I attempt to make it as perfect as I possibly can. Many of my co-workers share this passion. This is part of the reason we are so passionate about coding standards. Unfortunately, we don't always agree on what "perfect" code looks like.

On every project there are wars over "curly braces on their own line", "space before and after an (insert operator, e.g. = or +)", "prefix interfaces with I in C#". In the .NET world Visual Studio helps sort out the problems by auto formatting, but it doesn't address all situations.

Recently, I was pairing and Visual Studio 2005 Beta 2 kept formatting string.Format to String.Format. Stephen Chu noticed and remembered that another version (on another day, when the moon was full or some other reason that VS2005 decided to behave differently) had corrected String.Format to string.Format. He asked if we should have a standard. In general, I believe that standards should be created when necessary, not when possible. So, I responded that "I honestly don't care how VS formats the word 'string', I'm going to read it the same way". I believe that coding standards should be created to increase readability, not beauty.

Some coding standards are required. When starting a new C# project you should always vote no underscore prefix on private fields, camel case for private fields, and prefix all interfaces with "I". Those are obviously my opinions, but they are for clear reasons. Camel case for private fields is descriptive enough that a field is private; therefore, the underscore is unnecessary and redundant. While I actually hate the "I" prefix, Microsoft not only pushes it as a standard, but also follows this standard in the framework. Breaking the standard can cause confusion among developers joining the team. Also, refactoring technologies allow you to easily rename an interface if you change it to a class.

Standards should be significant majority elected. New standards are nominated by an employee and seconded by another before it's put to vote. If a vote takes place, 66% of the team must vote to add it to the coding standard list. Upon addition, a repeal vote will begin and last 7 days. If more than 33% of the team votes to repeal the standard, it will be removed. Once a standard is added and is not removed in the repeal vote it can only be challenged once every 30 days. 34% or greater votes to remove are always successful.

This is a bit of a process, but could be automated and will make coding standard issues easier to mediate.