The effectiveness of TDD, a whitepaper – level 200
Some researchers from North Carolina State University have done a small study on Test-Driven Development and published their results: http://collaboration.csc.ncsu.edu/laurie/Papers/TDDpaperv8.pdf
Jeffrey Palermo, Microsoft MVP, Author, Speaker, Clear Measure Chief Architect, Azure DevOps Expert
Some researchers from North Carolina State University have done a small study on Test-Driven Development and published their results: http://collaboration.csc.ncsu.edu/laurie/Papers/TDDpaperv8.pdf
I am hosting a pre-Tech Ed party on Saturday, June 4th. That is the day before the pre-con. The Peabody hotel is just across the street from the conference center, and that is where we will meet. Then, later on that evening, we’ll have a large geek dinner at the restaurant in the Peabody lobby (I’m assuming everyone won’t have cars to enable driving a few miles).
Party with Palermo is the official title (from Darrell Norton).
Here is the itinerary:
4PM – Arrival begins. meet and greet. Discuss conference sessions, etc. Networking.
6PM – Geek Dinner. I have reserved for 20, so I will need actual names for people to expect.
7:30PM – Drinks and conversation at the hotel bar.
Depart whenever.
The following is important. Use the contact form on my blog to let me know you are coming. I initially reserved 20, but when I get more (there may be 100 or more), I’ll need to adjust the reservation. It’s very important that everyone planning on attending sends me an email through my blog so that I have you on the list for dinner. I’ll also be able to send out targeted last-minute updates.
Please spread the word. I’d appreciate linkbacks if you have a blog as well as email referrals to colleagues who will be at Tech Ed.
From this point on, I’ll prefer to send updates directly via email, so be sure you send me your contact information so I can keep you in the loop.
Check out http://beta.asp.net/, for ASP.NET 2.0. It has announcements and quickstarts and other resources to help one get started with ASP.NET 2.0.
When I installed Server 2003 on my development laptop, I had to jerry-rig some XP drivers in order to use all my devices, but I’ve been using this OS for all my development for almost a year now, and it has worked out very well. It has been just as stable at XP and very nimble, too. I use a Dell Lattitude D600 with P-M1.8Ghz and 1GB RAM. The only complaint I have is MS’s lack of user support for Server 2003. For instance, I can’t use Windows Movie Maker on this OS, but those are little issues.
As a development workstation, I’ve been very satisfied. With XP, you are limited to one top-level website. With server 2003, it can be many. This is great if you do ASP.NET. Also, it supports multiple remote desktop sessions. I’ve had a very good experience with it.
The first thing that happens when someone posts performance numbers is to question the method of benchmarking. My test was an ad hoc benchmark on my own workstation to get a feel for the performance difference, if any, between bringing aggregate data from a database using a DataSet and using a collection of domain objects. Some have said that binding to collections of domain objects is a “heavy-handed” approach when all that is required is a read-only display of the data. My opinion was that the DataSet, with its deep and complex object model, is slower(and more memory intensive) than using custom domain objects for this task.
I created a read-only view of the customers table in the Northwind database. I created a Customer object and CustomFactory for presenting an aggregate of Customer objects (one for each row). I also created an object to return a DataSet with this same information. I created two ASP.NET pages identical with a single DataGrid (with Viewstate off). One bound the array of Customer objects, and one bound the DataSet. Then I used Application Center Test to profile each page. I made 2 runs each for 1 minute each.
The page that used an array of custom domain objects to present a read-only display was 3.7% FASTER than the same functionality using a DataSet. I’m not interested at all in the performance difference, but all I care to prove is that using custom domain objects is NOT a heavy-handed approach to displaying read-only aggregates of data. Besides that, it makes for a better application design with cohesive logic.
I prefer to use custom objects for just about everything. Yes, a DataSet does it all, but at the expense of everything else. If I need a few rows from a table, let’s take a look at all the objects I have to create just to get that:
It seems to me that using a DataSet is a heavy-handed approach to moving data, but given that the performance difference is so small, my main reason for choosing custom domain objects is design.
My main motivation for posting about this was to disprove the misconception that using custom domain objects for read-only aggregate views is an overkill. In fact, there is no proof of this performance difference by using the OO design.
The .Net user groups in Texas are working together to put on a .Net Olympics. If you hang your hat in Texas, then we want you! It’s very simple. Submit an application you have developed that hasn’t been released yet. There will be prizes for winners in every category.
Check out http://dotnetolympics.net/ for all the details. Create a log-in to become a contenstant.
If the title of this post gets you worked up, you probably shouldn’t read on. I’m about to bash your beloved DataSet.
Scott Mitchell has written more on why to avoid DataSets. While he touches on performance and the mechanics of what it does, I believe the issue is bigger.
Everyone can argue and compare performance numbers in different scenarios to justify one versus the other. The real argument, I think, is design. No matter what you do, your application must perform well enough to meet the customer’s expectations. The customer doesn’t care about the internal workings. So what other motivations do we have?
What about OO design. What about an object owning state and all behavior that goes with that state? A DataSet cannot substitute for a domain object. It has no custom behavior. It holds records. As long as DataSets are pulled and bound to the UI, the application will be of Procedural nature. I’m coming from a stand-point where I care about OO and the maintainability and testability gains associated with it, so DataSets don’t hold much weight with me.
Now we are talking about another issue altogether. Are you dragging and dropping a RAD application that will have a short lifespan (because RAD and Maintainability are antonyms)? If so, you can stop reading now because this discussion has no bearing on your application.
If your intention is to develop a well-factored object-oriented system with loose coupling between layers and high class cohesion, then please read on. You will want to identify entities in your system and develop your domain object first, NOT your database schema. If you start with the database schema first, you are starting with a handicap that is often hard to overcome. Start with your domain objects first and define the entities that your system will manage. Each of these entities will own some state and will encapsulate this state with custom behavior through their methods. One example is that if you have a Product object and you try to set the Price field to a negative number, the object shouldn’t allow it. The object is responsible for protecting its state. If you have a dumb data container (DataSet) floating around, then you have to jerry-rig some validating logic before you “Update” the changes back to the database.
If you have a well-factored OO design, you will always be able to extend the system with new functionality as well as change existing functionality with minimal impact. This can exist because each small responsibility is housed in its own object and not shared among the logical layers.
I really don’t care about convincing DataSet users to enroll in Rehab (unless they are on my team – which they aren’t). Good design isn’t easy, but the benefits are worth the effort. DataSets are easy. Speaking of easy. . . here are some other things that are easy:
For the record, I have used a DataSet (and a strongly-typed one), but not any more.
ADDITION:
I must say that the use of a DataSet object in an application doesn’t condemn it’s design to the lake of fire, but relying on that as your _business object_ does. If there is a situation where the overhead is acceptable, use it _inside_ your business object or somewhere else, but protect your entities. Don’t pass around naked entities in a DataSet exposed to the world. Use encapsulation. Protect your data.
Perhaps my main beef is with the use of DataSets as promoted by the VS.Net drag and drop tools.
I’m looking for experienced .Net/C# developers to work on Dell’s call-center application for the sales force. The more business experience the better. These positions are for very senior developers with a lot of high-level business knowledge. I can connect you directly with the hiring manager if you email me at jeffrey_palermo (AT) dell (DOT) com with a resume. Or use my contact page here on my blog.
This is full time salary + benefits in Round Rock, TX. Believe it or not, there is a shortage around here of good senior developers. If not, we’re having a hard time finding them. If you’re good, then we want you now (in the next month). These positions are not entry level or intermediate. Looking for level 300-400 developers only.
If you’d like the HR job description (which I never like), go to Dell.com and search for job “050004MM”. This is a sample Req. The application is the Windows Smart Client desktop app that the sales people use to manager customers, issue quotes and take orders for call in orders and our large customers. Basically all orders that don’t come in over the web. It’s currently rolled out to about 8000 salesmen. The software uses the IDD (Integrated Dell Desktop) application framework that MS did a case study about: http://www.microsoft.com/resources/casestudies/casestudy.asp?CaseStudyID=16276.
We all know to use the Strategy pattern to let the domain assembly know where to get and persist data. Our domain assembly has no references to any other custom assembly, but all assemblies reference the domain. We use the Dependency-Inversion Principle to accomplish this for data, but what about other services?
For instance, what if different domain object would benefit from knowledge of some state that is not AppDomain-specific, and not user-specific, but request-specific? The ASP.NET runtime offers a great statebag for request-specific information. HttpContext.Items. This collection of key/value pairs can be used in an ASP.NET context. Of course, we don’t want our model to depend on the ASP.NET runtime. In fact, our model doesn’t care. It will not reference any of our assemblies and especially not System.Web. How then can we make a domain object interact with this Items collection?
Simple. Use a Strategy that allows injecting an adapter class into the domain object. The adapter class can live at the ASP.NET runtime layer and server as a proxy for interaction. First, in the domain layer, we must define the appropriate interface required:
Any class implementing this can serve as our state or cache provider. For this example, I have two domain object that will consume this. There are many ways to inject a dependency, so use your imagination. I’m presenting the simplest way.
Customer will save a piece of state, and later, CustomerTracker will benefit from it being there. In our domain layer, we also have a singleton to hold an instance of IUserContextCacheable. This singleton will be set by the ASP.NET layer at startup.
Now, in our ASP.NET layer, we have this class:
It implements the required interface, and our controller will use an instance of this when constructing the domain objects. An instance of this is set to the singleton in the domain layer using our Global.asax (you could use a number of other methods as well).
Here’s a snippet of code that, for demonstration purposes can be placed in any page or user control (or any control for that matter):
When you run this page, you’ll see Trace enabled and the following outputted to the screen:
Trace Information | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| Category | Message | From First(s) | From Last(s) | ||||||
| To prove that no previous state exists, I’ll try to get ‘MyState’ | |||||||||
| MyState = | 0.000013 | 0.000013 | |||||||
| Invoking business object, Customer, to do something. | 0.000032 | 0.000019 | |||||||
| Invoking CustomerTracker, another business object that benefits from this knowledge. | 0.000734 | 0.000701 | |||||||
| Bar used the following state: “I went to the store at 07:17:18.” | 0.001141 | 0.000407 | |||||||
| aspx.page | Begin PreRender | 0.001162 | 0.000021 | ||||||
| aspx.page | End PreRender | 0.001181 | 0.000019 | ||||||
| aspx.page | Begin SaveViewState | 0.001252 | 0.000072 | ||||||
| aspx.page | End SaveViewState | 0.001374 | 0.000122 | ||||||
| aspx.page | Begin Render | 0.001390 | 0.000016 | ||||||
| aspx.page | End Render | 0.073195 | 0.071805 | ||||||
This proves that our ASP.NET runtime adapter class works as intended and allows our domain layer to store things in the ASP.NET runtime. This prevents us from having to implement our own caching mechanism that has the correct scope.
Using the Strategy pattern, we can hook up pretty much any outside service with an adapter class (adapter is my word).
I’ve zipped up this sample solution, and it is available on my downloads page.
If you are going to Tech Ed, check out http://www.msteched.com/content/sessions.aspx. All the sessions are scheduled now including BoF sessions. Click on the “Speaker” drop down and look for “Jeffrey Palermo”. It’s not every day that Microsoft.com publishes my name right next to Jeffrey Richter’s. What an honor! Actually it’s just a coincidence related to the alphabet.
Anyway, the closer the date gets, the more pumped I am. I look forward to some great learning and facilitating by Agile Methodologies with .Net BoF session.