Friday, May 16, 2008 #

Zero Friction & Maintainability

You probably heard me talk about zero friction and maintainability often enough in the past. But they were always separate subjects. When I prepared for my Zero Friction talk, I finally figured out what is the relation between the two.

I talk about zero friction as a way to reduce pain points in development. And I talk about maintainability as a way to ensure that we build sustainable solutions.

Let us go back a step and try to realize why we even have the issue of maintainability. Bits do not rot, why am I so worried about proactively ensuring that we will keep the code in a good shape?

As it turn out, while code may not rot, the design of the application does. But why?

If you have an environment that has friction in it, there is an incentive for the developers to subvert the design in order to produce a quick fix or hack a solution to solve a problem. Creating a zero friction environment will produce a system where there is no incentive to corrupt the design, the easiest thing to do is the right thing to do.

By reducing the friction in the environment, you increase the system maintainability

posted @ Friday, May 16, 2008 7:25 PM | Feedback (5)

Rhino Mocks 3.5 Beta Released

3 Months ago I released Rhino Mocks 3.4, we have made some great improvement to Rhino Mocks in the meantime, and it is time for a new release. I generally don't believe in beta releases for Rhino Mocks, but this time we are coming up with a new syntax, and I want to get additional external input before we make a final release on that.

The biggest new feature is the new AAA syntax, which you can read about in the relevant post, but we had a few other new things as well.

  • CreateMock() is deprecated and marked with the [Obsolete] attribute. Use StrictMock() instead.
  • Better handling of exception in raising events from mock objects
  • Fixing an issue with mock objects that expose methods with output parameter of type System.IntPtr.
  • Allowing to return to record mode without losing expectations, thanks to Jordan Terrel, for submitting this patch.

I intend to write a lot more documentation about the new AAA syntax, but for now, you can visit the tests for the feature, to see how it works.

As usual, you can find the bits here.

Note that this release if for .Net 3.5 only. Rhino Mocks 3.5 RTM will be for .Net 2.0 and 3.5, but I am focusing on the capabilities that I can get from the 3.5 platform at the moment.

posted @ Friday, May 16, 2008 3:50 PM | Feedback (0)

Rhino Mocks - Arrange, Act, Assert Syntax

I intend to release the new version of Rhino Mocks soon, and I wanted to show the new syntax that I have been working on. I still need to write more thorough documentation, but I think that just the syntax should be a pretty good indication of how things are going.

Without further ado, here is the code:

[Test]
public void WhenUserForgetPasswordWillSendNotification_WithConstraints()
{
    var userRepository = MockRepository.GenerateStub<IUserRepository>();
    var notificationSender = MockRepository.GenerateStub<INotificationSender>();

    userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" });

    new LoginController(userRepository, notificationSender).ForgotMyPassword(5);

    notificationSender.AssertWasCalled(x => x.Send(null),
        options => options.Constraints(Text.StartsWith("Changed")));
}

[Test]
public void WhenUserForgetPasswordWillSendNotification_WithArgMatchingInTheLambda()
{
    var userRepository = MockRepository.GenerateStub<IUserRepository>();
    var notificationSender = MockRepository.GenerateStub<INotificationSender>();

    userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" });

    new LoginController(userRepository, notificationSender).ForgotMyPassword(5);

    notificationSender.AssertWasCalled(x => x.Send(Arg<string>.Matches(s => s.StartsWith("Changed"))));
}

[Test]
public void WhenUserForgetPasswordWillSendNotification_WithArgumentMatching()
{
    var userRepository = MockRepository.GenerateStub<IUserRepository>();
    var notificationSender = MockRepository.GenerateStub<INotificationSender>();

    userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" });

    new LoginController(userRepository, notificationSender).ForgotMyPassword(5);

    notificationSender.AssertWasCalled(x => x.Send("Changed password for ayende"));
}


[Test]
public void WhenUserForgetPasswordWillSendNotification_UsingExpect()
{
    var userRepository = MockRepository.GenerateStub<IUserRepository>();
    var notificationSender = MockRepository.GenerateMock<INotificationSender>();

    userRepository.Stub(x => x.GetUserById(5)).Return(new User { Id = 5, Name = "ayende" });
    notificationSender.Expect(x => x.Send(null)).Constraints(Text.StartsWith("Changed"));

    new LoginController(userRepository, notificationSender).ForgotMyPassword(5);

    notificationSender.VerifyAllExpectations();
}

The class under test is:

public class LoginController
{
    private readonly IUserRepository repository;
    private readonly INotificationSender sender;

    public LoginController(IUserRepository repository, INotificationSender sender)
    {
        this.repository = repository;
        this.sender = sender;
    }

    public void ForgotMyPassword(int userId)
    {
        User user = repository.GetUserById(userId);
        sender.Send("Changed password for " + user.Name);
    }
}

posted @ Friday, May 16, 2008 3:34 PM | Feedback (1)

DevTeach Summary

Well, DevTeach Toronto is over, and so it my blogging hiatus. I haven't had time to blog because there was so much to do and take part of.

Now that it is officially over, I can look back and say that DevTeach is still my favorite conference. Leaving aside the great speaker and talk line up (thanks James, and thanks Scott for doing it on the last two DevTeach confs), what I really like about DevTeach is the interaction with the attendees and the amount of face to face time that you get with everyone. I haven't been able to crack what it is that makes DevTeach special in this way, but I have been to other big conferences, and they were good, but they weren't the same.

In short, in you have can make a conference, you really want to make it DevTeach.

I am feeling somewhat wrung out at the moment, the last week was intense. I had five talks and a panel discussion (which will be available on DotNetRocks! ) , and at some point it felt like playing musical chairs. I like presenting, make no mistake, but there is no denying that giving a talk is a high energy expenditure event.

Overall, I am happy with the way the talks went. I talked about:

  • Rapid (maintainable) web development with MonoRail
  • Advance usages of Inversion of Control containers
  • Writing Domain Specific Languages in Boo
  • Object Relational Mapping += 2: More then just data <-> object
  • Building Zero Friction Development Environment

The last talk was a surprise one, I had to fill in for Roy, who was sick (but is getting better). For an off the cuff session, I think it went very well. It was somewhat like posting to my blog, live and in speech, instead of writing. At least, that was how it felt, far more informal and more abstract than most of my talks. It has also given me the chance to clarify some of my thinking in the area of zero friction development and why this should be a goal.

The Advance IoC and Advance OR/M talks were pure fun. Fundamentals are important, but there isn't enough discussion about what happens after you grok the fundamentals as I would like to see.

Of course, I know that the pace of those talks is fairly... daunting. I am trying to cover in one hours concepts that took me months and years to figure out. I am not trying to impart the actual knowledge in those talks, there is just not enough time for it, but I am trying to point the way to interesting approaches, the advantages that using those approaches gives you, and where you should explore further.

The panel discussion, which was recorded as a Dot Net Rocks podcast, talked about The Future of .Net. Me, Ted Neward and Scott Bellware sat and talked about this for a while. I think that it was a good discussion, but I really feel the need to find something else to point as the negative examples. We have been beating the same horse for too long. I accept nominations, by the way.

Too many hallway discussions and side talks to count or articulate, I am afraid.

Greg's talk about DDDD was interesting, we talked about it afterward, and I think that we are much closer in our thinking that it would appear on the outside. A lot of the things that Greg objects to are things that I would hurry to avoid as well. We take different approaches to avoiding them, most probably because we tend to build very different applications.

In summary, DevTeach rocked!

I had a lot of fun, and baring intervention from a higher power, I definitely intend to be in the next one.

posted @ Friday, May 16, 2008 2:35 PM | Feedback (0)

DSL Course in Austin Next Week: Canceled

Due to low amount of attendees, the DSL course that was suppose to run next week in Austin had ben canceled.

posted @ Friday, May 16, 2008 12:16 PM | Feedback (3)

Tuesday, May 13, 2008 #

DevTeach: Home Grown Production System Monitoring and Reports

That was an interesting talk, Owen Rogers talked about how to setup an Operation Database in order to get more visibility on your production system. This is the first time that I sat in a talk that is a "Release It!" influenced talk, and it was very interesting.

The type of Operation Database is focused on adding more information for the developers, rather than exposing more information to the operation team. What was especially interesting is that the amount of data being capture is very small. The standard log data (time, message, exception, etc) and a command log, which I think about as a message handling log. This log capture some statistics about messages / commands that the application has handled. Message size (in & out), time, processing time, etc.

From that, you can get pretty interesting data about your application (just showing avg. message processing time over a period of time is extremely valuable).

The nice part about this is that the entry cost is basically zero.

posted @ Tuesday, May 13, 2008 6:32 PM | Feedback (4)

Zero friction IoC: Auto registration is mandatory

This is the entire Binsor config file for a real application:

import Castle.MonoRail.Framework
import Castle.MonoRail.WindsorExtension
import Rhino.Commons.Facilities from Rhino.Commons.ActiveRecord

