in

 

Chad Myers' Blog

Department of Problem Prevention

March 2008 - Posts

  • Pablo's Topic of the Month - April: Advanced Language Patterns for Visual Basic

    Pablo's Topic of the Month - April: Advanced Language Patterns for Visual Basic

    Following on the coat tails of last months' Pablo's Topic of the Month - March: Leveraging XML for Computationally Expensive Operations, I'd like to announce the new PTOM for April:  Advanced Language patterns (for Visual Basic).

    We (the Los Techies crew) have been discussing this month's topic vigorously for the past few weeks and we're really excited to announce the highlights of this month's topic.  Over the next few days and weeks we'll be doing an article storm (the new 'weblog' people call it 'blogging', but that will probably never catch on) on the subject of advanced language patterns in Visual Basic.  With the recent SP6 release of Visual Basic last summer (1998), several new and exciting features have opened up new possibilities with this exciting, robust (and enterprise Hah! Take that C++ know nothings!) language.  We plan on going pretty deep on these subjects, so please stay tuned and we welcome all your feedback and comments (except those from the the C++ guys -- don't bother, they'll be summarily deleted without ceremony because we don't need to free pointers. SNAP!).

    If you haven't already, please consider subscribing to the Los Techies Main Feed so that you can see the various post from the other Los Techies bloggers.

    The main feed is here:  http://feeds.feedburner.com/lostechies

    Advanced Topics for Visual Basic

    With the advent of much-anticipated Visual Basic 6, we now have the possibility to explore the benefits of having a fully object-oriented language with modern language concepts such as 'memory handling', 'reference counting', 'string concatenation', and the (magical and wonderful, in my opinion) advanced IDispatch support.

    The topics we plan on covering are quite broad and include:

    Advanced Error Handling

    Visual Basic 6 will be known for a long time for how it eliminated bugs due to it's comprehensive and first-rate structured error handling system. We'll do a series of posts on advanced features such as:

    • On Error Resume Next
    • On Error Goto (label)
    • On Error GoTo 0

    Advanced Web Page Creation with WebClasses

    Much to our chagrin, it appears the Web is here to stay. Fortunately, Microsoft has provided for us a world-class, top-notch Enterprise web framework that is sure to be the mainstay of web programming for the forseeable future known as 'Visual Basic WebClasses'.  Just imagine the sheer, raw power of in-memory, in-process VB6 code executing directly in IIS' main process space. This will definitely put to shame anyone who thought parsing script text files in ASP will ever catch on. Sorry folks, WebClasses is the future.

    • Stateful web patterns using VB6 Web Classes
    • Making the web seem more like regular Windows Programs
    • Avoid writing any HTML and use advanced, high-performance VB6 string concatenation techniques

    Advanced Object Oriented Programming

    For many years we have been chided and derrided by the C++ slogs about how VB isn't 'really OO' whatever that means. Well, with the advent of VB6, we finally have a FULL OO implementation including Interfaces. Take THAT C++. Don't bring no weak pure virtual abstract class action here! This is hard core enterprise programming now!

    • Defining and using interfaces with VB6

    That concludes the list of OO features in VB6.

    Strongly Typed Data: Variant V_TYPEs

    When dealing with more crude systems which do not have VB's wonderfully adaptive and presumptive typing system (*cough* C++), it may be necessary to actually have to pigeon-hole your data into 'strong types' by understanding how VB stores data in memory  and how it's marshaled to systems outside the glorious VBRuntime.  I know, I know, I thought we were past this too, but apparently some people find it necessary to still run code outside of a managed runtime environment like VB6 and so we must come down to their level.  Posts about this subject include:

    • Defining variant subtype with V_TYPE
    • Rewriting C++ code in VB6 without having to understand pointers (not sure if we'll get this one done by the end of April, that remains to be seen)

    Ergonomic Considerations for new Drag and Drop Coding Features

    VB6 has brought us even more opportunities to drag and drop our way to success without having to write much any code! But with great power also comes great responsibility. Many VB6 users have reported 'mouse hand', so it's important to remember that, despite our ridiculous RAD productivity, we still are mere mortals and our hands CAN break. So take care, the VB6 programmer hands are a temple, after all.

    • Using other fingers for the Left Mouse Button: The Double Deuce
    • Using your left hand for dragging
    • Add-ons with Mouse Gesturing capabilities

    Many different OPTIONs

    VB6 is frighteningly customizable. In fact, one of the customizations is to control how the interpreter compiler treats your finely crafted code. We'll explore these various options and how they can be helpful or harmful

    • OPTION BASE
    • OPTION EXPLICIT
    • OPTION STRICT (warning: Avoid this one like the plague, it restricts your creative expression entirely!)
    • OPTION COMPARE (Love this feature, totally change the way the entire app works with one keystroke, totally awesome!)
    Posted Mar 31 2008, 09:04 PM by chadmyers with 9 comment(s)
    Filed under: ,
  • Time-to-Login-Screen, and the absolute basic requirements for good software

    I went and hung out with some fellow developers on Saturday (at the Grist Mill in New Braunfels, TX, incidentally -- wonderful place!) and we talked about all sorts of things on the way down, there, and back up.  One of the things that particularly stuck in my mind was a discussion how there are still software development teams out there not using things like source control or automated builds.  Everything is by hand and magic.

    It occurred to me that this is simply unacceptable and embarrassing. I don't believe this is an elitist statement.  We can debate the finer points of how to do unit testing with TDD or TAD, etc we can debate about what BDD is and whether and to which degree it's useful, etc. But, and I hope this isn't just my thought, there are certain things which are simply not debatable. If you're not doing these things on any sufficiently large project (although I could make an argument that these things are important enough for any project delivered to a customer and/or used in a production manner).

    I'll get to what these things I consider as 'absolutes' in a minute, but before I do that, I want to explain why these things might be important -- to define the values I think are important so that you can get a feel for where I'm coming from.

    Horrors of an Unrepeatable Setup

    How many times have you walked into a software shop and picked up the maintenance on a project and it was in total tatters.  It didn't compile, you weren't even sure you had the latest source code --in fact, no one was sure, you seemed to be missing all sorts of 3rd party components and their obnoxiously-installed licenses, etc, etc.  To boot, your manager says that you need to fix a particular bug and get the fix in for testing by the end of the week.  It's Monday, you 'fix' the 'bug' in a matter of minutes, but now you about 4 days to figure out how to create a build and put it into the test environment.  Fast forward 6 days later after you manage to get enough clues from co-workers and phone calls to ex-employees, you find out that the build you deployed is an OLD build that contains several already-fixed-and-deployed bugs.  Now you begin the fun of trying to track down where the latest source code is so you can start everything over.

    I'm willing to bet that almost every single one of you has had an experience like this.  If not, then you are VERY lucky and I hate to be the one to tell you that, odds are, your time is coming.  For me, this scenario has happened to me on almost every project.

    The Virtues of Repeatability

    No Magic

    What this all points to is a failure of the dev team to create 'repeatability' in their software infrastructure. While it's unlikely, it's possible that the code itself could be the best, most well-written code ever known to man. But since no one knows how to build, test, or deploy it, it might as well be crayon drawings from my 2 year old.  One of the most important attributes of 'good software' should be 'repeatability' of its build, test, and deployment.  So I'm gonna go out on a limb here and suggest that these things should be a must for any software project that is intended to be used in a production manner. If you don't have these things, you have created a situation where serious and potentially irreconcilable problems will surface in the future.  If you don't have these things, you should be working soon on achieving these.  One last thing: I don't want to hear any argument about how long it will take to get this going. I can say without any hesitation that you've already spent or will be spending many orders of magnitude more time dealing with the problems that arise from NOT having repeatability, than you would setting your project up for repeatability.

    Without more adieu, the key points of achieving Repeatability (with more specifics on each will follow) are:

    • Source Control
    • Automated Build
    • Automated Testing
    • Continuous Integration (for multi-dev teams)
    • Automated Deployment

    Source Control

    The code should have it's primary home in a central, well-known location

    This one should hopefully be pretty simple: The code should have it's primary home in a central location and absolutely not in the 'Visual Studio Projects' folder in someone's My Documents folder on their desktop.  Of course, if you don't have an actual server box, the source control repository can be on someone's desktop, as long as that's the CENTRAL location for it and as long as it's getting backed up somewhere.

    In addition to simply having a central go-to location for the source code, I highly recommend you get a source control management software that tracks versions and enables you to do differences, branching, merging, reports, etc. I personally recommend 'Subversion' for this task, but others have found software such as git, SourceGear Vault, Microsoft Visual SourceSafe, StarTeam, and many others useful.  But whatever you do, for the love of software, PLEASE use SOMETHING.

    Automated Build

    The code should have an associated automated build script that requires no magic by the person running the script

    Once a would-be software maintenance programmer (possibly yourself) pulls down the latest code from the source control repository, building the code into a runnable unit should be automated to the maximum extent possible (preferably 100%).  Now, that doesn't mean you have to install IIS/Apache on the box, it's OK to have certain prerequisites (i.e. 'Windows' or 'Linux', 'IIS' or 'Apache', etc).  If there are specific environmental requirements like this though, you should have the build script at least check for them and alert the human there's something missing.

    A helpful side-effect of automating your build process is that you see what a pain it is to configure your app (and they're all a pain) and it might motivate you to make configuration easier.

    Automated Testing

    The code should have at least a basic success indication check test and it should be able to run in an automated fashion

    There should be some way to prove that a build was 'successful' beyond the build script telling us so. We need real proof/verification.  If you're not doing unit tests, TDD, TAD, POUT, whatever, at least have ONE test that does SOMETHING with the app to verify that it doesn't just crash on startup.

    A maintenance programmer who's new on the project should have some indication as to whether the code he just built actually resembles a working product or not.  The more tests you have, the more confident he/she's going to be. Please give the gift of confidence to your successor.

    Continuous Integration

    When a developer commits code to the source control repository, a separate, objective, and impartial system should report the success and health of the build.

    I was strongly tempted to say that this should be a requirement all the time -- even for a solo 'team'. CI certainly helps even for a solo developer, but I wouldn't consider it an absolute must.  Having said that, if you have more than one developer committing code to the same project in the same source control repository, I will say that CI is an absolute must.

    It is important that the CI run on a non-developer machine install.  It is, however, perfectly acceptable to run it in a virtual machine running on a developer's desktop computer though if the budget is tight.  The intention here is that you have a somewhat pristine OS install that isn't polluted with SDK's, developer tools, environment settings, etc. You want to try to create a production-like environment to the maximum extent possible. This doesn't mean you need to replicate your production hardware requirements, but you should try to use a similar OS setup to what the production servers (or customer computers) are running. If the production server is running Windows 2003 Server, see if you can get your CI OS install to be Windows 2003 Server. If the budget is tight, you might consider using a 180-day trial version of Windows 2003 Server until you're sure how you like your CI setup. Hopefully by then you'll be making some money and you can afford a copy of Server or an MSDN Subscription or something.  I'm not sure about the legality of re-building your CI virtual machine with 180-day trial versions of Win2K3 server over and over again. If it's legal and doesn't violate the EULA, that may be an option too. Please check with your corporate attorney, priest, or local fortune teller for clarification (since no one can really truly understand Microsoft EULA's and divination is just as an acceptable means as legal review for getting it right).

    Automated Deployment

    The build product should be deployable to another environment with minimal human interaction

    Having said that, it's not usually possible (or feasible) to have one-button automatic deployment to any specified environment. In my experience, there's always something required by a SysAdmin, a DBA, etc.  If not perfection, you should at least strive to deliver a ZIP file or MSI to your SysAdmin and a well formatted change script to your DBA.  Strive to make deployment convenient, coherent, and above all consistent.

    A Metric of Success: Time-to-Login-Screen

    Now that we've established the virtues of repeatability -- and I hope you are intent on practicing them -- how do we measure our success in this effort?  I propose a metric: "Time-to-Login-Screen" (TTLS).  This of course assumes your app has a 'login screen', but it applies to any app. Perhaps it should say 'Time-to-First-Screen-With-Functionality' but 'TTFSWF' isn't as sexy as 'TTLS'.

    The general concept here is how long can I take a new-hire developer and get him/her coding/building/deploying (i.e. a productive member of the team).  This metric can expand further to include how long HR takes, how long IT takes to set up the desktop, how long it takes to install any 3rd party components, etc.   But for our purposes, let's start with using TTLS to represent the time it takes from when the IT department hands your dev a desktop to the time when they're committing code into your source control repository. Let's exclude commonalities like Visual Studio (or your IDE of choice) and other basic tools.  We should, however, include things like 3rd party components (Infragistics, Telerik, etc) and their licenses and such.

    If your TTLS is over a day, that's a problem. If it's over a few days, the situation is highly dysfunctional. If it's approaching a month (yes, I worked at a shop where it was a little over a MONTH before I was committing code into source control), then you should re-think how you do software entirely. Something is seriously wrong.

     

  • PTOM: The Liskov Substitution Principle

    The Liskov Substitution Principle

    In my first (of hopefully more than one) post for The Los Techies Pablo's Topic of the Month - March: SOLID Principles effort, I'm going to talk about The Liskov Substitution Principle, as made popular by Robert 'Uncle Bob' Martin in The C++ Report.

    I'm going to try as much as possible not to repeat everything that Uncle Bob said in the afore-linked PDF as you can go read the important stuff there. I'm going to try to give some real examples and relate this to the .NET world.

    In case you're too lazy to read the link, let me start off with a quick summary of what LSP is: If you have a base class BASE and subclasses SUB1 and SUB2, the rest of your code should always refer to BASE and NOT SUB1 and SUB2.

    A case study in LSP ignorance

    The problems that LSP solves are almost always easily avoidable. There are some usual tell-tale signs that an LSP-violation is coming up in your code. Here's a scenario that walks through how an LSP-violation might occur. I'm sure we've all run into situations like this. Hopefully by walking through this, you can start getting used to spotting this trend up front and cutting it off before you paint yourself into a corner.

    Let's say that somewhere in your data access code you had a nifty method through which all your DAO's/Entities passed and it did common things like setting the CreatedDate/UpdatedDate, etc.

    public void SaveEntity(IEntity entity)
    {
    DateTime saveDate = DateTime.Now;

    if( entity.IsNew )
    entity.CreatedDate = saveDate;

    entity.UpdatedDate = saveDate;

    DBConnection.Save(entity);
    }

    Clever. Works like a champ.  Many of you will hopefully have cringed at this code. I had a hard time writing it, but it's for illustration. There's a lot of code out there being written like this.  If you didn't cringe and you don't see what's wrong with that code, please continue reading. Now, the stakeholders come to you with a feature request:

    Whenever a user saves a Widget, we need to generate a Widget Audit record in the database for tracking later.

    You might be tempted to add it to your handy-dandy SaveEntity routine through which all entities pass:

    public void SaveEntity(IEntity entity)
    {
    WidgetEntity widget = entity as WidgetEntity;
    if( widget != null )
    {
    GenerateWidgetAuditEntry(widget);
    }

    // ...

     

    Great! Also works like a champ. But a few weeks later, they come to you with a list of 6 other entities that need similar auditing features.  So you plug in those 6 entities. A few weeks later, the come to you and ask you something like this:

    When an Approval record is saved, we need to verify that the Approval is of the correct level. If it's not the correct level, we need to prompt the user for an excuse, otherwise they can't continue saving.

    Oh boy, that's tricky. Well, now our SaveEntity looks something like this:

    public void SaveEntity(IEntity entity)
    {
    if( (entity as WidgetEntity) != null ){
    GenerateWidgetAuditEntry((WidgetEntity) entity);
    }

    if ((entity as ChocolateEntity) != null){
    GenerateChocolateAuditEntry((ChocolateEntity)entity);
    }

    // ...

    ApprovalEntity approval = entity as ApprovalEntity;
    if( approval != null && approval.Level < 2 ){
    throw new RequiresApprovalException(approval);
    }

    // ...

     

    Pretty soon your small, clever SaveEntity method is 1,500 lines long and knows everything about every entity in the entire system.

    Where'd we go wrong?

    Well, there's several places to start here. Centralizing the saving of entities isn't the greatest idea.  Putting the logic for whether audit entries need created or not into the SaveEntity method was definitely the wrong thing to do.  And, finally, due to the complexities of handling wildly differing business logic for different entities, you have a control flow problem with the approval level that requires the use of an thrown exception to break out of the flow (which is akin to a 'goto' statement in days of yore).

    The concerns of auditing, setting created/updated dates, and approval levels are separate and orthogonal from each other and shouldn't be seen together, hanging around in the same method, generally making a mess of things. 

    But, more to the point of this blog post: SaveEntity violates the Liskov Substitution Principle.  That is to say, SaveEntity takes an IEntity interface/base class but deals with specific sub-classes and implementations of IEntity. This violates a fundamental rule of object-oriented design (polymorphism) since SaveEntity pretends to work with any particular IEntity implementation when, in fact, it doesn't. More precisely, it doesn't treat all IEntity's exactly the same. Some get more attention than others.

    Why is this a problem? What if you were reusing your terribly clever SaveEntity method on another project and have dozens of IEntity implementations over there and the stakeholders for that project also wanted the auditing feature. Now you've got a problem.

    Solutions

    One fine approach to this problem of having to do things a-the-moment-of-saving would be to use the Visitor Pattern as described by Matthew Cory in this post.  Though, I would say in this particular example, there is a much more deep-rooted and systemic design problem which revolves around the centralization of data access.

    Another, in our case more preferable, way to go might be to use the repository pattern for managing data access.  Rather than having "One Method to Rule them All", you could have your repositories worry about the Created/Updated date time and devise a system whereby all the repository implementations share some of the Created/Updated date entity save/setup logic.

    As specific one-off problems arise (such as auditing, extra approval/verification,etc) they can be handled in a similarly one-off manner by the individual entity's related repository (who knows all about that one type of entity and that's it).  If you notice that several entities are doing the same sort of thing (i.e. auditing) you can, again, create a class and method some where for handling auditing in a common manner and providing the various repositories who need auditing with that functionality.  Resist, if at all possible, the urge to create an 'AuditingRepositoryBase' class that provides the auditing functionality. Inevitably, one of those audit-requiring entities will have another, orthogonal concern for which you will have another *Base class and, since you can't do multiple inheritance in .NET, you are now stuck.  Prefer composition of functionality over inheritance of functionality whenever possible.

    If you have a rich domain model, perhaps the most elegant approach of all would be to make things like auditing a first-class feature of the domain model whereas every Widget always has at least one WidgetAuditEntry associated with it and this association is managed through the domain logic itself.  Likewise, the approval level would be best handled higher up in the logic chain to prevent last minute "gotchas" in the lifecycle that would require something less than elegant like an exception as a thinly veiled 'goto' bailout.

  • Interesting (good) Behavior of Closures

    For some reason, I didn't think this would work, but it does:

                Customer c = null;
                Func<string> func = ()=>c.LookupName;
                c = new Customer {LookupName = "First"};
                System.Diagnostics.Debug.WriteLine(func());

    I would've expected an NRE in the WriteLine because the 'c' reference (null) would've been packaged up by the closure.  But apparently it packages up the reference to the reference also so that if the 'c' variable value changes, so does the closure's reference to it. 

    The output is not an NRE, but rather "First".

    I was curious what it would do with valid references. Consider the following example:

                Customer c = new Customer{LookupName="First"};
                Func<string> func = ()=>c.LookupName;
                c = new Customer {LookupName = "Second"};
                System.Diagnostics.Debug.WriteLine(func());

    The output is, as you would expect, "Second".

    One last stretch here, what about stack-based value types:

                int i = -1;
                Func<int> func = ()=>i;
                i = 99;
                System.Diagnostics.Debug.WriteLine(func());

    The output is 99.

    The way it works under the hood is that the compiler doesn't actually create a new stack variable called 'i' (like it would normally), it creates a new class called <>c__DisplayClass2d in my case. Well, that's hard to type, so let's just call it FancyClass:

                public class FancyClass{
                    public int i;
    
                    public int GetI(){
                        return i;
                    }
                }

    Then, it re-writes -- rather it compiles slightly different IL -- the code above (the int i = -1 example) like this:

                FancyClass c = new FancyClass();
                c.i = -1;
                Func<int> func = ()=>c.i;
                c.i = 99;
                System.Diagnostics.Debug.WriteLine(func());

    Seen like this, it seems a bit more obvious.

    Many of you are probably saying "Duh!", but this went against my understanding of how closures package up their context. 

    Posted Mar 08 2008, 10:42 PM by chadmyers with 3 comment(s)
    Filed under: ,
  • Pablo's Topic of the Month - March: SOLID Principles

    Pablo's Topic of the Month - March: SOLID Principlespablos_topic

    Over the next few days and weeks, the Los Techies crew will be writing a number of blog posts focused a particular subject in addition to their regular blogging.  Pablo's Topic of the Month for the month of March is Bob Martin's S.O.L.I.D. design principles. We'll try to cover all of them by the end of the month or we might focus in on a few of them and go really deep.  Please stay tuned and please give us some feedback of how you like this format because we're considering doing it in upcoming months.

    If you haven't already, please consider subscribing to the Los Techies Main Feed so that you can see the various post from the other Los Techies bloggers.

    The main feed is here:  http://feeds.feedburner.com/lostechies 

    What is S.O.L.I.D.?

    S.O.L.I.D. is a collection of best-practice object-oriented design principles that you can apply to your design to accomplish various desirable goals like loose-coupling, higher maintainability, intuitive location of interesting code, etc.  S.O.L.I.D. is an acronym for the following principles (which are, themselves acronyms -- confused yet?).

    These principles were pioneered and first collected into a written work by Robert 'Uncle Bob' Martin. You can find more details here: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

    I'm going to give you a teaser here of each one, but I won't go into much detail here as that's what the other Los Techies articles are going to be about.  Please check back to this post as I'll be updating it with links to other posts as they appear so you can keep tabs on what's going on. 

    The attribution of the following snippets goes to Robert Martin from various publications.

    SRP: Single Responsibility Principle

    THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE.

    OCP: Open Closed Principle

    SOFTWARE ENTITIES (CLASSES, MODULES, FUNCTIONS, ETC.) SHOULD BE OPEN FOR EXTENSION BUT CLOSED FOR MODIFICATION.

    LSP: Liskov Substitution Principle

    FUNCTIONS THAT USE ... REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES  WITHOUT KNOWING IT.

    ISP: Interface Segregation Principle

    CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE

    DIP: Dependency Inversion Principle

    A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS

    B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS

  • Ping-pong Pairing, it's not just for breakfast anymore

    Yesterday was my first day working at my new employer, Bayern Software, with (name drop alert!) Jeremy Miller.  We started right off with an easy story doing ping-pong pairing. I really dig this method of doing development (not to mention pairing, not to mention TDD).

    We started off with a quick whiteboard discussion. Fortunately this story was so easy, there wasn't much room for debate or design decisions, so we went right to the keyboard.  We were going to implement the Command Executor Pattern and start off with a synchronous implementation to make testing easier (and worry about the asynchronous stuff later).

    Our setup this:  Jeremy at his laptop with a keyboard and mouse  and me at a monitor connected to his laptop with my own keyboard and mouse (also connected to the laptop).

    How it worked

    Jeremy started off and added an interface (ICommandExecutor) with some methods we know we'd need.  At this point I noticed he was using r-click, add new item in Visual Studio to add the new interface and I was able to suggest a ReSharper shortcut (hit ALT+INS and choose 'Interface') to have it generate a new interface file for you using a more refined template than the default one built into VS.  Score one for Pair Programming!  Sidebar for those playing the home game: that would be Chad 1, Jeremy 0 -- the game would eventually end with Chad 1, Jeremy 5,984. He then added a new class called SynchronousCommandExecutor, had it implement ICommandExecutor, use R# to stub out all the methods (each throwing a 'NotImplementedException') to satisfy the ICommandExecutor requirements, and then he added it to the project's StructureMap configuration code.

    He then wrote a test for the first method we added to the ICommandExecutor interface.  It was very simple and straight to the point. He ran the test, which failed of course because all the method does now is throw a NotImplementedException. He then turned the control over to me and I started banging on the keyboard.  I went to the SynchronousCommandExecutor class and removed the 'throw new NotImplementedException', and then implemented the code of the method properly.  I then re-ran the test (using the Re-Run Last Test keyboard shortcut for R# which he happened to have bound to CTRL+3) and it passed!  Throughout this process, Jeremy kept correcting all my inefficiencies by showing me how R# can do that, or do this with a simple keyboard shortcut.  I thought I knew R# pretty well, but I didn't. Shame! 

    Note to self: We (.NET community in general) need more R# screencasts that show R#-fu in action.

    Next, I wrote the test for the next method in SynchronousCommandExecutor.  I ran it, and it failed.  I then turned control of the system over to Jeremy who then implemented the method using a flurry of R# shortcuts.  He then ran the test again and now it passed.  

    We proceeded in this manner until we had tested the SynchronousCommandExecutor thoroughly and were both satisfied that it was well tested. We then moved onto a different story that was more complicated and involved touching more parts of the system.

    Why I like it

    First, I got to tour the system in a non-threatening way.  I was able to discover the parts of the system I needed and work out from there. It wasn't a heavy top-down or bottom-up scan through the code, it was focused on where I was concerned and branched out from there.  Second, Jeremy and I were able to swap (well, Jeremy was for the most part) tips and tricks to speed each other up.  There was a bit of friendly competition of sorts to see who could be most efficient.  It was mildly intense in a good way and broke the ice with a new codebase.  Third, perhaps most importantly, with in the first few hours on my first day, we had already completed a few stories. I learned more in those 2-3 hours than I would've learned after weeks of studying the code from the outside or pouring over reams of already-outdated "Systems Documentation" or specifications or requirements that are common at most non-Agile shops.  Fourth, by keeping each other honest and focused, we were less inclined to be distracted with other things or be tempted to floor the gas pedal and stream out a bunch of code that wasn't tested properly.

    Try it!

    In most shops I've worked in (which were almost all non-Agile shops), pair programming happened. It may not have been institutionalized, but it was not uncommon to see two developers at one workstation hammering out an issue. It's just natural, it works, so people instinctively gravitate toward that style of cooperation. Sure, you don't do it every hour of the day (nor should you!). There is a time and place for pair programming.  I suggest that WHEN you find yourself in a pair situation, make the most of it and become super-productive by using the ping-pong technique (even if you're not doing TDD, and you're just banging out code you can still benefit from this!).

Copyright Los Techies 2007. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems