Only a Programmer Can QA Your Programming Components

It seems self-evident, but if you’re developing software components for use by other programmers—APIs, SDKs, reusable controls, whatever—if your QA is being done by non-programmers, you’re just pretending to do QA.

I once worked for a company where the QA process for some of our software components went like this: a developer is told to create a new software component for use by other developers. However, since the QA team can’t test the component directly, the developer should also create a “tester application” that gives the QA team a nice, graphical way of exercising the module. Well, you can imagine what happened.

The “tester application” the developer created only exposed a minimal, even partial, set of functionality supported by the component; enough for the QA team to do a sanity check and make sure the thing isn’t completely broken, but by no means a robust tool for ensuring quality. The reason for this isn’t necessarily because the developer was lazy or didn’t care (though that certainly doesn’t help). Rather, there is a more fundamental issue.

When you ask a developer to create a UI for a non-programmer to test a component that is intended for use within a full-fledged programming environment, you’re essentially asking them to create a programming-language-to-user-interface mapper, a daunting task that will never be able to retain the full fidelity and power of the original programming environment. The dream of easy, graphical programming for the masses has been around for a long time, but it has yet to be attained and arguably never will be. There’s a reason professional development is still done using text-based grammars. Asking your developer to adorn the software component with a UI so the QA department can test it is like asking them to convert a diesel engine to use gasoline. It’s a lot of work that results in something very different than the original.

Graphical testing tools a great for a quick sanity check that your QA team can run. But if you want real, comprehensive QA of your software components, you need a programmer to build an automated test suite that will exercise the component as it will actually be used.


string.KeepOnly

Often when working with string data you need to strip away certain characters, filtering out some kernel that you’re interested in. For example, you may want to compare two phone numbers to see if they contain the same digits but you don’t care if the formatting is different. Or you may need to strip punctuation, or keep only letters, etc.

Normally when you encounter a situation like this, we pull out a for loop and get the job done. But if you solve any one of the above scenarios directly with an off-the-cuff for loop, you’ve just missed your chance to solve the more generic problem of keeping only certain types of characters in a string. And in C#, this is a solution we can easily package into an extension method:

public static string KeepOnly(this string text, Func<char, bool> filter)
{
    if (text == null)
        return null;

    var sb = new StringBuilder();

    foreach (var c in text)
    {
        if (filter(c))
            sb.Append(c);
    }

    return sb.ToString();
}

Armed with this simple method, we can now strip a string of anything but digits like this:

var digits = "(123) 456-7890".KeepOnly(c => char.IsDigit(c));

Which can be packaged into an even more easy-to-read extension:

public static string KeepOnlyDigits(this string text)
{
    return text.KeepOnly(c => char.IsDigit(c));
}

Which can be used like this:

var digits = "(123) 456-7890".KeepOnlyDigits();

Which, I guarantee you, is a thousand times more readable and reusable than an inline for loop.

In my .NET utility library I’ve defined a bunch of methods like this for common string filtering. Here’s the entire class:

using System;
using System.Text;
using System.Linq;

namespace Cooper.Extensions
{
    public static class StringKeepOnlyExtensions
    {
        public static string KeepOnly(this string text, Func<char, bool> filter)
        {
            if (text.IsNull())
                return null;

            var sb = new StringBuilder();

            foreach (var c in text)
            {
                if (filter(c))
                    sb.Append(c);
            }

            return sb.ToString();
        }

        public static string KeepOnly(this string text, char characterToKeep)
        {
            return text.KeepOnly(c => c == characterToKeep);
        }

        public static string KeepOnly(this string text, char[] charactersToKeep)
        {
            return text.KeepOnly(c => charactersToKeep.Contains(c));
        }

        public static string KeepOnlyDigits(this string text)
        {
            return text.KeepOnly(c => c.IsDigit());
        }

        public static string KeepOnlyDigitsAnd(this string text, char additionalCharacterToKeep)
        {
            return text.KeepOnly(c => (char.IsDigit(c) || c == additionalCharacterToKeep));
        }

        public static string KeepOnlyDigitsAnd(this string text, char[] additionalCharactersToKeep)
        {
            return text.KeepOnly(c => (char.IsDigit(c) || additionalCharactersToKeep.Contains(c)));
        }

        public static string KeepOnlyLetters(this string text)
        {
            return text.KeepOnly(c => char.IsLetter(c));
        }

        public static string KeepOnlyLettersAnd(this string text, char additionalCharacterToKeep)
        {
            return text.KeepOnly(c => (char.IsLetter(c) || c == additionalCharacterToKeep));
        }

        public static string KeepOnlyLettersAnd(this string text, char[] additionalCharactersToKeep)
        {
            return text.KeepOnly(c => (char.IsLetter(c) || additionalCharactersToKeep.Contains(c)));
        }

        public static string KeepOnlyLettersAndDigits(this string text)
        {
            return text.KeepOnly(c => char.IsLetterOrDigit(c));
        }

        public static string KeepOnlyLettersAndDigitsAnd(this string text, char additionalCharacterToKeep)
        {
            return text.KeepOnly(c => (char.IsLetterOrDigit(c) || c == additionalCharacterToKeep));
        }

        public static string KeepOnlyLettersAndDigitsAnd(this string text, char[] additionalCharactersToKeep)
        {
            return text.KeepOnly(c => (char.IsLetterOrDigit(c) || additionalCharactersToKeep.Contains(c)));
        }

        public static string KeepOnlySpaces(this string text)
        {
            return text.KeepOnly(c => (c == ' '));
        }

        public static string KeepOnlyWhitespace(this string text)
        {
            return text.KeepOnly(c => (c.IsWhitespaceCharacter()));
        }
    }
}


Your Mindset is More Important

It’s amazing how prone we can be to superficial measures of maturity. Sometimes we get it in our head—though we wouldn’t ever say it out loud—that using things like TDD, BDD, IoC, ORM, etc. will automatically lead us to software quality nirvana. It doesn’t.

Kyle Baley has a great article reminding me that, ultimately, what makes a software team great is not the tools or practices that they use, but their mindset. It is only when developers care deeply about keeping the codebase clean and maintainable and producing quality results that these tools and practices show their merit. In order to build truly great software we need to do more than use great tools and say we follow great practices; we need to cultivate and encourage a culture of quality.


Learning F# via Project Euler

I’m convinced F# is a far more advanced and productive language than the C# I’ve been using for the past 7 years. And having tasted the beauty and power of functional programming with LINQ, lambdas, and extension methods in C# 3.5, I’m anxious to move to a paradigm that’s functional by default.

And yet every time I try to start a personal project in F#, I seem to freeze with questions. Is this really the way I should be building this? Should I be using a class or a record? What was the name of that module again? You know, basic stuff.

So I’ve decided that a good way to learn F# would be to go through (at least some of) the Project Euler problems. Already I’ve worked through the first two and have found the experience to be very worthwhile in general and a nice way to get comfortable with F#. Time permitting I may blog about some of my experiences.


Are ORMs Solving Anything? Yes.

This was meant to appear as a comment on Shawn Wildermuth's post "Are ORMs Solving Anything?". But it turned out to be too long to be accepted as a comment, so I've posted it here.

This is certainly thought provoking, though I'm surprised that the small consensus here is that ORMs are better suited for small projects. Because, in my experience, it is in the enterprise space that a good ORM (like NHibernate) can really shine.
I think we often assume that using an ORM means generating code or schema. With some ORMs, this is definitely the case. Why, LINQ-to-SQL generates reams of C# code to give you a type safe version of your data schema. And, as you pointed out, you can use tools like NHibernate to go the other direction as well, generating your data schema from your class mappings. If code/schema generation was what made ORMs great, then yeah, I think they'd only be good for newer, smaller projects. But there's a lot more to ORM than code generation that's worth keeping in mind.
In my mind, using a robust ORM (like NHibernate) really makes the most sense in an enterprise context. Here are some of the advantages of using NHibernate on a project of substantial size:
  • Database agnosticism. If being able to provide a choice in database technology is an advantage to your business, NHibernate can certainly deliver. 
  • Testing. As a consequence of being database agnostic, you can replace your heavy SQL Server with a nimble SqlLite instance at test time, allowing you to write blazing fast tests. 
  • Caching. NHibernate offers a tremendous amount of options and control when it comes to caching. First-level caching is built in, and there are a variety of second-level caching providers you can chose from. Need to add distributed memory caching to your app? Plug into the NHibernate memcached second-level cache provider.
  • Higher level of query abstraction. Most ORMs allow programmers to query in ways that are more consise (and type safe) than raw SQL. Of course, this can be a performance liability if you don't know what you're doing (or if you neglect to use a good profiler), but overall the productivity gain seems like a win to me. 
  • Dealing with legacy data schemas. A framework like NHibernate offers significant advantages when working with a large, legacy codebase and database that need to be maintained. With it, I can start modeling my own view of the world in code (in a DDD style) and then use NHibernate to map my objects down to the way they are actually represented in the database. Having done that, we can then start querying our domain model directly, allowing the development team to think in terms of the way we think of the business rather than having to constantly translate our intent to a data schema that hasn't grown with the business. And, once we've abstracted away direct access to the database, we can more safely modify the database schema knowing that it is only our mappings which will need updating.
I'm sure someone with more ORM experience than me could add a lot more to the list. But notice that none of the points above are particularly suited for small projects. In fact, quite the opposite. All of these gains are relevant when developing larger projects for the enterprise.
So I suppose what I'm suggesting is that code and schema generation is only a small part of what ORMs offer. Of course there are enterprise situations where performance is so important that an ORM tool is simply not appropriate, but few of us are Amazon or Google, and I would argue that the advantages listed above make long-term maintenance, changes, and refactoring significantly easier rather than more difficult.
I hope this reply doesn't come off as defensive, because, like you, I'm not trying to chose a pro- or anti-ORM side. But I did want to point out some ways in which ORMs can be very helpful in managing large projects.
I do wonder what the future holds for ORMs though, particularly with the rise of functional programming. A language like F# can deal with relational data in a much more natural way than most of the OO languages we've been using for so long, and I wonder how that might change the ORM landscape.
Your chart on the right side of the page illustrates something that astounds me--look at how much stuff we have to do above and below the business objects, which are the real heart of the software! It's amazing how much effort and energy we expend on all of the infrastructural pieces that we need to put in place in order to get real work done. Then again, perhaps the better way to look at it is that we're trying to extract that sweet core of business knowledge out of and away from all of the infrastructure concerns (both front end and back) so that we can swap out the pieces that change without destroying the code that models our business. And that is, I think, the real reason I find value in an ORM.
Hope that helps,
Adam

My Lord, My Truth, My Way

Shortly before he was arrested and crucified, Jesus told his disciples “you know the way to where I am going” (John 14:4). Where he was going was heaven, to be with the Father. The way there was through the cross he was about to suffer.

But Thomas, bewildered and confused, responded “Lord, we do not [even] know where you are going. How can we know the way?” (John 14:5) To which Jesus said: “I am the way, and the truth, and the life. No one comes to the Father except through me.”

This answer is startling—Jesus has already told his disciples there is a “way” which he must go to the Father (an obvious reference to the coming cross), but now he says that this “way” is himself.
It seems to me this is Jesus’ way of saying that there’s more to his cross than just the wood and the nails and the fact that he died. There is something about Jesus himself in the way he went to the cross that we need to pay close attention to.

Jesus’ death was not a tragedy that could have been avoided, something that just “happened” to him. No, it was a deliberate choice, a choice to sacrifice himself for the good of others, a deliberate decision to lay aside his greatness and stoop low, as low as anyone has, to rescue a sinful humanity. It was this particular aspect of his death, this willingness to humble himself out of love for other people, that Jesus seems to refer to when he says “I am the way.” It is as if Jesus was saying “Look at me Thomas. Watch what I am about to do. Learn from my humble submission to the Father and my sacrificial love for you, and then make my example the pattern for all of your life.” Indeed, he had just told them “just as I have loved you, you also are to love one another.” (John 13:34)

But after an unjust arrest, a brutal execution, and three days of loneliness as Jesus’ body lay lifeless in a tomb, Thomas seems to have lost all hope. How could this be the “way” to the Father? When the disciples come to him claiming that Jesus was alive, he would not hear it. “Unless I see in his hands the mark of the nails, and place my finger into the mark of the nails, and place my hand into his side, I will never believe.” (John 20:25)

Eight days later, Thomas found himself face-to-face with the man he thought he would never see again. “Put your finger here,” Jesus said, “and see my hands; and put out your hand, and place it in my side. Do not disbelieve, but believe.” (John 20:27 ) In that moment Thomas suddenly realized that Jesus’ dying example was not a failure, but a pathway to victory, to which he exclaimed “My Lord and my God!” (John 20:28)

One of my favorite hymns, My Lord, My Truth, My Way, begins with a line that is really a combination of Jesus’ words to Thomas before his death (“I am the way, the truth, and the life”) and Thomas’ words to Jesus’ after his resurrection (“My Lord and my God!”). It is the song of a Thomas-like disciple who has seen the risen Lord and longs to follow in his steps. Here are three verses:

My Lord, my Truth, my Way,
My sure, unerring light,
On Thee my feeble steps I stay,
Which Thou wilt guide aright.

My Wisdom and my Guide,
My Counsellor Thou art;
O never let me leave Thy side,
Or from Thy paths depart!

Teach me the happy art
In all things to depend
On Thee; O never, Lord, depart,
But love me to the end!

You can find the entire hymn at CyberHymnal.