facility MonoRailFacility
facility RhinoTransactionFacility
facility ActiveRecordUnitOfWorkFacility:
	assembly = "HibernatingRhinos"

for type in AllTypesBased of IController("HibernatingRhinos"):
	component type.Name, type
	
for type in AllTypes("HibernatingRhinos").WhereNamespaceEq("HibernatingRhinos.Services"):
	component type.GetServiceInterface(), type

And I am pretty confident that I am not going to have to do much in the future with those.

And yes, you can do it with the fluent registration API as well.

posted @ Tuesday, May 13, 2008 4:08 PM | Feedback (6)

Binsor & Auto Registration - Making it even simpler

Here is the syntax that I am getting at...

for type in AllTypesBased of IView("Rhino.Commons.Test"):
	component type
	
for type in AllTypesWithAttribute of ControllerAttribute("Rhino.Commons.Test"):
	component type
	
for type in AllTypes("Rhino.Commons.Test") \
	.WhereNamespaceEq("Rhino.Commons.Test.Binsor"):
	component type

for type in AllTypes("Rhino.Commons.NHibernate") \
	.Where({ t as System.Type | t.Name.Contains("NHRepository") }):
	component "nh.repos", type.GetSeriveInterface(), type

And this seems to cover just about any scenario that I can think of. Combine that with Binsor's extend facility, and we are more or less done.

posted @ Tuesday, May 13, 2008 8:21 AM | Feedback (3)

Setting up MonoRail Windsor Integration with Binsor

I think I'll let it stand on its own:

facility Castle.MonoRail.WindsorExtension.MonoRailFacility 
for type in AllTypesBased of Controller("HibernatingRhinos"):
	component type.Name, type

Done.

I didn't believe it, to be fair.

posted @ Tuesday, May 13, 2008 7:17 AM | Feedback (2)

Saturday, May 10, 2008 #

Open seats for the DSL course

There are still open seats for the DSL course in Austin, next week (19th - 20th May).

You can register here: http://ayende.eventbrite.com/

posted @ Saturday, May 10, 2008 8:55 PM | Feedback (1)

WPF & Prism: Before & After

Glenn posted the before/after pictures of the Prism Reference Implementation application.

I was very impressed when I saw how they got from the traditional developer styled UI:

StockTrader_Before.png

To this UI:

StockTrader_After.png

Something else that Glenn said couldn't be stressed enough, this metamorphosis has happened in two weeks by a single developer.

I have worked on skinnable (win forms) applications, and that kind of thing just doesn't take just two man weeks, if it is possible at all.

Wow!

This make the WPF story much more compelling.

posted @ Saturday, May 10, 2008 3:27 PM | Feedback (3)

Talks Abstracts

This is an update of an old post of mine, listing all the talks that I am thinking of / had prepared. I am using this mostly as a way to centralize all my talks. Comments are welcome.

Level 200: Producing Production Quality Software

Working software is no longer the only thing that we need to produce. We need to create a software system that has a chance of surviving in the cruel world of production system, outside the clean room and sterile environment of development and QA. Understanding bottlenecks in the system, preventing cascading failures and recovery strategies have ceased being the problems of the very high end players. With the cost of system downtime being measures in $$$/second, this is an area we have to consider all the way.
In this talk we will cover how we can map common weaknesses in the system design, preemptively protect ourselves from them, and produce software systems that can withstand the real world hostile environment.

Level 200: Building Zero Friction Development Environment

Development is a fun process, most of the time. But in any development environment, we have areas where we have friction points that we have to deal with. If it is the pain of having to add a column to the database, or taking three weeks in order to deploy to production. It doesn't have to be like this. Being conscious of those friction points and actively eliminating them is a key to creating sustainable development environment and keeping our software design from being corrupt by "quick fixes" or hacks.

Level 4/300:Object Relational Mapping += 2: More then just data <-> object

Object relational mapping are becoming only more popular, as people developing complex systems find that they need more than the tabular model to work with in their applications. A sophisticated ORM can do a lot more than merely get the data out of the database in object form, it can be a valuable assest in simplifying development and making things possible. In this session, you will see how you can utilize an ORM in untraditional ways to get an additional, better, approach to solving complex issues.
Some of those ways include business rules, localization, state transitions, inversion of control, etc. All done via the ORM layer, and all can be used to drasticly simplify the complexity of the given scenarios.

Level 100: Using Active Record to write less code

What would you say if I told you that you can stop writing data access code in .Net? Aren't you tired of writing the same thing over and over again, opening connection, querying the database, figuring out what to return, getting back untype data that you need to start putting on the form? Do you really see some value in writing yet another UPDATE statement?
The Active Record framework allows you to fully utilize the power of the database, but without the back breaking work that it used to take. Active Record uses .Net objects to relieveyou from the repeating task of persistance. Those objects are schema aware and can persist and load themselves without you needing to write a single line of SQL. Building business application using Active Record is a pleasure, the database stuff just happens, and you are free to implement the business functionality.

Presentation for this can be found here: Using Castle Active Record

Level 200: Rapid (maintainable) web development with MonoRail

If you're a fan of Ruby on Rails and want to see similar capabilities in .NET, or you're an ASP.NET developer looking for an easier way to do things, MonoRail will be irresistible once you find out what it can do for you. Strong support for Ajax makes writing buzzward compliant web applications a breeze. Utilization of the Model-View-Controller architecture and convention over configuration makes web development with MonoRail a pleasure. Free yourself from page-life cycle issues and viewstate worries, start working with MonoRail, where the framework works for you.
This talk will introduce the general concepts of the framework, and how you can use them

Level 100: Interaction based testing With Rhino Mocks

Beyond the simplest scenarios, all objects had collaborators that they work with. This flies in the face of testing objects in isolation. This is the problem that mock objects were created to solve. In this talk you will learn what mock objects are, how to utilize them and best practices on when / how to utilize them. Rhino Mocks is a mock objects framework for .Net whose core goals are to let the developer rely on the compiler work well with refactoring tools.

Level 200: Inversion of Control and Dependency Injection: Breaking out from the dependecy hell

Responding to change is the holy grail of software development. Inversion of Control (IoC) and Dependency Injection (DI) are two related patterns that allows to make significant changes to an application without having to touch every part of the application. IoC and DI encourage breaking the application into discerete, highly cohesive parts, so a change, when it eventually comes, is very local. A nice benefit is that applications that uses IoC are also very testable applications.
This talk will introduce the concepts of IoC and how to use them in your application.

Presentation can be found here: Inversion Of Control and Dependency Injection

Level 4/300: Advance usages of Inversion of Control containers

You already understand the concepts of Inversion of Control and Dependency Injection, now is the time to see how far we can make the IoC container works for us. This talk will focus on using an IoC container in complex scenarios. We will talk about generic decorator chains and generic specialization, contextful containers and IoC DSLs. These powerful concepts can greatly enhance your ability to respond to change in your application.

Level 300: Writing Domain Specific Languages in Boo

Domain Specific Langauge is not just the DSL SDK from Microsoft. A DSL can make working with the domain much easier, since you are capable of leveraging the domain concepts directly. The other alternative to a DSL is an XML file, and we all know how well declarative model can work when you need imperative concepts, just consider NAnt for a minute and you will see the issue. Usually, writing a DSL in .Net would be a complex issue, requiring writing a parser, interpreter, etc. Boo already handles all of that, and its open architecture means that it is very easy to extend it to express the concepts of the domain. This talk will show you how to build DSLs in Boo and how to utilize this power in your applications.

Presentation can be found here: Writing Domain Specific Language in Boo


posted @ Saturday, May 10, 2008 3:09 PM | Feedback (0)

SvnBridge - Success

My personal success criteria for SvnBridge is when I no longer care what I am working against. And I think we got to that point. There are still issues, to be fair, but they won't hit you anywhere near mainline development.

I am listening to Rob Conery MVC Storefront 9 in which he makes the mistake of referring to CodePlex as Subversion. With that, I think that I can say, we are there.

posted @ Saturday, May 10, 2008 3:08 PM | Feedback (1)

Friday, May 09, 2008 #

Writing unreliable software

This had surprised me, to say the least.  I run into a bug during stress testing for SvnBridge, after a while, it would simply get stuck.

I am not the best in figuring out exactly what got an application stuck, but I generally manage to put some effort before I call the big guns. This time, I had managed to get a working theory, and prove that that the symptoms that I am seeing are consistent with my theory. I decided to dig into it a bit, and came up with interesting results.

The jury is still out with regards to whatever that was the real reason for the issue that SvnBridge has, but I went to some trouble to try research my theory. Along the way, I came up with some interesting conclusions. Just a reminder, I recently read Release It! and I consider this a very influential book. One of the things that kept coming up in the book how a chain reaction can take down an application. A single server stop responding, and the other just crumble. The examples that were brought up were of two kinds: a flawed implementation of a connection pool and a mismatch in the various capacities of the systems.

In SvBridge, there is a location where I am doing something similar to this code:

revision = webService.GetLatestVersion()

items = webService.GetItems(revision, downloadPath)
items.ForEach { item | item.BeginDownload(webService) }

comment = webService.GetLog(revision)items.WaitForAllToFinishDownloading()

SendToClient( Revision: revision, Comment: comment)

for item in items:
	item.WaitUntilLoaded()
	SendToClient( item.Data )

This isn't the exact code, but is is a good, and simple, representation of what is going on. This code can lead, under a set of special circumstances, to a hung server.

The important thing to understand here is that this code is using BeginDownload to perform an async invocation over HTTP. Another important data point is that we tend to hit the same physical server for a lot of our work.

Take a look at what is actually happening...

image

It is very important to observe that GetLog is actually never executed. The .NET framework is allowing (by default) only 2 connections to a server. GetLog will only be executed when there is a free slot, and the async requests are ahead of it in the queue. (Actually, I haven't verified the exact sequence in which this would be executed).

Until the async requests complete, we are stuck. This is important because it is not limited to the current request. This means that one request can block another, and since a single checkout request can cause a few thousands sub requests to the backend server, a single user can take down the system for a significant amount of time. (What I am actually seeing is a bit different, it looks like the async request never returns from the server under high load, but never mind that).

There are solutions for all of that, connection groups and overriding the number of allowed connections per server are just few of them.

I remember reading the Release It book and being thankful that so much of that seems to be focused on Java (thankfully, I have never had to write my own connection pool, which seems to be a pastime in the Java land).

What caused this post was actually my defensive approach failing with spectacular results. I started by defensively specifying Timeout on my requests. It would kill the request, but it would also keep the server alive. Unfortunately, Timeout has no affect on async requests. I found this very surprising, even though it is documented, I consider this a bug. Even worse, the actual example in BeginGetRequest is for fixing this issue, to support Timeout in async requests.

Leaving aside my own annoyance for hitting this tripwire, it brought to mind sharply that we should be very aware of the things that we do, and how they affect the longevity and scalability of our solutions. I spent most of the beginning of this week and the end of last week doing just that, throwing huge amounts of code and requests at SvnBridge. This was the most interesting problem that it had, and it was very interesting to see how we will solve that.

posted @ Friday, May 09, 2008 10:29 AM | Feedback (9)

Tuesday, May 06, 2008 #

Choose the appropriate medium

I recently started using Tweet, which also means that I mostly watch conversations go past. It has made me feel even more strongly about selecting the appropriate medium for discussion.

You cannot have a meaningful discussion in Twitter or IM, the conventions and limitations of the platform. Email is a better medium to expression complex concepts, but voice or video are far better methods of communication. Obviously, nothing can supercede the quality of discussion face to face, especially in open space format.

The other side of the coin is the cost of this interaction.

  • IM / Twitter have very little cost, and almost zero expectation. I can send you an "r u thr?" msg without being disturbed by that.
  • Doing the same in email, however, would be unacceptable, because I have different expectations from email.
  • Escalating to voice or video is far more costly. Now you have tied people in time, which tends to be very hard. I just had a discussion with someone that is 8 hours away from me. Finding the right time to schedule the conversation was... interesting.
  • Face to face ties use in both space and time
  • Etc...

So you have to choose the appropriate medium for the message you are trying to send.

Sending the message using inappropriate medium tends to cause difficulties.

posted @ Tuesday, May 06, 2008 12:43 PM | Feedback (11)

Pluggable Domain Model

Rhino Security is a good example of a pluggable domain model, in which we can plug some functionality into different and varied domains.

Here is an interesting demo to show how you can use it.

public static void DemoUsingCustomerCareModule<TCustomer>(string schema)
    where TCustomer : ICustomer, new()
{
    Configuration cfg = new Configuration()
        .Configure("hibernate.cfg.xml")
        .AddAssembly(typeof(Lead).Assembly)
        .AddAssembly(typeof(TCustomer).Assembly);

    cfg.MapManyToOne<ICustomer, TCustomer>();
    cfg.SetSchema(schema);

    new SchemaExport(cfg).Execute(true, true, false, true);

    ISessionFactory factory = cfg.BuildSessionFactory();

    object accountCustomerId;
    using (var session = factory.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        var customer = new TCustomer { Name = "ayende"};
        session.Save(customer);
        var customerCareService = new CustomerCareService(session);
        customerCareService.GenerateLeadFor(customer, "phone call");
        customerCareService.GenerateLeadFor(customer, "email ");
        tx.Commit();

        accountCustomerId = session.GetIdentifier(customer);
    }

    using (var session = factory.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        var customer = session.Get<TCustomer>(accountCustomerId);
        var customerCareService = new CustomerCareService(session);

        Console.WriteLine("Leads for: " + customer.Name);
        foreach (var lead in customerCareService.GetLeadsFor(customer))
        {
            Console.WriteLine("\t" + lead.Note);
        }

        tx.Commit();
    }
}

This can be used with:

DemoUsingCustomerCareModule<AccountingCustomer>("Accounting");

DemoUsingCustomerCareModule<HelpDeskCustomer>("HelpDesk");

posted @ Tuesday, May 06, 2008 11:48 AM | Feedback (3)

Course: Building Domain Specific Languages in Boo

You can register here for a two days course in building DSL with Boo.

It is going to take place two weeks from today, in Austin. (19 - 20 May)

I know that this is short notice, but it wasn't something that was planned well in advance. It came out of the ALT.Net conference.

Topics:

  • Creating Domain Specific Languages
  • The Boo Language
  • Flexible compiler and malleable language
  • Creating applications with embedded DSL
  • Management, tracing and debugging
  • Tooling support
  • Testing and maintainability concerns

There are ten seats open for that.

I hope we would have fun.

I would also like to thank Jeffrey Palermo and Headspring for hosting the course.

posted @ Tuesday, May 06, 2008 4:33 AM | Feedback (5)

Monday, May 05, 2008 #

How do you track that?

I have an interesting problem with SvnBridge.

After around 5000 full revision request (a set of requests that can occur), the application get hung making a web service call to TFS. This comes after making quite a few calls to TFS, and is generally fairly easily reproducible. The actual call being made is not an expensive one (nor is it the same call). TFS is responsive during that time, so it is not its fault.

It looks very much like I am hitting the 2 concurrent HTTP requests, except that all requests are serialized, and there is no multi threaded work involved.

I have been unable to reproduce this under a profiler or debugger...

Thoughts?

posted @ Monday, May 05, 2008 11:09 AM | Feedback (9)

Actively enforce your conventions

Glenn posted about a test I wrote PrismShouldNotReferenceUnity, in which I codified an assumption that the team made.

This is something that I try to do whenever I decide that I will have some sort of convention. If at all possible, I will try to make sure that the compiler will break if you don't follow the convention, but that is often just not possible, therefor, tests and active enforcement of the system convention fill that place.

Wait a minute, I haven't even defined what a convention is! By convention, I means things like:

  • All services should have a Dispose() method - add a Dispose abstract method to AbstractService
  • All messages should be serializable - create a test that scan the message assemblies and check for non serializable messages.
  • You may only call the database twice per web request - create an http module that will throw an exception if you call it more than that
  • A request should take less than 100 milliseconds - add an interceptor that would fail if this is not the case.
  • The interface assembly may not contain logic - add a test that would fail if it find a class with a method on the interface assembly.

All of those are ways to increase the feedback speed. This is especially true if there is some extra step that you need to perform, or you want to draw a line in the sand, from which you will not deviate.

Actively enforced conventions keep you honest.


posted @ Monday, May 05, 2008 10:51 AM | Feedback (11)

Sunday, May 04, 2008 #

Matching SVK revision numbers to the SVN revision number

I am using SVK as a way to learn Distributed SCM, and it has been fine so far, except for one big issue. I am trying to apply SVN patches against SVK repositories. Actually, I setup SVK so I work against a local mirror, so the workflow is exactly the same, but the revision numbers are different. Trying to apply a patch just doesn't work because of that.

You can go into the patch and edit the root revision, so it is not hard to make this work, except...

SVK doesn't give me any mapping between the local revision and the source revisions (maybe it does, and someone will tell me). This means that in order to find the right version I have to do some annoying digging.

Here is a small program to solve the problem. Usage: svkrev [repository path] [source revision to look for]

Example:

C:\Users\Administrator>svkrev //mirror/nhcontrib 195
exec: svk log --xml //mirror/nhcontrib
exec: svk propget svm:headrev //mirror/nhcontrib --revprop -r 10298
exec: svk propget svm:headrev //mirror/nhcontrib --revprop -r 10260
:=349
exec: svk propget svm:headrev //mirror/nhcontrib --revprop -r 10092
:=195
Found: 10092

By the way, can you find the huge perf boost here?

class Program
{
    static void Main(string[] args)
    {
        string path = args[0]; // "//mirror/nhcontrib"
        var revisionToSearchFor = int.Parse(args[1]); // 195
        string logAsXml = ExecuteProcessAndReturnOutput("log --xml "+ path);
        var doc = new XmlDocument();
        doc.LoadXml(logAsXml);
        var list = new ArrayList();
        foreach (var node in doc.SelectNodes("/log/logentry/@revision"))
        {
            list.Add(node);
        }
        for (int i = 0; i < list.Count;i++ )
        {
            XmlNode node = (XmlNode)list[i];
            int revision = int.Parse(node.Value);
            string output = ExecuteProcessAndReturnOutput("propget svm:headrev " + path + " --revprop -r " + revision);
            if (string.IsNullOrEmpty(output.Trim()) == false)
            {
                var currentRevision = int.Parse(output.Split(':')[1].Trim());
                Console.WriteLine(":=" + currentRevision);
                if (currentRevision == revisionToSearchFor)
                {
                    Console.WriteLine("Found: " + revision);
                    return;
                }
                i += (currentRevision - revisionToSearchFor -1);
            }
        }
    }

    public static string ExecuteProcessAndReturnOutput(string args)
    {
        var output = new StringBuilder();
        string err = null;
        var psi = new ProcessStartInfo
                      {
                          FileName = @"c:\Program Files\svk\svk.bat",
                          Arguments = args,
                          RedirectStandardOutput = true,
                          RedirectStandardError = true,
                          CreateNoWindow = true,
                          UseShellExecute = false
                      };
        Console.WriteLine("exec: svk "+ args);
        Process svk = Process.Start(psi);
        ThreadPool.QueueUserWorkItem(delegate
        {
            err = svk.StandardError.ReadToEnd();
        });
        ThreadPool.QueueUserWorkItem(delegate
        {
            string line;
            while ((line = svk.StandardOutput.ReadLine()) != null)
            {
                Debug.WriteLine(line);
                output.AppendLine(line);
            }
        });
        svk.WaitForExit();
        if (string.IsNullOrEmpty(err) == false)
            throw new InvalidOperationException(err);
        svk.WaitForExit();
        Thread.Sleep(500);
        return output.ToString();
    }
}

posted @ Sunday, May 04, 2008 8:21 PM | Feedback (0)

Rhino Mocks new feature: Persistent Mock Repository

Here is something that most people just don't think about, but it hits some people at the high end. Rhino Mocks is extremely efficient mocking framework, for just about any scenario. However, when it comes the time to mock large amount of interfaces (where large is in the hundreds to thousands), or if you have large interfaces (with dozens or hundreds of methods), it is costly to create the mocks. Rhino Mocks does some caching internally, but it is noticeable issue to some people.

We are talking about an extra second or two for even most pathological cases here, so in general it wouldn't warrant anything special from the library, except that there is a twist to that. Under the debugger, the amount of time spent actually generating the mock can go up by several orders of magnitudes. To add insult to injury, Visual Studio 2005 has several bugs in the debugger that will cause it to crush & burn under several such scenarios.

Karl Lew has been kind enough to not only provide a patch for Rhino Mocks to help this problem, but also document it.

Thanks, and sorry for taking so long to get to it.

posted @ Sunday, May 04, 2008 2:02 AM | Feedback (0)

Solving the impendence mismatch between Hierarchical Data and XML

I was very impressed when I saw how Subversion handles the complexity of having data of hierarchical nature that needs to be serialized to XML. Check this out.

<?xml version="1.0" encoding="utf-8"?>
<S:editor-report xmlns:S="svn:">
  <S:target-revision rev="11"/>
  <S:open-root rev="-1"/>
  <S:open-directory name="tags" rev="-1"/>
  <S:add-directory name="tags/asd"/>
  <S:close-directory/>
  <S:close-directory/>
  <S:close-directory/>
</S:editor-report>

And here is another one:

<?xml version="1.0" encoding="utf-8"?>
<S:editor-report xmlns:S="svn:">
  <S:target-revision rev="15"/>
  <S:open-root rev="-1"/>
  <S:open-directory name="trunk" rev="-1"/>
  <S:open-file name="trunk/a.txt" rev="-1"/>
  <S:apply-textdelta checksum="eabc96676e7defda414a1eed33bdfb09">
    U1ZOAAAQEwETk2FzZDENCjINCjMNCjQNCjUNCjY=
  </S:apply-textdelta>
  <S:close-file checksum="c6301e5dad1330a7b9bd5491702c801b"/>
  <S:close-directory/>
  <S:close-directory/>
</S:editor-report>

I was, as they say incredibly happy with this Work Time Fun.

posted @ Sunday, May 04, 2008 1:38 AM | Feedback (1)

Saturday, May 03, 2008 #

Go with High End Solutions

About a year and a half ago, I start an exciting new project (there is a demo of the actual project here). The actual application is fairly complex, and has some it gave me the chance to explore some very interesting ideas. Rhino Security is a generalization of the security scheme used in this project, and it is pretty much the driving force for Rhino Igloo. But that is not what I want to talk about.

What I do want to talk about is the infrastructure that we used for the project. We used IoC, OR/M, AoP, MVC and many other buzz worthy TLD. It was the first time that I had the chance in implementing real high end complexity reduction techniques. I left the team 10 months ago. In the meantime, the application was under active development, got a few new team members and had two major releases.

I am really proud of that system.

A few weeks ago I got a phone call from the current team lead, asking me about the windsor.boo file that is sitting there. The last time anyone touched it was shortly after I left, after which, it just... existed. I had the chance to do a code review on the new stuff that the team developed, about three months ago. I couldn't find any real difference between the code develop before and after I left.

Anyway, I had to spend 15 minutes on the phone, explaining the process that was going on there. Before I left (and during the time I was the team lead), I made sure that I passed on all the knowledge that I had about the system, the design decisions and the overall picture. However, there was a period of nearly three months in which I forgot that we even had this infrastructure, because we hadn't have to deal with it for that time period. After I left...

  • 9 months.
  • 2 major releases.
  • Zero issues with the infrastructure.

I asked the team lead what she thinks about that. Since it is her project now, and if she thinks that it was the right decision to make. She love the infrastructure, and wouldn't hear about using a lower end solution. Most of what we did was actually going over the file and explaining historical decisions, for that matter.

As an additional data point, I was able to look at a piece of code I have last seen over a year ago and figure out not only what it does, but the how and why of it with no ramp up time.

I consider this a success.

posted @ Saturday, May 03, 2008 2:00 PM | Feedback (7)

How to test this?

I have an interesting problem. I am currently working on adding sync support to SvnBridge. This mostly involves tracking down what SVN does and duplicating it myself. There isn't that many new code (I had to add a class and a method). I have nothing that I can _unit_ test. Oh, I could probably craft something, but I am reluctant to mock the world when what I want is to test the actual integration between the SVN client and the TFS server.

The problem is that I have no real way of testing this. Why do I mean by that? syncing is an operation that works over the entire repository, from start to finish. I can't perform an integration test, because that would take too long in most scenarios. I would need to setup a whole new TFS server per test. Not really a good solution, no matter how you turn the dice. And the other problem is that I need a rich set of situations to actually test this.

Right now I am driving that by going against the production CodePlex servers, and trying to see if I can get all the information from there. This is incredibly valuable, because it gives me access to a lot of source control practices that are out there, and expose a lot of false assumptions. But I can't write tests for those, because they are too big a scenario.

I can probably set an integration test that would execute against the production servers (they are publicly exposed, after all, so no issue there), but I am... less than thrill about having a single test that can potentially run for hours or days.

Right now I think that I am leaning toward partial fake of both the client and the server. That is, create a set of input values which, while not being the real world values, would still test that logic.

Considering that the actual interaction we are talking about take place over many requests, this is tricky, but possible.

Suggestions?

posted @ Saturday, May 03, 2008 3:45 AM | Feedback (4)

Friday, May 02, 2008 #

Avoid retrofitting unit tests

I was asked this a few days ago, should we spend time creating proper unit tests for our code?

The team in question already have a set of integration tests, but very few tests that qualify as unit tests.

My response was rambling and long, but it can be put down to the following statement: TDD is a design technique, not a testing technique. TDD, and especially test first, have the nice side affect of leaving tests as part of the process, which can be incredibly helpful when you are working with the code. But retrofitting tests? That tends to be a waste of time.

Writing a unit test before touching the code is absolutely the way to go, but going and adding unit tests, as a task of its own? I don't see the value in that.

If you have integration tests there, that tends to be good enough, and you will write unit tests when you change the code, so eventually you'll have enough unit tests ( eventually you will have enough unit tests on the hot spots ).

posted @ Friday, May 02, 2008 1:21 PM | Feedback (15)